/*
* 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.Iterator;
import java.util.Map;
import java.util.SortedSet;
import javax.validation.constraints.NotNull;
import org.bson.BSONObject;
import org.obiba.magma.Value;
import org.obiba.magma.ValueSet;
import org.obiba.magma.ValueType;
import org.obiba.magma.VariableEntity;
import org.obiba.magma.VariableValueSource;
import org.obiba.magma.VectorSource;
import org.obiba.magma.datasource.mongodb.converter.ValueConverter;
import org.obiba.magma.datasource.mongodb.converter.VariableConverter;
import org.obiba.magma.type.BinaryType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
public class MongoDBVariableValueSource implements VariableValueSource, VectorSource {
private final MongoDBValueTable table;
private final String name;
private MongoDBVariable variable;
private Value lastUpdated;
public MongoDBVariableValueSource(MongoDBValueTable table, String name) {
this.table = table;
this.name = name;
}
@NotNull
@Override
public String getName() {
return name;
}
@NotNull
@Override
public synchronized MongoDBVariable getVariable() {
Value tableLastUpdate = table.getTimestamps().getLastUpdate();
if(variable == null || lastUpdated == null || !lastUpdated.equals(tableLastUpdate)) {
lastUpdated = tableLastUpdate;
variable = VariableConverter.unmarshall(table.findVariable(name));
}
return variable;
}
@NotNull
@Override
public ValueType getValueType() {
return getVariable().getValueType();
}
@Override
public Iterable<Value> getValues(final SortedSet<VariableEntity> entities) {
if(entities.isEmpty()) {
return ImmutableList.of();
}
return () -> new ValueIterator(getVariable(), entities.iterator());
}
@NotNull
@Override
public Value getValue(ValueSet valueSet) {
return ((MongoDBValueSet) valueSet).getValue(getVariable());
}
@Override
public boolean supportVectorSource() {
return true;
}
@NotNull
@Override
public VectorSource asVectorSource() {
return this;
}
private class ValueIterator implements Iterator<Value> {
private final String field;
private final ValueType type;
private final boolean repeatable;
private final DBObject fields;
private final Iterator<VariableEntity> entities;
private final DBCursor cursor;
private final Map<String, Value> valueMap = Maps.newHashMap();
private ValueIterator(MongoDBVariable variable, Iterator<VariableEntity> entities) {
field = variable.getId();
type = variable.getValueType();
repeatable = variable.isRepeatable();
fields = BasicDBObjectBuilder.start(field, 1).get();
this.entities = entities;
cursor = table.getValueSetCollection().find(new BasicDBObject(), fields);
}
@Override
public boolean hasNext() {
return entities.hasNext();
}
@Override
public Value next() {
VariableEntity entity = entities.next();
if(valueMap.containsKey(entity.getIdentifier())) return getValueFromMap(entity);
boolean found = false;
while(cursor.hasNext() && !found) {
DBObject obj = cursor.next();
String id = obj.get("_id").toString();
Value value = variable.getValueType().equals(BinaryType.get())
? getBinaryValue(obj)
: ValueConverter.unmarshall(type, repeatable, field, obj);
valueMap.put(id, value);
found = id.equals(entity.getIdentifier());
}
if(valueMap.containsKey(entity.getIdentifier())) return getValueFromMap(entity);
return ValueConverter.unmarshall(type, repeatable, field, null);
}
private Value getBinaryValue(BSONObject valueObject) {
return MongoDBValueSet.getBinaryValue(table.getMongoDBFactory(), variable, valueObject);
}
/**
* No duplicate of entities, so remove value from map once get.
* @param entity
* @return
*/
private Value getValueFromMap(VariableEntity entity) {
Value value = valueMap.get(entity.getIdentifier());
valueMap.remove(entity.getIdentifier());
return value;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}