/* * Copyright (c) 2017 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.magma.datasource.mongodb; import java.util.*; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; import org.bson.BSONObject; import org.bson.types.ObjectId; import org.obiba.magma.*; import org.obiba.magma.datasource.mongodb.converter.ValueConverter; import org.obiba.magma.support.AbstractValueTable; import org.obiba.magma.support.NullTimestamps; import org.obiba.magma.type.DateTimeType; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObjectBuilder; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.mongodb.WriteConcern; import com.mongodb.gridfs.GridFS; public class MongoDBValueTable extends AbstractValueTable { private static final String VARIABLE_SUFFIX = "_variable"; private static final String VALUE_SET_SUFFIX = "_value_set"; private DBObject dbObject; public MongoDBValueTable(@NotNull Datasource datasource, @NotNull String name) { this(datasource, name, null); } public MongoDBValueTable(@NotNull Datasource datasource, @NotNull String name, @Nullable String entityType) { super(datasource, name); setVariableEntityProvider(new MongoDBVariableEntityProvider(this, entityType)); // ensure corresponding document is stored asDBObject(); } MongoDBFactory getMongoDBFactory() { return getMongoDBDatasource().getMongoDBFactory(); } DBCollection getValueTableCollection() { return getMongoDBDatasource().getValueTableCollection(); } DBCollection getVariablesCollection() { return getMongoDBFactory().execute(new MongoDBFactory.MongoDBCallback<DBCollection>() { @Override public DBCollection doWithDB(DB db) { DBCollection collection = db.getCollection(getId() + VARIABLE_SUFFIX); collection.createIndex("name"); return collection; } }); } DBCollection getValueSetCollection() { return getMongoDBFactory().execute(new MongoDBFactory.MongoDBCallback<DBCollection>() { @Override public DBCollection doWithDB(DB db) { return db.getCollection(getId() + VALUE_SET_SUFFIX); } }); } DBObject asDBObject() { if(dbObject == null) { dbObject = getValueTableCollection().findOne(BasicDBObjectBuilder.start() // .add("datasource", getMongoDBDatasource().asDBObject().get("_id")) // .add("name", getName()) // .get()); // create DBObject if not found if(dbObject == null) { dbObject = BasicDBObjectBuilder.start() // .add("datasource", getMongoDBDatasource().asDBObject().get("_id")) // .add("name", getName()) // .add("entityType", getEntityType()) // .add(MongoDBDatasource.TIMESTAMPS_FIELD, MongoDBDatasource.createTimestampsObject()).get(); getValueTableCollection().insert(dbObject, WriteConcern.ACKNOWLEDGED); } } return dbObject; } void setLastUpdate(Date date) { ((BSONObject) asDBObject().get(MongoDBDatasource.TIMESTAMPS_FIELD)).put("updated", date); getValueTableCollection().save(asDBObject()); getMongoDBDatasource().setLastUpdate(date); } private String getId() { return asDBObject().get("_id").toString(); } private ObjectId getIdAsObjectId() { return new ObjectId(getId()); } @Override protected void addVariableValueSource(VariableValueSource source) { super.addVariableValueSource(source); } @Override protected void removeVariableValueSource(String variableName) { super.removeVariableValueSource(variableName); } @Override public void initialise() { addVariableValueSources(new MongoDBVariableValueSourceFactory(this)); super.initialise(); } @Override public Timestamps getValueSetTimestamps(VariableEntity entity) throws NoSuchValueSetException { if(!hasValueSet(entity)) { throw new NoSuchValueSetException(this, entity); } return super.getValueSetTimestamps(entity); } @Override public Iterable<Timestamps> getValueSetTimestamps(final SortedSet<VariableEntity> entities) { if(entities.isEmpty()) { return ImmutableList.of(); } return new Iterable<Timestamps>() { @Override public Iterator<Timestamps> iterator() { return new TimestampsIterator(entities.iterator()); } }; } @Override public ValueSet getValueSet(VariableEntity entity) throws NoSuchValueSetException { if(!hasValueSet(entity)) { throw new NoSuchValueSetException(this, entity); } return new MongoDBValueSet(this, entity); } @Override protected ValueSetBatch getValueSetsBatch(List<VariableEntity> entities) { return new MongoDBValueSetBatch(this, entities); } @NotNull @Override public Timestamps getTimestamps() { return new Timestamps() { private final BSONObject timestampsObject = (BSONObject) asDBObject().get(MongoDBDatasource.TIMESTAMPS_FIELD); @NotNull @Override public Value getLastUpdate() { return DateTimeType.get().valueOf(timestampsObject.get(MongoDBDatasource.TIMESTAMPS_UPDATED_FIELD)); } @NotNull @Override public Value getCreated() { return DateTimeType.get().valueOf(timestampsObject.get(MongoDBDatasource.TIMESTAMPS_CREATED_FIELD)); } }; } public void drop() { // drop associated collections dropFiles(); getValueSetCollection().drop(); getVariablesCollection().drop(); getValueTableCollection().remove(BasicDBObjectBuilder.start().add("_id", getIdAsObjectId()).get()); getMongoDBDatasource().setLastUpdate(new Date()); dbObject = null; } DBObject findVariable(String variableName) { return getVariablesCollection().findOne(BasicDBObjectBuilder.start("name", variableName).get()); } @Override public int getVariableCount() { return (int) getVariablesCollection().count(); } @Override public int getValueSetCount() { return (int) getValueSetCollection().count(); } @Override public int getVariableEntityCount() { return (int) getValueSetCollection().count(); } @Override public boolean canDropValueSets() { return true; } @Override public void dropValueSets() { dropFiles(); getValueSetCollection().drop(); setLastUpdate(new Date()); } private MongoDBDatasource getMongoDBDatasource() { return ((MongoDBDatasource) getDatasource()); } /** * Drop the files from the {@link com.mongodb.gridfs.GridFS} for this table. */ private void dropFiles() { GridFS gridFS = getMongoDBDatasource().getMongoDBFactory().getGridFS(); BasicDBObjectBuilder metaDataQuery = BasicDBObjectBuilder.start() // .add("metadata.datasource", getDatasource().getName()) // .add("metadata.table", getName()); gridFS.remove(metaDataQuery.get()); } private class TimestampsIterator implements Iterator<Timestamps> { private final Iterator<VariableEntity> entities; private final DBCursor cursor; private final Map<String, Timestamps> timestampsMap = Maps.newHashMap(); private TimestampsIterator(Iterator<VariableEntity> entities) { this.entities = entities; DBObject fields = BasicDBObjectBuilder.start(MongoDBDatasource.TIMESTAMPS_FIELD, 1).get(); cursor = getValueSetCollection().find(new BasicDBObject(), fields); } @Override public boolean hasNext() { return entities.hasNext(); } @Override public Timestamps next() { VariableEntity entity = entities.next(); if(timestampsMap.containsKey(entity.getIdentifier())) return getTimestampsFromMap(entity); boolean found = false; while(cursor.hasNext() && !found) { DBObject obj = cursor.next(); String id = obj.get("_id").toString(); BSONObject timestamps = (BSONObject) obj.get(MongoDBDatasource.TIMESTAMPS_FIELD); timestampsMap.put(id, new TimestampsBean(ValueConverter.unmarshall(DateTimeType.get(), timestamps.get("created")), ValueConverter.unmarshall(DateTimeType.get(), timestamps.get("updated"))) ); found = id.equals(entity.getIdentifier()); } if(timestampsMap.containsKey(entity.getIdentifier())) return getTimestampsFromMap(entity); return NullTimestamps.get(); } /** * No duplicate of entities, so remove timestamps from map once get. * * @param entity * @return */ private Timestamps getTimestampsFromMap(VariableEntity entity) { Timestamps value = timestampsMap.get(entity.getIdentifier()); timestampsMap.remove(entity.getIdentifier()); return value; } @Override public void remove() { throw new UnsupportedOperationException(); } } }