package org.wikibrain.core.dao.sql; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.typesafe.config.Config; import gnu.trove.set.TIntSet; import gnu.trove.set.hash.TIntHashSet; import org.jooq.*; import org.wikibrain.conf.*; import org.wikibrain.core.dao.DaoException; import org.wikibrain.core.dao.DaoFilter; import org.wikibrain.core.dao.LocalLinkDao; import org.wikibrain.core.dao.UniversalLinkDao; import org.wikibrain.core.jooq.Tables; import org.wikibrain.core.lang.Language; import org.wikibrain.core.lang.LanguageSet; import org.wikibrain.core.model.LocalLink; import org.wikibrain.core.model.UniversalLink; import org.wikibrain.core.model.UniversalLinkGroup; import java.util.*; /** * * A SQL database implementation of the UniversalLinkDao. * * @author Ari Weiland * */ public class UniversalLinkSqlDao extends AbstractSqlDao<UniversalLink> implements UniversalLinkDao { private final LocalLinkDao localLinkDao; private final int algorithmId; public UniversalLinkSqlDao(WpDataSource dataSource, LocalLinkDao localLinkDao, int algorithmId) throws DaoException { super(dataSource, INSERT_FIELDS, "/db/universal-link"); this.localLinkDao = localLinkDao; this.algorithmId = algorithmId; } private static final TableField [] INSERT_FIELDS = new TableField[] { Tables.UNIVERSAL_LINK.LANG_ID, Tables.UNIVERSAL_LINK.LOCAL_SOURCE_ID, Tables.UNIVERSAL_LINK.LOCAL_DEST_ID, Tables.UNIVERSAL_LINK.UNIV_SOURCE_ID, Tables.UNIVERSAL_LINK.UNIV_DEST_ID, Tables.UNIVERSAL_LINK.ALGORITHM_ID, }; @Override public void save(UniversalLink link) throws DaoException { for (Language language : link.getLanguageSet()) { for (LocalLink localLink : link.getLocalLinks(language)) { save( localLink, link.getSourceId(), link.getDestId(), link.getAlgorithmId() ); } } } public void save(LocalLink localLink, int sourceUnivId, int destUnivId, int algorithmId) throws DaoException { insert( localLink.getLanguage().getId(), localLink.getSourceId(), localLink.getDestId(), sourceUnivId, destUnivId, algorithmId ); } @Override public Iterable<UniversalLink> get(DaoFilter daoFilter) throws DaoException { DSLContext context = getJooq(); try { Collection<Condition> conditions = new ArrayList<Condition>(); if (daoFilter.getSourceIds() != null) { conditions.add(Tables.UNIVERSAL_LINK.UNIV_SOURCE_ID.in(daoFilter.getSourceIds())); } if (daoFilter.getDestIds() != null) { conditions.add(Tables.UNIVERSAL_LINK.UNIV_DEST_ID.in(daoFilter.getDestIds())); } if (daoFilter.isRedirect() != null) { conditions.add(Tables.UNIVERSAL_LINK.ALGORITHM_ID.eq(algorithmId)); } Cursor<Record> result = context.select(). from(Tables.UNIVERSAL_LINK). where(conditions). limit(daoFilter.getLimitOrInfinity()). fetchLazy(getFetchSize()); return buildUniversalLinksIterable(result, context); } catch (RuntimeException e) { freeJooq(context); throw e; } } @Override public int getCount(DaoFilter daoFilter) throws DaoException { DSLContext context = getJooq(); try { Collection<Condition> conditions = new ArrayList<Condition>(); if (daoFilter.getSourceIds() != null) { conditions.add(Tables.UNIVERSAL_LINK.UNIV_SOURCE_ID.in(daoFilter.getSourceIds())); } if (daoFilter.getDestIds() != null) { conditions.add(Tables.UNIVERSAL_LINK.UNIV_DEST_ID.in(daoFilter.getDestIds())); } if (daoFilter.isRedirect() != null) { conditions.add(Tables.UNIVERSAL_LINK.ALGORITHM_ID.eq(algorithmId)); } return context.selectDistinct(Tables.UNIVERSAL_LINK.UNIV_SOURCE_ID, Tables.UNIVERSAL_LINK.UNIV_DEST_ID, Tables.UNIVERSAL_LINK.ALGORITHM_ID) .from(Tables.UNIVERSAL_LINK) .where(conditions) .fetchCount(); } finally { freeJooq(context); } } @Override public UniversalLinkGroup getOutlinks(int sourceId) throws DaoException { DSLContext context = getJooq(); try { Cursor<Record> result = context.select(). from(Tables.UNIVERSAL_LINK). where(Tables.UNIVERSAL_LINK.UNIV_SOURCE_ID.eq(sourceId)). and(Tables.UNIVERSAL_LINK.ALGORITHM_ID.eq(algorithmId)). fetchLazy(getFetchSize()); return buildUniversalLinkGroup(result, true); } finally { freeJooq(context); } } @Override public TIntSet getOutlinkIds(int sourceId) throws DaoException{ DSLContext context = getJooq(); try { Cursor<Record> result = context.select(). from(Tables.UNIVERSAL_LINK). where(Tables.UNIVERSAL_LINK.UNIV_SOURCE_ID.eq(sourceId)). and(Tables.UNIVERSAL_LINK.ALGORITHM_ID.eq(algorithmId)). fetchLazy(getFetchSize()); TIntSet ids = new TIntHashSet(); for (Record record : result){ ids.add(record.getValue(Tables.UNIVERSAL_LINK.UNIV_DEST_ID)); } return ids; } finally { freeJooq(context); } } @Override public UniversalLinkGroup getInlinks(int destId) throws DaoException { DSLContext context = getJooq(); try { Cursor<Record> result = context.select(). from(Tables.UNIVERSAL_LINK). where(Tables.UNIVERSAL_LINK.UNIV_DEST_ID.eq(destId)). and(Tables.UNIVERSAL_LINK.ALGORITHM_ID.eq(algorithmId)). fetchLazy(getFetchSize()); return buildUniversalLinkGroup(result, false); } finally { freeJooq(context); } } @Override public TIntSet getInlinkIds(int destId) throws DaoException{ DSLContext context = getJooq(); try { Cursor<Record> result = context.select(). from(Tables.UNIVERSAL_LINK). where(Tables.UNIVERSAL_LINK.UNIV_DEST_ID.eq(destId)). and(Tables.UNIVERSAL_LINK.ALGORITHM_ID.eq(algorithmId)). fetchLazy(getFetchSize()); TIntSet ids = new TIntHashSet(); for (Record record : result){ ids.add(record.getValue(Tables.UNIVERSAL_LINK.UNIV_SOURCE_ID)); } return ids; } finally { freeJooq(context); } } @Override public UniversalLink getUniversalLink(int sourceId, int destId) throws DaoException { DSLContext context = getJooq(); try { Result<Record> result = context.select(). from(Tables.UNIVERSAL_LINK). where(Tables.UNIVERSAL_LINK.UNIV_SOURCE_ID.eq(sourceId)). and(Tables.UNIVERSAL_LINK.UNIV_DEST_ID.eq(destId)). and(Tables.UNIVERSAL_LINK.ALGORITHM_ID.eq(algorithmId)). fetch(); return buildUniversalLink(result); } finally { freeJooq(context); } } private UniversalLinkGroup buildUniversalLinkGroup(Cursor<Record> result, boolean outlinks) throws DaoException { if (!result.hasNext()) { return null; } Multimap<Integer, Record> allRecords = HashMultimap.create(); Set<Language> languages = new HashSet<Language>(); int commonId = -1; int algorithmId = -1; for (Record record : result) { allRecords.put( record.getValue(outlinks ? // Gets the unique ID of the links Tables.UNIVERSAL_LINK.UNIV_DEST_ID : // If links are outlinks, dest ID is unique Tables.UNIVERSAL_LINK.UNIV_SOURCE_ID), // If links are inlinks, source ID is unique record); languages.add(Language.getById(record.getValue(Tables.UNIVERSAL_LINK.LANG_ID))); if (commonId == -1) { commonId = record.getValue(outlinks ? // Gets the common ID of the links Tables.UNIVERSAL_LINK.UNIV_SOURCE_ID : // If links are outlinks, source ID is common Tables.UNIVERSAL_LINK.UNIV_DEST_ID); // If links are inlinks, dest ID is common; algorithmId = record.getValue(Tables.UNIVERSAL_LINK.ALGORITHM_ID); } } Map<Integer, UniversalLink> map = new HashMap<Integer, UniversalLink>(); for (Integer integer : allRecords.keySet()) { map.put(integer, buildUniversalLink(allRecords.get(integer))); } return new UniversalLinkGroup( map, outlinks, commonId, algorithmId, new LanguageSet(languages) ); } private Iterable<UniversalLink> buildUniversalLinksIterable(Cursor<Record> result, DSLContext context) throws DaoException { Set<Integer[]> links = new HashSet<Integer[]>(); for (Record record : result) { links.add(new Integer[]{ record.getValue(Tables.UNIVERSAL_LINK.UNIV_SOURCE_ID), record.getValue(Tables.UNIVERSAL_LINK.UNIV_DEST_ID), record.getValue(Tables.UNIVERSAL_LINK.ALGORITHM_ID)}); } return new SqlDaoIterable<UniversalLink, Integer[]>(result, links.iterator(), context) { @Override public UniversalLink transform(Integer[] item) throws DaoException { return getUniversalLink(item[0], item[1]); } }; } private UniversalLink buildUniversalLink(Collection<Record> records) throws DaoException { if (records == null || records.isEmpty()) { return null; } Multimap<Language, LocalLink> map = HashMultimap.create(records.size(), records.size()); for (Record record : records) { Language language = Language.getById(record.getValue(Tables.UNIVERSAL_LINK.LANG_ID)); LocalLink temp = localLinkDao.getLink( language, record.getValue(Tables.UNIVERSAL_LINK.LOCAL_SOURCE_ID), record.getValue(Tables.UNIVERSAL_LINK.LOCAL_DEST_ID) ); map.put(language, temp); } Record temp = records.iterator().next(); return new UniversalLink( temp.getValue(Tables.UNIVERSAL_LINK.UNIV_SOURCE_ID), temp.getValue(Tables.UNIVERSAL_LINK.UNIV_DEST_ID), temp.getValue(Tables.UNIVERSAL_LINK.ALGORITHM_ID), map ); } public static class Provider extends org.wikibrain.conf.Provider<UniversalLinkDao> { public Provider(Configurator configurator, org.wikibrain.conf.Configuration config) throws ConfigurationException { super(configurator, config); } @Override public Class getType() { return UniversalLinkDao.class; } @Override public String getPath() { return "dao.universalLink"; } @Override public UniversalLinkDao get(String name, Config config, Map<String, String> runtimeParams) throws ConfigurationException { if (!config.getString("type").equals("sql")) { return null; } try { int algorithmId = getConfig().get().getInt("mapper." + config.getString("mapper") + ".algorithmId"); return new UniversalLinkSqlDao( getConfigurator().get( WpDataSource.class, config.getString("dataSource")), getConfigurator().get( LocalLinkDao.class, config.getString("localLinkDao")), algorithmId ); } catch (DaoException e) { throw new ConfigurationException(e); } } } }