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.map.TIntIntMap;
import gnu.trove.map.hash.TIntIntHashMap;
import org.jooq.*;
import org.wikibrain.conf.Configuration;
import org.wikibrain.conf.ConfigurationException;
import org.wikibrain.conf.Configurator;
import org.wikibrain.core.dao.*;
import org.wikibrain.core.jooq.Tables;
import org.wikibrain.core.lang.Language;
import org.wikibrain.core.lang.LanguageSet;
import org.wikibrain.core.lang.LocalId;
import org.wikibrain.core.model.LocalPage;
import org.wikibrain.core.model.NameSpace;
import org.wikibrain.core.model.UniversalPage;
import java.util.*;
/**
*
* A SQL database implementation of the UniversalPageDao.
*
* @author Ari Weiland
* @author Shilad Sen
*
*/
public class UniversalPageSqlDao extends AbstractSqlDao<UniversalPage> implements UniversalPageDao {
private static final TableField [] INSERT_FIELDS = new TableField[] {
Tables.UNIVERSAL_PAGE.LANG_ID,
Tables.UNIVERSAL_PAGE.PAGE_ID,
Tables.UNIVERSAL_PAGE.NAME_SPACE,
Tables.UNIVERSAL_PAGE.UNIV_ID,
Tables.UNIVERSAL_PAGE.ALGORITHM_ID
};
private final int algorithmId;
public UniversalPageSqlDao(WpDataSource dataSource, int algorithmId) throws DaoException {
super(dataSource, INSERT_FIELDS, "/db/universal-page");
this.algorithmId = algorithmId;
}
@Override
public void save(UniversalPage page) throws DaoException {
NameSpace nameSpace = page.getNameSpace();
for (Language language : page.getLanguageSet()) {
for (LocalId localPage : page.getLocalEntities(language)) {
insert(
language.getId(),
localPage.getId(),
nameSpace.getArbitraryId(),
page.getUnivId(),
page.getAlgorithmId()
);
}
}
}
@Override
public Iterable<UniversalPage> get(DaoFilter daoFilter) throws DaoException {
DSLContext context = getJooq();
try {
Collection<Condition> conditions = new ArrayList<Condition>();
if (daoFilter.getNameSpaceIds() != null) {
conditions.add(Tables.UNIVERSAL_PAGE.NAME_SPACE.in(daoFilter.getNameSpaceIds()));
}
if (daoFilter.isRedirect() != null) {
conditions.add(Tables.UNIVERSAL_PAGE.ALGORITHM_ID.eq(algorithmId));
}
Cursor<Record> result = context.select()
.from(Tables.UNIVERSAL_PAGE)
.where(conditions)
.limit(daoFilter.getLimitOrInfinity())
.fetchLazy(getFetchSize());
Set<int[]> pages = new HashSet<int[]>();
for (Record record : result) {
pages.add(new int[]{
record.getValue(Tables.UNIVERSAL_PAGE.UNIV_ID),
record.getValue(Tables.UNIVERSAL_PAGE.ALGORITHM_ID)});
}
return new SqlDaoIterable<UniversalPage, int[]>(result, pages.iterator(), context) {
@Override
public UniversalPage transform(int[] item) throws DaoException {
return getById(item[0]);
}
};
} 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.getNameSpaceIds() != null) {
conditions.add(Tables.UNIVERSAL_PAGE.NAME_SPACE.in(daoFilter.getNameSpaceIds()));
}
if (daoFilter.isRedirect() != null) {
conditions.add(Tables.UNIVERSAL_PAGE.ALGORITHM_ID.eq(algorithmId));
}
return context.selectDistinct(Tables.UNIVERSAL_PAGE.UNIV_ID, Tables.UNIVERSAL_PAGE.ALGORITHM_ID)
.from(Tables.UNIVERSAL_PAGE)
.where(conditions)
.fetchCount();
} finally {
freeJooq(context);
}
}
@Override
public UniversalPage getById(int univId) throws DaoException {
DSLContext context = getJooq();
try {
Result<Record> result = context.select()
.from(Tables.UNIVERSAL_PAGE)
.where(Tables.UNIVERSAL_PAGE.UNIV_ID.eq(univId))
.and(Tables.UNIVERSAL_PAGE.ALGORITHM_ID.eq(algorithmId))
.fetch();
return (UniversalPage)buildUniversalPage(result);
} finally {
freeJooq(context);
}
}
@Override
public Map<Integer, UniversalPage> getByIds(Collection<Integer> univIds) throws DaoException {
if (univIds == null || univIds.isEmpty()) {
return null;
}
Map<Integer, UniversalPage> map = new HashMap<Integer, UniversalPage>();
for (Integer univId : univIds){
map.put(univId, getById(univId));
}
return map;
}
@Override
public UniversalPage getByLocalPage(LocalPage localPage) throws DaoException {
int conceptId = getUnivPageId(localPage);
if (conceptId < 0) {
return null;
}
return getById(conceptId);
}
@Override
public int getUnivPageId(Language language, int localPageId) throws DaoException {
DSLContext context = getJooq();
try {
Record record = context.select()
.from(Tables.UNIVERSAL_PAGE)
.where(Tables.UNIVERSAL_PAGE.LANG_ID.eq(language.getId()))
.and(Tables.UNIVERSAL_PAGE.PAGE_ID.eq(localPageId))
.and(Tables.UNIVERSAL_PAGE.ALGORITHM_ID.eq(algorithmId))
.limit(1) // TODO: Remove
.fetchOne();
if (record == null) {
return -1;
} else {
return record.getValue(Tables.UNIVERSAL_PAGE.UNIV_ID);
}
} finally {
freeJooq(context);
}
}
@Override
public int getUnivPageId(LocalPage localPage) throws DaoException {
return getUnivPageId(localPage.getLanguage(), localPage.getLocalId());
}
@Override
public Map<Language, TIntIntMap> getAllLocalToUnivIdsMap(LanguageSet ls) throws DaoException {
DSLContext context = getJooq();
try {
Map<Language, TIntIntMap> map = new HashMap<Language, TIntIntMap>();
for (Language l : ls) {
Cursor<Record> cursor = context.select()
.from(Tables.UNIVERSAL_PAGE)
.where(Tables.UNIVERSAL_PAGE.ALGORITHM_ID.eq(algorithmId))
.and(Tables.UNIVERSAL_PAGE.LANG_ID.eq(l.getId()))
.fetchLazy(getFetchSize());
TIntIntMap ids = new TIntIntHashMap(
gnu.trove.impl.Constants.DEFAULT_CAPACITY,
gnu.trove.impl.Constants.DEFAULT_LOAD_FACTOR,
-1, -1);
for (Record record : cursor) {
ids.put(record.getValue(Tables.UNIVERSAL_PAGE.PAGE_ID),
record.getValue(Tables.UNIVERSAL_PAGE.UNIV_ID));
}
map.put(l, ids);
}
return map;
} finally {
freeJooq(context);
}
}
@Override
public Map<Language, TIntIntMap> getAllUnivToLocalIdsMap(LanguageSet ls) throws DaoException {
DSLContext context = getJooq();
try {
Map<Language, TIntIntMap> map = new HashMap<Language, TIntIntMap>();
for (Language l : ls) {
Cursor<Record> cursor = context.select()
.from(Tables.UNIVERSAL_PAGE)
.where(Tables.UNIVERSAL_PAGE.ALGORITHM_ID.eq(algorithmId))
.and(Tables.UNIVERSAL_PAGE.LANG_ID.eq(l.getId()))
.fetchLazy(getFetchSize());
TIntIntMap ids = new TIntIntHashMap(
gnu.trove.impl.Constants.DEFAULT_CAPACITY,
gnu.trove.impl.Constants.DEFAULT_LOAD_FACTOR,
-1, -1);
for (Record record : cursor) {
ids.put(record.getValue(Tables.UNIVERSAL_PAGE.UNIV_ID),
record.getValue(Tables.UNIVERSAL_PAGE.PAGE_ID));
}
map.put(l, ids);
}
return map;
} finally {
freeJooq(context);
}
}
/**
* Returns the local page ids for that language, value in map is -1 they do not exist
* @param language
* @param universalIds
* @return
* @throws DaoException
*/
@Override
public Map<Integer, Integer> getLocalIds(Language language, Collection<Integer> universalIds) throws DaoException {
DSLContext context = getJooq();
try {
Object rows[][] = context
.select(Tables.UNIVERSAL_PAGE.UNIV_ID, Tables.UNIVERSAL_PAGE.PAGE_ID)
.from(Tables.UNIVERSAL_PAGE)
.where(Tables.UNIVERSAL_PAGE.LANG_ID.eq(language.getId()))
.and(Tables.UNIVERSAL_PAGE.UNIV_ID.in(universalIds))
.and(Tables.UNIVERSAL_PAGE.ALGORITHM_ID.eq(algorithmId))
.fetchArrays();
Map<Integer, Integer> result = new HashMap<Integer, Integer>();
if (rows == null) {
return result;
}
for (Object [] row : rows) {
result.put((Integer)row[0], (Integer)row[1]);
}
return result;
} finally {
freeJooq(context);
}
}
/**
* Returns the local page id for that language, or -1 if it does not exist
* @param language
* @param universalId
* @return
* @throws DaoException
*/
@Override
public int getLocalId(Language language, int universalId) throws DaoException {
DSLContext context = getJooq();
try {
Record record = context.select()
.from(Tables.UNIVERSAL_PAGE)
.where(Tables.UNIVERSAL_PAGE.LANG_ID.eq(language.getId()))
.and(Tables.UNIVERSAL_PAGE.UNIV_ID.eq(universalId))
.and(Tables.UNIVERSAL_PAGE.ALGORITHM_ID.eq(algorithmId))
.limit(1) // TODO: Remove
.fetchOne();
if (record == null) {
return -1;
} else {
return record.getValue(Tables.UNIVERSAL_PAGE.PAGE_ID);
}
} finally {
freeJooq(context);
}
}
/**
* Build a UniversalPage from a database record representation.
* Classes that extend class this should override this method.
*
* @param result a list of database records
* @return a UniversalPage representation of the given database record
* @throws DaoException if the record is not a Page
*/
protected UniversalPage buildUniversalPage(List<Record> result) throws DaoException {
if (result == null || result.isEmpty()) {
return null;
}
Multimap<Language, LocalId> localPages = HashMultimap.create(result.size(), result.size());
NameSpace nameSpace = NameSpace.getNameSpaceByArbitraryId(result.get(0).getValue(Tables.LOCAL_PAGE.NAME_SPACE));
for(Record record : result) {
Language language = Language.getById(record.getValue(Tables.UNIVERSAL_PAGE.LANG_ID));
int pageId = record.getValue(Tables.UNIVERSAL_PAGE.PAGE_ID);
localPages.put(language, new LocalId(language, pageId));
}
return new UniversalPage(
result.get(0).getValue(Tables.UNIVERSAL_PAGE.UNIV_ID),
result.get(0).getValue(Tables.UNIVERSAL_PAGE.ALGORITHM_ID),
nameSpace,
localPages
);
}
public static class Provider extends org.wikibrain.conf.Provider<UniversalPageDao> {
public Provider(Configurator configurator, Configuration config) throws ConfigurationException {
super(configurator, config);
}
@Override
public Class getType() {
return UniversalPageDao.class;
}
@Override
public String getPath() {
return "dao.universalPage";
}
@Override
public UniversalPageDao 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 UniversalPageSqlDao(
getConfigurator().get(
WpDataSource.class,
config.getString("dataSource")),
algorithmId
);
} catch (DaoException e) {
throw new ConfigurationException(e);
}
}
}
}