Java tutorial
/* * Copyright 2011 VZ Netzwerke Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.vz.mongodb.jackson; import com.mongodb.*; import net.vz.mongodb.jackson.internal.IdHandler; import net.vz.mongodb.jackson.internal.IdHandlerFactory; import net.vz.mongodb.jackson.internal.MongoJacksonMapperModule; import net.vz.mongodb.jackson.internal.object.BsonObjectGenerator; import net.vz.mongodb.jackson.internal.object.BsonObjectTraversingParser; import net.vz.mongodb.jackson.internal.stream.JacksonDBObject; import net.vz.mongodb.jackson.internal.stream.JacksonDecoderFactory; import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.ObjectMapper; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * A DBCollection that marshals/demarshals objects to/from Jackson annotated classes. It provides a very thin wrapper * over an existing DBCollection. * * @author James Roper * @since 1.0 */ public class JacksonDBCollection<T, K> { public enum Feature { /** * Deserialise objects directly from the MongoDB stream. This is the default, as it performs the best. If set * to false, then it uses the MongoDB driver to deserialise objects to DBObjects, and then traverses those * objects to do the Jackson parsing. This may be desirable, for example, when auto hydrating of objects is * enabled, because in order to hydrate objects, a second connection needs to be made to MongoDB, which has the * potential to deadlock when the connection pool gets exhausted when using stream deserialization. Using * object deserialization, the hydration occurs after the connection to load the object has been returned to * the pool. */ USE_STREAM_DESERIALIZATION(true), /** * Automatically hydrate all DB references. This is not advised, either declare the objects to be of type * {@link net.vz.mongodb.jackson.DBRef}, and hydrate by calling fetch(), or declare it as the referenced type * and use {@link net.vz.mongodb.jackson.JacksonDBCollection.hydrate()} to hydrate the referenced types. * <p/> * If using this feature, it is strongly recommended that you set {@link Feature.USE_STREAM_DESERIALIZATION} to * false, to avoid potential deadlocks. */ AUTO_HYDRATE(false); Feature(boolean enabledByDefault) { this.enabledByDefault = enabledByDefault; } private final boolean enabledByDefault; public boolean isEnabledByDefault() { return enabledByDefault; } } private static final ObjectMapper DEFAULT_OBJECT_MAPPER = new ObjectMapper(); static { // Configure to use the object id annotation introspector DEFAULT_OBJECT_MAPPER.registerModule(MongoJacksonMapperModule.INSTANCE); } private final DBCollection dbCollection; private final Class<T> type; private final Class<K> keyType; private final ObjectMapper objectMapper; private final IdHandler<K, Object> idHandler; private final JacksonDecoderFactory<T> decoderFactory; private final Map<Feature, Boolean> features; protected JacksonDBCollection(DBCollection dbCollection, Class<T> type, Class<K> keyType, ObjectMapper objectMapper) { this.dbCollection = dbCollection; this.type = type; this.keyType = keyType; this.objectMapper = objectMapper; this.decoderFactory = new JacksonDecoderFactory<T>(objectMapper, type); // We want to find how we should serialize the ID, in case it is passed to us try { this.idHandler = (IdHandler) IdHandlerFactory.getIdHandlerForProperty(objectMapper, type, "_id", keyType); } catch (JsonMappingException e) { throw new MongoJsonMappingException("Unable to introspect class", e); } this.features = new ConcurrentHashMap<Feature, Boolean>(); } /** * Wraps a DB collection in a JacksonDBCollection * * @param dbCollection The DB collection to wrap * @param type The type of objects to deserialise to * @return The wrapped collection */ public static <T> JacksonDBCollection<T, Object> wrap(DBCollection dbCollection, Class<T> type) { return new JacksonDBCollection<T, Object>(dbCollection, type, Object.class, DEFAULT_OBJECT_MAPPER); } /** * Wraps a DB collection in a JacksonDBCollection * * @param dbCollection The DB collection to wrap * @param type The type of objects to deserialise to * @param keyType The type of the objects key * @return The wrapped collection */ public static <T, K> JacksonDBCollection<T, K> wrap(DBCollection dbCollection, Class<T> type, Class<K> keyType) { return new JacksonDBCollection<T, K>(dbCollection, type, keyType, DEFAULT_OBJECT_MAPPER); } /** * Wraps a DB collection in a JacksonDBCollection * * @param dbCollection The DB collection to wrap * @param type The type of objects to deserialise to * @param keyType The type of the objects key * @param view The JSON view to use for serialisation * @return The wrapped collection */ public static <T, K> JacksonDBCollection<T, K> wrap(DBCollection dbCollection, Class<T> type, Class<K> keyType, Class<?> view) { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.withModule(MongoJacksonMapperModule.INSTANCE); objectMapper.setSerializationConfig(objectMapper.getSerializationConfig().withView(view)); return new JacksonDBCollection<T, K>(dbCollection, type, keyType, objectMapper); } /** * Wraps a DB collection in a JacksonDBCollection, using the given object mapper * * @param dbCollection The DB collection to wrap * @param type The type of objects to deserialise to * @param objectMapper The ObjectMapper * @return The wrapped collection */ public static <T, K> JacksonDBCollection<T, K> wrap(DBCollection dbCollection, Class<T> type, Class<K> keyType, ObjectMapper objectMapper) { return new JacksonDBCollection<T, K>(dbCollection, type, keyType, objectMapper); } /** * Enable the given feature * * @param feature The feature to enable * @return this object */ public JacksonDBCollection<T, K> enable(Feature feature) { features.put(feature, true); return this; } /** * Disable the given feature * * @param feature The feature to disable * @return this object */ public JacksonDBCollection<T, K> disable(Feature feature) { features.put(feature, false); return this; } /** * Whether the given feature is enabled * * @param feature The feature to check * @return whether it is enabled */ public boolean isEnabled(Feature feature) { Boolean enabled = features.get(feature); if (enabled == null) { return feature.isEnabledByDefault(); } else { return enabled; } } /** * Get the underlying db collection * * @return The underlying db collection */ public DBCollection getDbCollection() { return dbCollection; } /** * Inserts an object into the database. * if the objects _id is null, one will be generated * you can get the _id that was generated by calling getSavedObject() or getSavedId() on the result * * @param object The object to insert * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> insert(T object) throws MongoException { DBObject dbObject = convertToDbObject(object); return new WriteResult<T, K>(this, dbCollection.insert(dbObject), dbObject); } /** * Inserts an object into the database. * if the objects _id is null, one will be generated * you can get the _id that was generated by calling getSavedObject() or getSavedId() on the result * * @param object The object to insert * @param concern the write concern * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> insert(T object, WriteConcern concern) throws MongoException { DBObject dbObject = convertToDbObject(object); return new WriteResult<T, K>(this, dbCollection.insert(dbObject, concern), dbObject); } /** * Inserts objects into the database. * if the objects _id is null, one will be generated * you can get the _id that were generated by calling getSavedObjects() or getSavedIds() on the result * * @param objects The objects to insert * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> insert(T... objects) throws MongoException { DBObject[] dbObjects = convertToDbObjects(objects); return new WriteResult<T, K>(this, dbCollection.insert(dbObjects), dbObjects); } /** * Inserts objects into the database. * if the objects _id is null, one will be generated * you can get the _id that were generated by calling getSavedObjects() or getSavedIds() on the result * * @param objects The objects to insert * @param concern the write concern * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> insert(WriteConcern concern, T... objects) throws MongoException { DBObject[] dbObjects = convertToDbObjects(objects); return new WriteResult<T, K>(this, dbCollection.insert(concern, dbObjects), dbObjects); } /** * Inserts objects into the database. * if the objects _id is null, one will be generated * you can get the _id that were generated by calling getSavedObjects() or getSavedIds() on the result * * @param list The objects to insert * @return The result * @throws MongoException If an error occurred */ @SuppressWarnings({ "unchecked" }) public WriteResult<T, K> insert(List<T> list) throws MongoException { return insert(list.toArray((T[]) new Object[list.size()])); } /** * Inserts objects into the database. * if the objects _id is null, one will be generated * you can get the _id that were generated by calling getSavedObjects() or getSavedIds() on the result * * @param list The objects to insert * @param concern the write concern * @return The result * @throws MongoException If an error occurred */ @SuppressWarnings({ "unchecked" }) public WriteResult<T, K> insert(List<T> list, WriteConcern concern) throws MongoException { return insert(concern, list.toArray((T[]) new Object[list.size()])); } /** * Performs an update operation. * * @param query search query for old object to update * @param object object with which to update <tt>query</tt> * @param upsert if the database should create the element if it does not exist * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above). An object will * not be inserted if it does not exist in the collection and upsert=true and multi=true. * See <a href="http://www.mongodb.org/display/DOCS/Atomic+Operations">http://www.mongodb.org/display/DOCS/Atomic+Operations</a> * @param concern the write concern * @return The write result * @throws MongoException If an error occurred */ public WriteResult<T, K> update(DBObject query, DBObject object, boolean upsert, boolean multi, WriteConcern concern) throws MongoException { return new WriteResult<T, K>(this, dbCollection.update(query, object, upsert, multi, concern)); } /** * Performs an update operation. * * @param query search query for old object to update * @param update update with which to update <tt>query</tt> * @param upsert if the database should create the element if it does not exist * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above). An object will * not be inserted if it does not exist in the collection and upsert=true and multi=true. * See <a href="http://www.mongodb.org/display/DOCS/Atomic+Operations">http://www.mongodb.org/display/DOCS/Atomic+Operations</a> * @param concern the write concern * @return The write result * @throws MongoException If an error occurred */ public WriteResult<T, K> update(DBObject query, DBUpdate.Builder update, boolean upsert, boolean multi, WriteConcern concern) throws MongoException { return this.update(query, update.serialiseAndGet(objectMapper), upsert, multi, concern); } /** * Performs an update operation. * * @param query search query for old object to update * @param object object with which to update <tt>query</tt> * @param upsert if the database should create the element if it does not exist * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above). An object will * not be inserted if it does not exist in the collection and upsert=true and multi=true. * See <a href="http://www.mongodb.org/display/DOCS/Atomic+Operations">http://www.mongodb.org/display/DOCS/Atomic+Operations</a> * @param concern the write concern * @return The write result * @throws MongoException If an error occurred */ public WriteResult<T, K> update(T query, T object, boolean upsert, boolean multi, WriteConcern concern) throws MongoException { return update(convertToDbObject(query), convertToDbObject(object), upsert, multi, concern); } /** * Performs an update operation. * * @param query search query for old object to update * @param object object with which to update <tt>q</tt> * @param upsert if the database should create the element if it does not exist * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above). An object will * not be inserted if it does not exist in the collection and upsert=true and multi=true. * See <a href="http://www.mongodb.org/display/DOCS/Atomic+Operations">http://www.mongodb.org/display/DOCS/Atomic+Operations</a> * @param concern the write concern * @param encoder the DBEncoder to use * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> update(DBObject query, DBObject object, boolean upsert, boolean multi, WriteConcern concern, DBEncoder encoder) throws MongoException { return new WriteResult<T, K>(this, dbCollection.update(query, object, upsert, multi, concern, encoder)); } /** * Performs an update operation. * * @param query search query for old object to update * @param object object with which to update <tt>q</tt> * @param upsert if the database should create the element if it does not exist * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above). An object will * not be inserted if it does not exist in the collection and upsert=true and multi=true. * See <a href="http://www.mongodb.org/display/DOCS/Atomic+Operations">http://www.mongodb.org/display/DOCS/Atomic+Operations</a> * @param concern the write concern * @param encoder the DBEncoder to use * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> update(T query, T object, boolean upsert, boolean multi, WriteConcern concern, DBEncoder encoder) throws MongoException { return update(convertToDbObject(query), convertToDbObject(object), upsert, multi, concern, encoder); } /** * calls {@link DBCollection#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean, com.mongodb.WriteConcern)} with default WriteConcern. * * @param query search query for old object to update * @param object object with which to update <tt>q</tt> * @param upsert if the database should create the element if it does not exist * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above) * See http://www.mongodb.org/display/DOCS/Atomic+Operations * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> update(DBObject query, DBObject object, boolean upsert, boolean multi) throws MongoException { return update(query, object, upsert, multi, getWriteConcern()); } /** * Performs an update operation. * * @param query search query for old object to update * @param update update with which to update <tt>query</tt> * @param upsert if the database should create the element if it does not exist * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above). An object will * not be inserted if it does not exist in the collection and upsert=true and multi=true. * See <a href="http://www.mongodb.org/display/DOCS/Atomic+Operations">http://www.mongodb.org/display/DOCS/Atomic+Operations</a> * @return The write result * @throws MongoException If an error occurred */ public WriteResult<T, K> update(DBObject query, DBUpdate.Builder update, boolean upsert, boolean multi) throws MongoException { return this.update(query, update.serialiseAndGet(objectMapper), upsert, multi); } /** * calls {@link DBCollection#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean, com.mongodb.WriteConcern)} with default WriteConcern. * * @param query search query for old object to update * @param object object with which to update <tt>q</tt> * @param upsert if the database should create the element if it does not exist * @param multi if the update should be applied to all objects matching (db version 1.1.3 and above) * See http://www.mongodb.org/display/DOCS/Atomic+Operations * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> update(T query, T object, boolean upsert, boolean multi) throws MongoException { return update(query, object, upsert, multi, getWriteConcern()); } /** * calls {@link DBCollection#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean)} with upsert=false and multi=false * * @param query search query for old object to update * @param object object with which to update <tt>query</tt> * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> update(DBObject query, DBObject object) throws MongoException { return update(query, object, false, false); } /** * Performs an update operation. * * @param query search query for old object to update * @param update update with which to update <tt>query</tt> * @return The write result * @throws MongoException If an error occurred */ public WriteResult<T, K> update(DBObject query, DBUpdate.Builder update) throws MongoException { return this.update(query, update.serialiseAndGet(objectMapper)); } /** * calls {@link DBCollection#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean)} with upsert=false and multi=false * * @param query search query for old object to update * @param object object with which to update <tt>query</tt> * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> update(T query, T object) throws MongoException { return update(query, object, false, false); } /** * calls {@link DBCollection#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean)} with upsert=false and multi=false * * @param id the id of the object to update * @param object object with which to update <tt>query</tt> * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> updateById(K id, T object) throws MongoException { return update(createIdQuery(id), convertToDbObject(object), false, false); } /** * Performs an update operation. * * @param id The id of the document to update * @param update update with which to update <tt>query</tt> * @return The write result * @throws MongoException If an error occurred */ public WriteResult<T, K> updateById(K id, DBUpdate.Builder update) throws MongoException { return this.update(createIdQuery(id), update.serialiseAndGet(objectMapper)); } /** * calls {@link DBCollection#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean)} with upsert=false and multi=true * * @param query search query for old object to update * @param object object with which to update <tt>query</tt> * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> updateMulti(DBObject query, DBObject object) throws MongoException { return update(query, object, false, true); } /** * calls {@link DBCollection#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean)} with upsert=false and multi=true * * @param query search query for old object to update * @param update update with which to update <tt>query</tt> * @return The write result * @throws MongoException If an error occurred */ public WriteResult<T, K> updateMulti(DBObject query, DBUpdate.Builder update) throws MongoException { return this.updateMulti(query, update.serialiseAndGet(objectMapper)); } /** * calls {@link DBCollection#update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean)} with upsert=false and multi=true * * @param query search query for old object to update * @param object object with which to update <tt>query</tt> * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> updateMulti(T query, T object) throws MongoException { return update(query, object, false, true); } /** * Removes objects from the database collection. * * @param object the object that documents to be removed must match * @param concern WriteConcern for this operation * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> remove(DBObject object, WriteConcern concern) throws MongoException { return new WriteResult<T, K>(this, dbCollection.remove(object, concern)); } /** * Removes objects from the database collection. * * @param object the object that documents to be removed must match * @param concern WriteConcern for this operation * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> remove(T object, WriteConcern concern) throws MongoException { return remove(convertToDbObject(object), concern); } /** * Removes objects from the database collection. * * @param object the object that documents to be removed must match * @param concern WriteConcern for this operation * @param encoder the DBEncoder to use * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> remove(DBObject object, WriteConcern concern, DBEncoder encoder) throws MongoException { return new WriteResult<T, K>(this, dbCollection.remove(object, concern, encoder)); } /** * calls {@link DBCollection#remove(com.mongodb.DBObject, com.mongodb.WriteConcern)} with the default WriteConcern * * @param object the object that documents to be removed must match * @return The write result * @throws MongoException If an error occurred */ public WriteResult<T, K> remove(DBObject object) throws MongoException { return new WriteResult<T, K>(this, dbCollection.remove(object)); } /** * calls {@link DBCollection#remove(com.mongodb.DBObject, com.mongodb.WriteConcern)} with the default WriteConcern * * @param object the object that documents to be removed must match * @return The write result * @throws MongoException If an error occurred */ public WriteResult<T, K> remove(T object) throws MongoException { return remove(convertToDbObject(object)); } /** * calls {@link DBCollection#remove(com.mongodb.DBObject, com.mongodb.WriteConcern)} with the default WriteConcern * * @param id the id of the document to remove * @return The write result * @throws MongoException If an error occurred */ public WriteResult<T, K> removeById(K id) throws MongoException { return new WriteResult<T, K>(this, dbCollection.remove(createIdQuery(id))); } /** * Finds the first document in the query and updates it. * * @param query query to match * @param fields fields to be returned * @param sort sort to apply before picking first document * @param remove if true, document found will be removed * @param update update to apply * @param returnNew if true, the updated document is returned, otherwise the old document is returned (or it would be lost forever) * @param upsert do upsert (insert if document not present) * @return the object */ public T findAndModify(DBObject query, DBObject fields, DBObject sort, boolean remove, DBObject update, boolean returnNew, boolean upsert) { return convertFromDbObject( dbCollection.findAndModify(query, fields, sort, remove, update, returnNew, upsert)); } /** * calls {@link DBCollection#findAndModify(com.mongodb.DBObject, com.mongodb.DBObject, com.mongodb.DBObject, boolean, com.mongodb.DBObject, boolean, boolean)} * with fields=null, remove=false, returnNew=false, upsert=false * * @param query The query * @param sort The sort * @param update The update to apply * @return the old object */ public T findAndModify(DBObject query, DBObject sort, DBObject update) { return findAndModify(query, null, sort, false, update, false, false); } /** * calls {@link DBCollection#findAndModify(com.mongodb.DBObject, com.mongodb.DBObject, com.mongodb.DBObject, boolean, com.mongodb.DBObject, boolean, boolean)} * with fields=null, sort=null, remove=false, returnNew=false, upsert=false * * @param query The query * @param update The update to apply * @return the old object */ public T findAndModify(DBObject query, DBObject update) { return findAndModify(query, null, null, false, update, false, false); } /** * calls {@link DBCollection#findAndModify(com.mongodb.DBObject, com.mongodb.DBObject, com.mongodb.DBObject, boolean, com.mongodb.DBObject, boolean, boolean)} * with fields=null, sort=null, remove=true, returnNew=false, upsert=false * * @param query The query * @return the removed object */ public T findAndRemove(DBObject query) { return findAndModify(query, null, null, true, null, false, false); } /** * calls {@link DBCollection#createIndex(com.mongodb.DBObject, com.mongodb.DBObject)} with default index options * * @param keys an object with a key set of the fields desired for the index * @throws MongoException If an error occurred */ public final void createIndex(final DBObject keys) throws MongoException { dbCollection.createIndex(keys); } /** * Forces creation of an index on a set of fields, if one does not already exist. * * @param keys The keys to index * @param options The options * @throws MongoException If an error occurred */ public void createIndex(DBObject keys, DBObject options) throws MongoException { dbCollection.createIndex(keys, options); } /** * Forces creation of an index on a set of fields, if one does not already exist. * * @param keys The keys to index * @param options The index options * @param encoder the DBEncoder to use * @throws MongoException If an error occurred */ public void createIndex(DBObject keys, DBObject options, DBEncoder encoder) throws MongoException { dbCollection.createIndex(keys, options, encoder); } /** * Creates an ascending index on a field with default options, if one does not already exist. * * @param name name of field to index on */ public final void ensureIndex(final String name) { ensureIndex(new BasicDBObject(name, 1)); } /** * calls {@link DBCollection#ensureIndex(com.mongodb.DBObject, com.mongodb.DBObject)} with default options * * @param keys an object with a key set of the fields desired for the index * @throws MongoException If an error occurred */ public final void ensureIndex(final DBObject keys) throws MongoException { dbCollection.ensureIndex(keys); } /** * calls {@link DBCollection#ensureIndex(com.mongodb.DBObject, java.lang.String, boolean)} with unique=false * * @param keys fields to use for index * @param name an identifier for the index * @throws MongoException If an error occurred */ public void ensureIndex(DBObject keys, String name) throws MongoException { ensureIndex(keys, name, false); } /** * Ensures an index on this collection (that is, the index will be created if it does not exist). * * @param keys fields to use for index * @param name an identifier for the index. If null or empty, the default name will be used. * @param unique if the index should be unique * @throws MongoException If an error occurred */ public void ensureIndex(DBObject keys, String name, boolean unique) throws MongoException { dbCollection.ensureIndex(keys, name, unique); } /** * Creates an index on a set of fields, if one does not already exist. * * @param keys an object with a key set of the fields desired for the index * @param optionsIN options for the index (name, unique, etc) * @throws MongoException If an error occurred */ public void ensureIndex(final DBObject keys, final DBObject optionsIN) throws MongoException { dbCollection.ensureIndex(keys, optionsIN); } /** * Clears all indices that have not yet been applied to this collection. */ public void resetIndexCache() { dbCollection.resetIndexCache(); } /** * Set hint fields for this collection (to optimize queries). * * @param lst a list of <code>DBObject</code>s to be used as hints */ public void setHintFields(List<DBObject> lst) { dbCollection.setHintFields(lst); } /** * Queries for an object in this collection. * * @param query object for which to search * @return an iterator over the results * @throws MongoException If an error occurred */ public DBCursor<T> find(DBObject query) throws MongoException { return new DBCursor<T>(this, dbCollection.find(query)); } /** * Queries for an object in this collection. * * @param query object for which to search * @return an iterator over the results * @throws MongoException If an error occurred */ public DBCursor<T> find(T query) throws MongoException { return find(convertToDbObject(query)); } /** * Queries for an object in this collection. * <p/> * <p> * An empty DBObject will match every document in the collection. * Regardless of fields specified, the _id fields are always returned. * </p> * <p> * An example that returns the "x" and "_id" fields for every document * in the collection that has an "x" field: * </p> * <blockquote><pre> * BasicDBObject keys = new BasicDBObject(); * keys.put("x", 1); * <p/> * DBCursor cursor = collection.find(new BasicDBObject(), keys); * </pre></blockquote> * * @param query object for which to search * @param keys fields to return * @return a cursor to iterate over results */ public final DBCursor<T> find(DBObject query, DBObject keys) { return new DBCursor<T>(this, dbCollection.find(query, keys)); } /** * Queries for an object in this collection. * <p/> * <p> * An empty DBObject will match every document in the collection. * Regardless of fields specified, the _id fields are always returned. * </p> * To keys object should have non null values for every key that you want to return * * @param query object for which to search * @param keys fields to return * @return a cursor to iterate over results */ public final DBCursor<T> find(T query, T keys) { return find(convertToDbObject(query), convertToDbObject(keys)); } /** * Queries for all objects in this collection. * * @return a cursor which will iterate over every object * @throws MongoException If an error occurred */ public final DBCursor<T> find() throws MongoException { return new DBCursor<T>(this, dbCollection.find()); } /** * Returns a single object from this collection. * * @return the object found, or <code>null</code> if the collection is empty * @throws MongoException If an error occurred */ public T findOne() throws MongoException { return findOne(new BasicDBObject()); } /** * Find an object by the given id * * @param id The id * @return The object * @throws MongoException If an error occurred */ public T findOneById(K id) throws MongoException { return findOneById(id, (DBObject) null); } /** * Find an object by the given id * * @param id The id * @return The object * @throws MongoException If an error occurred */ public T findOneById(K id, DBObject fields) throws MongoException { return findOne(new BasicDBObject("_id", idHandler.toDbId(id)), fields); } /** * Find an object by the given id * * @param id The id * @return The object * @throws MongoException If an error occurred */ public T findOneById(K id, T fields) throws MongoException { return findOneById(id, convertToDbObject(fields)); } /** * Returns a single object from this collection matching the query. * * @param query the query object * @return the object found, or <code>null</code> if no such object exists * @throws MongoException If an error occurred */ public T findOne(DBObject query) throws MongoException { return findOne(query, null); } /** * Returns a single object from this collection matching the query. * * @param query the query object * @return the object found, or <code>null</code> if no such object exists * @throws MongoException If an error occurred */ public T findOne(T query) throws MongoException { return findOne(query, null); } /** * Returns a single object from this collection matching the query. * * @param query the query object * @param fields the fields to return * @return the object found, or <code>null</code> if no such object exists */ public T findOne(DBObject query, DBObject fields) { return findOne(query, fields, getReadPreference()); } /** * Returns a single object from this collection matching the query. * * @param query the query object * @param fields an object for which every non null field will be returned * @return the object found, or <code>null</code> if no such object exists */ public T findOne(T query, T fields) { return findOne(convertToDbObject(query), convertToDbObject(fields)); } /** * Returns a single object from this collection matching the query. * * @param query the query object * @param fields fields to return * @param readPref The read preference * @return the object found, or <code>null</code> if no such object exists */ public T findOne(DBObject query, DBObject fields, ReadPreference readPref) { DBCursor<T> cursor = find(query, fields).setReadPreference(readPref); if (cursor.hasNext()) { return cursor.next(); } else { return null; } } /** * Returns a single object from this collection matching the query. * * @param query the query object * @param fields an object for which every non null field will be returned * @param readPref The read preferences * @return the object found, or <code>null</code> if no such object exists */ public T findOne(T query, T fields, ReadPreference readPref) { return findOne(convertToDbObject(query), convertToDbObject(fields), readPref); } /** * calls {@link DBCollection#save(com.mongodb.DBObject, com.mongodb.WriteConcern)} with default WriteConcern * * @param object the object to save * will add <code>_id</code> field to jo if needed * @return The result */ public final WriteResult<T, K> save(T object) { return save(object, getWriteConcern()); } /** * Saves an object to this collection (does insert or update based on the object _id). * * @param object the <code>DBObject</code> to save * @param concern the write concern * @return The result * @throws MongoException If an error occurred */ public WriteResult<T, K> save(T object, WriteConcern concern) throws MongoException { DBObject dbObject = convertToDbObject(object); return new WriteResult<T, K>(this, dbCollection.save(dbObject, concern), dbObject); } /** * Drops all indices from this collection * * @throws MongoException If an error occurred */ public void dropIndexes() throws MongoException { dropIndexes("*"); } /** * Drops an index from this collection * * @param name the index name * @throws MongoException If an error occurred */ public void dropIndexes(String name) throws MongoException { dbCollection.dropIndexes(name); } /** * Drops (deletes) this collection. Use with care. * * @throws MongoException If an error occurred */ public void drop() throws MongoException { dbCollection.drop(); } /** * returns the number of documents in this collection. * * @return The count * @throws MongoException If an error occurred */ public long count() throws MongoException { return getCount(new BasicDBObject(), null); } /** * returns the number of documents that match a query. * * @param query query to match * @return The count * @throws MongoException If an error occurred */ public long count(DBObject query) throws MongoException { return getCount(query, null); } /** * calls {@link DBCollection#getCount(com.mongodb.DBObject, com.mongodb.DBObject)} with an empty query and null fields. * * @return number of documents that match query * @throws MongoException If an error occurred */ public long getCount() throws MongoException { return getCount(new BasicDBObject(), null); } /** * calls {@link DBCollection#getCount(com.mongodb.DBObject, com.mongodb.DBObject)} with null fields. * * @param query query to match * @return The count * @throws MongoException If an error occurred */ public long getCount(DBObject query) throws MongoException { return getCount(query, null); } /** * calls {@link DBCollection#getCount(com.mongodb.DBObject, com.mongodb.DBObject)} with null fields. * * @param query query to match * @return The count * @throws MongoException If an error occurred */ public long getCount(T query) throws MongoException { return getCount(query, null); } /** * calls {@link DBCollection#getCount(com.mongodb.DBObject, com.mongodb.DBObject, long, long)} with limit=0 and skip=0 * * @param query query to match * @param fields fields to return * @return The count * @throws MongoException If an error occurred */ public long getCount(DBObject query, DBObject fields) throws MongoException { return getCount(query, fields, 0, 0); } /** * calls {@link DBCollection#getCount(com.mongodb.DBObject, com.mongodb.DBObject, long, long)} with limit=0 and skip=0 * * @param query query to match * @param fields fields to return * @return The count * @throws MongoException If an error occurred */ public long getCount(T query, T fields) throws MongoException { return getCount(query, fields, 0, 0); } /** * Returns the number of documents in the collection * that match the specified query * * @param query query to select documents to count * @param fields fields to return * @param limit limit the count to this value * @param skip number of entries to skip * @return number of documents that match query and fields * @throws MongoException If an error occurred */ public long getCount(DBObject query, DBObject fields, long limit, long skip) throws MongoException { return dbCollection.getCount(query, fields, limit, skip); } /** * Returns the number of documents in the collection * that match the specified query * * @param query query to select documents to count * @param fields fields to return * @param limit limit the count to this value * @param skip number of entries to skip * @return number of documents that match query and fields * @throws MongoException If an error occurred */ public long getCount(T query, T fields, long limit, long skip) throws MongoException { return getCount(convertToDbObject(query), convertToDbObject(fields), limit, skip); } /** * Calls {@link DBCollection#rename(java.lang.String, boolean)} with dropTarget=false * * @param newName new collection name (not a full namespace) * @return the new collection * @throws MongoException If an error occurred */ public JacksonDBCollection<T, K> rename(String newName) throws MongoException { return rename(newName, false); } /** * renames of this collection to newName * * @param newName new collection name (not a full namespace) * @param dropTarget if a collection with the new name exists, whether or not to drop it * @return the new collection * @throws MongoException If an error occurred */ public JacksonDBCollection<T, K> rename(String newName, boolean dropTarget) throws MongoException { return JacksonDBCollection.wrap(dbCollection.rename(newName, dropTarget), type, keyType, objectMapper); } /** * calls {@link DBCollection#group(com.mongodb.DBObject, com.mongodb.DBObject, com.mongodb.DBObject, java.lang.String, java.lang.String)} with finalize=null * * @param key - { a : true } * @param cond - optional condition on query * @param reduce javascript reduce function * @param initial initial value for first match on a key * @return The results * @throws MongoException If an error occurred * @see <a href="http://www.mongodb.org/display/DOCS/Aggregation">http://www.mongodb.org/display/DOCS/Aggregation</a> */ public DBObject group(DBObject key, DBObject cond, DBObject initial, String reduce) throws MongoException { return group(key, cond, initial, reduce, null); } /** * Applies a group operation * * @param key - { a : true } * @param cond - optional condition on query * @param reduce javascript reduce function * @param initial initial value for first match on a key * @param finalize An optional function that can operate on the result(s) of the reduce function. * @return The results * @throws MongoException If an error occurred * @see <a href="http://www.mongodb.org/display/DOCS/Aggregation">http://www.mongodb.org/display/DOCS/Aggregation</a> */ public DBObject group(DBObject key, DBObject cond, DBObject initial, String reduce, String finalize) throws MongoException { GroupCommand cmd = new GroupCommand(dbCollection, key, cond, initial, reduce, finalize); return group(cmd); } /** * Applies a group operation * * @param cmd the group command * @return The results * @throws MongoException * @see <a href="http://www.mongodb.org/display/DOCS/Aggregation">http://www.mongodb.org/display/DOCS/Aggregation</a> */ public DBObject group(GroupCommand cmd) { return dbCollection.group(cmd); } /** * find distinct values for a key * * @param key The key * @return The results */ public List distinct(String key) { return distinct(key, new BasicDBObject()); } /** * find distinct values for a key * * @param key The key * @param query query to match * @return The results */ public List distinct(String key, DBObject query) { return dbCollection.distinct(key, query); } /** * performs a map reduce operation * Runs the command in REPLACE output mode (saves to named collection) * * @param map map function in javascript code * @param outputTarget optional - leave null if want to use temp collection * @param reduce reduce function in javascript code * @param query to match * @return The output * @throws MongoException If an error occurred */ public MapReduceOutput mapReduce(String map, String reduce, String outputTarget, DBObject query) throws MongoException { return mapReduce(new MapReduceCommand(dbCollection, map, reduce, outputTarget, MapReduceCommand.OutputType.REPLACE, query)); } /** * performs a map reduce operation * Specify an outputType to control job execution * * INLINE - Return results inline * * REPLACE - Replace the output collection with the job output * * MERGE - Merge the job output with the existing contents of outputTarget * * REDUCE - Reduce the job output with the existing contents of * outputTarget * * @param map map function in javascript code * @param outputTarget optional - leave null if want to use temp collection * @param outputType set the type of job output * @param reduce reduce function in javascript code * @param query to match * @return The output * @throws MongoException If an error occurred */ public MapReduceOutput mapReduce(String map, String reduce, String outputTarget, MapReduceCommand.OutputType outputType, DBObject query) throws MongoException { return mapReduce(new MapReduceCommand(dbCollection, map, reduce, outputTarget, outputType, query)); } /** * performs a map reduce operation * * @param command object representing the parameters * @return The results * @throws MongoException If an error occurred */ public MapReduceOutput mapReduce(MapReduceCommand command) throws MongoException { return dbCollection.mapReduce(command); } /** * performs a map reduce operation * * @param command object representing the parameters * @return The output * @throws MongoException If an error occurred */ public MapReduceOutput mapReduce(DBObject command) throws MongoException { return dbCollection.mapReduce(command); } /** * Return a list of the indexes for this collection. Each object * in the list is the "info document" from MongoDB * * @return list of index documents */ public List<DBObject> getIndexInfo() { return dbCollection.getIndexInfo(); } /** * Drops an index from this collection * * @param keys keys of the index * @throws MongoException If an error occurred */ public void dropIndex(DBObject keys) throws MongoException { dbCollection.dropIndex(keys); } /** * Drops an index from this collection * * @param name name of index to drop * @throws MongoException If an error occurred */ public void dropIndex(String name) throws MongoException { dbCollection.dropIndex(name); } /** * gets the collections statistics ("collstats" command) * * @return the stats */ public CommandResult getStats() { return dbCollection.getStats(); } /** * returns whether or not this is a capped collection * * @return whether it is capped */ public boolean isCapped() { return dbCollection.isCapped(); } /** * Finds a collection that is prefixed with this collection's name. * A typical use of this might be * <blockquote><pre> * DBCollection users = mongo.getCollection( "wiki" ).getCollection( "users" ); * </pre></blockquote> * Which is equivalent to * <pre><blockquote> * DBCollection users = mongo.getCollection( "wiki.users" ); * </pre></blockquote> * * @param n the name of the collection to find * @param type The type of the collection * @return the matching collection */ public <S, L> JacksonDBCollection<S, L> getCollection(String n, Class<S> type, Class<L> keyType) { return wrap(getDB().getCollection(getName() + "." + n), type, keyType, objectMapper); } /** * Returns the name of this collection. * * @return the name of this collection */ public String getName() { return dbCollection.getName(); } /** * Returns the full name of this collection, with the database name as a prefix. * * @return the name of this collection */ public String getFullName() { return dbCollection.getFullName(); } /** * Returns the database this collection is a member of. * * @return this collection's database */ public DB getDB() { return dbCollection.getDB(); } @Override public int hashCode() { return dbCollection.hashCode(); } @Override public boolean equals(Object o) { return o == this; } @Override public String toString() { return dbCollection.toString(); } /** * Set the write concern for this collection. Will be used for * writes to this collection. Overrides any setting of write * concern at the DB level. See the documentation for * {@link WriteConcern} for more information. * * @param concern write concern to use */ public void setWriteConcern(WriteConcern concern) { dbCollection.setWriteConcern(concern); } /** * Get the write concern for this collection. * * @return THe write concern */ public WriteConcern getWriteConcern() { return dbCollection.getWriteConcern(); } /** * Sets the read preference for this collection. Will be used as default * for reads from this collection; overrides DB & Connection level settings. * See the * documentation for {@link ReadPreference} for more information. * * @param preference Read Preference to use */ public void setReadPreference(ReadPreference preference) { dbCollection.setReadPreference(preference); } /** * Gets the read preference * * @return The read preference */ public ReadPreference getReadPreference() { return dbCollection.getReadPreference(); } /** * adds a default query option * * @param option The option to add */ public void addOption(int option) { dbCollection.addOption(option); } /** * sets the default query options * * @param options The options */ public void setOptions(int options) { dbCollection.setOptions(options); } /** * resets the default query options */ public void resetOptions() { dbCollection.resetOptions(); } /** * gets the default query options * * @return The options */ public int getOptions() { return dbCollection.getOptions(); } JacksonDecoderFactory<T> getDecoderFactory() { return decoderFactory; } DBObject createIdQuery(K object) { return new BasicDBObjectBuilder().add("_id", idHandler.toDbId(object)).get(); } K convertFromDbId(Object object) { return idHandler.fromDbId(object); } DBObject convertToDbObject(T object) throws MongoException { if (object == null) { return null; } BsonObjectGenerator generator = new BsonObjectGenerator(); try { objectMapper.writeValue(generator, object); } catch (JsonMappingException e) { throw new MongoJsonMappingException(e); } catch (IOException e) { // This shouldn't happen throw new MongoException("Unknown error occurred converting BSON to object", e); } return generator.getDBObject(); } DBObject[] convertToDbObjects(T... objects) throws MongoException { // Yay for generic array creation DBObject[] results = new DBObject[objects.length]; for (int i = 0; i < objects.length; i++) { results[i] = convertToDbObject(objects[i]); } return results; } T convertFromDbObject(DBObject dbObject) throws MongoException { if (dbObject == null) { return null; } if (dbObject instanceof JacksonDBObject) { return (T) ((JacksonDBObject) dbObject).getObject(); } try { return objectMapper.readValue(new BsonObjectTraversingParser(dbObject), type); } catch (JsonMappingException e) { throw new MongoJsonMappingException(e); } catch (IOException e) { // This shouldn't happen throw new MongoException("Unknown error occurred converting BSON to object", e); } } @SuppressWarnings({ "unchecked" }) T[] convertFromDbObjects(DBObject... dbObjects) throws MongoException { T[] results = (T[]) new Object[dbObjects.length]; for (int i = 0; i < dbObjects.length; i++) { results[i] = convertFromDbObject(dbObjects[i]); } return results; } }