Java tutorial
// This file is part of MongoMVCC. // // Copyright (c) 2012 Fraunhofer IGD // // MongoMVCC is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // MongoMVCC is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with MongoMVCC. If not, see <http://www.gnu.org/licenses/>. package de.fhg.igd.mongomvcc.impl; import java.util.Map; import com.mongodb.BasicDBObject; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import de.fhg.igd.mongomvcc.VCollection; import de.fhg.igd.mongomvcc.VConstants; import de.fhg.igd.mongomvcc.VCounter; import de.fhg.igd.mongomvcc.VCursor; import de.fhg.igd.mongomvcc.helper.Filter; import de.fhg.igd.mongomvcc.helper.IdMap; import de.fhg.igd.mongomvcc.impl.internal.Index; import de.fhg.igd.mongomvcc.impl.internal.MongoDBConstants; /** * Implements {@link VCollection} for MongoDB * @author Michel Kraemer */ public class MongoDBVCollection implements VCollection { /** * The attribute for the unique ID */ protected final static String UID = VConstants.UID; /** * The ID unique for each new object, regardless if it is really new * or a new copy of an already existing object */ protected final static String OID = MongoDBConstants.ID; /** * A {@link DBObject} that can be passed to find methods in order * to exclude the hidden fields from the result */ protected final static DBObject EXCLUDEHIDDENATTRS = new BasicDBObject(); static { EXCLUDEHIDDENATTRS.put(MongoDBConstants.LIFETIME, 0); EXCLUDEHIDDENATTRS.put(MongoDBConstants.TIMESTAMP, 0); } /** * The actual MongoDB collection */ private final DBCollection _delegate; /** * The collection's name */ private final String _name; /** * The branch currently checked out */ private final MongoDBVBranch _branch; /** * A counter to generate unique IDs */ private final VCounter _counter; /** * A predicate which filters out objects with OIDs not in the current index */ private final class OIDInIndexFilter implements Filter<DBObject> { private final Index _idx; OIDInIndexFilter() { _idx = _branch.getIndex(); } @Override public boolean filter(DBObject input) { return _idx.containsOID(_name, (Long) input.get(OID)); } } /** * Creates a new MongoDBVCollection. * @param delegate the actual MongoDB collection * @param branch the branch currently checked out * @param counter a counter to generate unique IDs */ public MongoDBVCollection(DBCollection delegate, MongoDBVBranch branch, VCounter counter) { _delegate = delegate; _name = delegate.getName(); _branch = branch; _counter = counter; } @Override public void insert(Map<String, Object> obj) { //check UID (throws ClassCastException of UID is no Long, this //is by intention) Long uid = (Long) obj.get(UID); if (uid == null) { uid = _counter.getNextId(); obj.put(UID, uid); } //generate OID long oid = _counter.getNextId(); obj.put(OID, oid); DBObject dbo; if (obj instanceof DBObject) { dbo = (DBObject) obj; } else { dbo = new BasicDBObject(obj); } //insert timestamp dbo.put(MongoDBConstants.TIMESTAMP, System.currentTimeMillis()); //insert object into database _delegate.insert(dbo); if (dbo == obj) { //remove timestamp from original document obj.remove(MongoDBConstants.TIMESTAMP); } //insert object into index _branch.getIndex().insert(_name, uid, oid); } @Override public void delete(long uid) { _branch.getIndex().delete(_name, uid); } @Override public void delete(Map<String, Object> example) { VCursor allIds = find(example, UID); for (Map<String, Object> id : allIds) { _branch.getIndex().delete(_name, (Long) id.get(UID)); } } /** * Creates a new cursor. Subclasses may override this method to * add specialized filters and converters. * @param delegate the actual MongoDB cursor * @param filter a filter which decides if a DBObject should be included * into the cursor's result or not (can be null) * @return the cursor */ protected VCursor createCursor(DBCursor delegate, Filter<DBObject> filter) { return new MongoDBVCursor(delegate, filter); } @Override public VCursor find() { //ask index for OIDs IdMap objs = _branch.getIndex().find(_name); if (objs.size() == 0) { return MongoDBVCursor.EMPTY; } //ask MongoDB for objects with the given OIDs if (objs.size() == 1) { //shortcut for one object return createCursor(_delegate.find(new BasicDBObject(OID, objs.values()[0]), EXCLUDEHIDDENATTRS), null); } else { DBObject qo = new BasicDBObject(); qo.putAll(_branch.getQueryObject()); return createCursor(_delegate.find(qo, EXCLUDEHIDDENATTRS), new OIDInIndexFilter()); } } @Override public VCursor find(Map<String, Object> example) { DBObject o = new BasicDBObject(); o.putAll(_branch.getQueryObject()); o.putAll(example); return createCursor(_delegate.find(o, EXCLUDEHIDDENATTRS), new OIDInIndexFilter()); } @Override public VCursor find(Map<String, Object> example, String... fields) { DBObject fo = new BasicDBObject(); for (String f : fields) { fo.put(f, 1); } //make sure UID and OID are also returned fo.put(UID, 1); fo.put(OID, 1); //exclude lifetime //FIXME MongoDB cannot currently mix including and excluding fields //FIXME if this is an issue for you, vote for https://jira.mongodb.org/browse/SERVER-391 //fo.putAll(EXCLUDELIFETIME); DBObject o = new BasicDBObject(); o.putAll(_branch.getQueryObject()); o.putAll(example); return createCursor(_delegate.find(o, fo), new OIDInIndexFilter()); } @SuppressWarnings("unchecked") @Override public Map<String, Object> findOne(Map<String, Object> example) { DBObject o = new BasicDBObject(); o.putAll(_branch.getQueryObject()); o.putAll(example); OIDInIndexFilter filter = new OIDInIndexFilter(); DBCursor c = _delegate.find(o, EXCLUDEHIDDENATTRS); for (DBObject obj : c) { if (filter.filter(obj)) { if (obj instanceof Map) { return (Map<String, Object>) obj; } return obj.toMap(); } } return null; } @Override public String getName() { return _name; } /** * @return the counter used to generate unique IDs */ protected VCounter getCounter() { return _counter; } }