db.friends.find({age: {$gt: 18}})
Java Driver
friends.find(new BasicDBObject("age",new BasicDBObject("$gt",18)))Jongo
friends.find("{age: {$gt: 18}}").as(Friend.class)
Faithful spiritMongo query language isn't available in Java, Jongo fixes that. Copy/paste your queries to string.
Object orientedSave & find objects into & from collections. Use embedded Jackson marshalling or your own.
Wood solidAs fast as Mongo Java driver. Open source, fully tested & made of rock solid librairies.
Jongo relies upon Jackson, Bson4Jackson and Mongo Java Driver. Its Maven dependency, an OSGI compliant jar, comes with the first two, you have to provide a dependency to the driver.
Jongo is deployed into OSS Sonatype (Maven repository hosting service for open source
projects).
Add the following dependency to your pom.xml
<dependency> <groupId>org.jongo</groupId> <artifactId>jongo</artifactId> <version>[1.5.0,)</version> </dependency>
You can find the available versions at mvnrepository
First install Mongo, then choose a driver's version, finally download Jongo and you're all set.
DB db = new MongoClient().getDB("dbname"); Jongo jongo = new Jongo(db); MongoCollection friends = jongo.getCollection("friends"); MongoCursor<Friend> all = friends.find("{name: 'Joe'}").as(Friend.class); Friend one = friends.findOne("{name: 'Joe'}").as(Friend.class);
Passing an object to the save(..)
method will do the job. See mapping
section for objects defintion.
Friend joe = new Friend("Joe", 27); friends.save(joe); joe.age = 28; friends.save(joe);
Update syntax is a bit different than in Mongo shell: modifier
query has to be passed
using
with(..)
. You can update with a String
or with an object. You can also
parameterize queries with #
(using primitives or complex objects), see query
templating for more details.
friends.update("{name: 'Joe'}").with("{$inc: {age: 1}}"); friends.update("{name: 'Joe'}").upsert().multi().with("{$inc: {age: 1}}"); friends.update("{name: 'Joe'}").with(new Friend(..)); friends.update("{name: 'Joe'}").with("{$set: {address: #}}", new Address(..));
Insert works as in Mongo shell.
friends.insert("{name: 'Joe', age: 18}"); friends.insert(new Friend(..)); friends.insert(new Friend(..), new Friend(..));
Remove works as in Mongo shell.
friends.remove("{name: 'Joe'}"); friends.remove(new ObjectId("4c...e"));
Query results are automatically mapped to objects. By default, this relies on Jackson; it respects
document structure, handles lists and ignores missing attributes. It just needs a no-arg
constructor, even private (if the object has to be immutable, @JsonCreator
annotation can be used instead).
_id
is a unique identifier available on every Mongo document. If it isn't
setted,
it is generated. To handle it with Jongo, one attribute has to be named _id
or
annotated with @MongoId
(alias for @JsonProperty("_id")
). It can be
handled
with the dedicated ObjectId
class or with a simple String
— annotated
@MongoObjectId
.
Careful, saving a document with a custom _id
(ie: any Java type, other than array,
so
long as it is unique) always involves to set it manually before persisting.
public class Friend { // manual private long _id; }
public class Friend { // manual @MongoId private long key; }
public class Friend { // manual @MongoId private String key; }
public class Friend { // auto @MongoObjectId private String _id; }
public class Friend { @MongoId // auto @MongoObjectId private String key; }
public class Friend { // auto private ObjectId _id; }
JSON
properties are automatically mapped to their equivalent object attributes (and
complex ones mapped as sub-attributes). No extra annotations needed. To ignore
a
pojo attribute, annotate it with @JsonIgnore
.
For fine grained control over
(un)marshalling, configure Jongo mapper or create
your own.
To handle polymorphism of Java objects, a field containing the class name must be set into the Mongo
document. One can persist a Fox
(sub-class of Friend
) and
manipulate it
as a Friend
. This feature is provided out-of-the-box by Jackson. Using another
(un)marshaller implies extra work.
@JsonTypeInfo(use=Id.CLASS,property="_class") public class Friend { int age; }
public class Fox extends Friend { String name, color; }
friends.save(new Fox("fantastic", "red-haired")); Friend friend = friends.findOne("{name:'fantastic'}").as(Friend.class); Fox fantastic = (Fox)friend;
Using a singular reserved name, like _class
, for this field is a good
idea.
Query syntax is almost the same as in Mongo shell: copy/paste, it just works.
Strings have to be escaped with single quotes "{name: 'Joe'}"
, numbers don't "{age:
18}"
.
MongoCursor<Friend> all = friends.find("{name: 'Joe'}").as(Friend.class); Friend joe = friends.findOne("{name: 'Joe'}").as(Friend.class); Friend joe = friends.findOne(new ObjectId("4c...e")).as(Friend.class);
Field names need quotes when using dot notation "{'address.city': 'London'}"
.
Field selection aka. partial loading is not written as in Mongo shell:
Jongo exposes a projection(..)
method. A json selector must be provided: {field:
1}
to include it, {field: 0}
to exclude it.
friends.find().projection("{lastname: 1, address: 1}").as(Friend.class);
Documents can be sorted in ascending or descending order using a json selector: {field:
1}
for ascending, {field: -1}
for descending.
friends.findOne().orderBy("{firstname: 1}").as(Friend.class); friends.find().sort("{firstname: 1}").as(Friend.class); friends.find().sort("{lasttname: -1}").as(Friend.class);
Behaviour similar to Java driver DBCursor.
friends.find().skip(20).as(Friend.class); friends.find().limit(10).as(Friend.class);
Behaviour similar to Java driver Find.hint(..).
friends.find().hint("{age: 1}").as(Friend.class);
Query modifiers can be setted on DBCursor by implementing QueryModifier.
friends.find().with(new QueryModifier() { public void modify(DBCursor cursor) { cursor.batchSize(10); cursor.addSpecial("$maxScan", 1); } }).as(Friend.class);
You can close cursor manually by calling close()
method on MongoCursor
MongoCursor<Friend> cursor = friends.find().as(Friend.class); //iterate over it cursor.close();
As said in the mapping section, _id
identifier can be
manipulated
with a String
annotated with @MongoObjectId
. If you want to completly
avoid using
driver's ObjectId
, Jongo offers the Oid
class.
import static org.jongo.Oid.withOid; Friend joe = new Friend(); // @MongoObjectId String _id friends.save(joe); friends.find(withOid(joe.id)).as(Friend.class); // instead of new ObjectId(joe.id)
Almost all queries in Jongo can be templated: add anchors #
as follow. Binded
parameters
can be BSON
Primitives or any complex type made of those.
friends.find("{name:#, age:#}", "Joe", 18); //→ will produce {name: 'Joe', age: 18} friends.find("{address: #}", new Address(..)); //→ will marshall Address object
List<String> ages = Lists.newArrayList(22, 63); friends.find("{age: {$in:#}}", ages); //→ will produce {age: {$in:[22,63]}}
Projections can also be templated
friends.find().projection("{name:#",1).as(Friend.class); friends.find().projection("{family:{$elemMatch:{name:#}}}","Daddy").as(Friend.class);
Classic $regex: 'jo.*'
operator works in Jongo. It worth mentioning that Pattern.compile("jo.*")
also does. Unluckily, JavaScript syntax /jo*/g
is not available in Mongo Java Driver
and
Jongo cannot use it yet.
collection.findOne("{name: {$regex: #}}", "jo.*").as(Friend.class); collection.findOne("{name:#}", Pattern.compile("jo.*")).as(Friend.class);
Behaviour similar to Java driver DBCollection.count(..).
friends.count("{name: 'Joe'}");
Note that a count()
method is also available on MongoCursor, behaviour similar to DBCursor.count()
MongoCursor<Friend> cursor = friends.find().as(Friend.class); int nbResults = cursor.count();
Behaviour pretty similar to Java driver db.runCommand(..).
jongo.runCommand("{geoNear: 'friends', near: [48,9]}").as(GeoNearResult.class);
You can map command results to a single result field.
jongo.runCommand("{geoNear: 'friends', near : [48.690,9.140]}") .throwOnError() .field("results") .as(GeoNearResult.class);
Distinct syntax is almost the same as Find/FindOne operations. Optional query can be setted using
query(..)
method
List<String> names = friends.distinct("name").as(String.class);
List<Address> addresses = friends.distinct("address").query("{name: 'Joe'}").as(Address.class);
Careful, this feature is only available in the Mongo v2.2 release. All aggregation operators are
supported $project, $match, $limit, $skip, $unwind, $group, $sort
and can be piped
using
and(..)
method
collection.aggregate("{$project:{sender:1}}") .and("{$match:{tags:'read'}}") .and("{$limit:10}") .as(Email.class);
Using Mongo geospacial capacities requires an index and some BSON operators.
friends.ensureIndex("{address: '2d'}"); friends.find("{address: {$near: [0, 0], $maxDistance: 5}}").as(Address.class);
Read and write
behaviors can be configured on a query basis. ReadPreference
defines how multiple
instances are readed, WriteConcern
defines how writes are done.
friends.withWriteConcern(SAFE).withReadPreference(primaryPreferred()).find(..);
Mongo uses a behind the scenes
syntactic sugar for methods like sort(..)
, limit(..)
and others. The
complete expression can also be used in Jongo.
friends.find("{$query: {}, $maxScan: 2}").as(Friend.class); friends.find("{$query: {}, $orderby: {name: 1}}").as(Friend.class);
Jongo is lighting fast. Not because it is made of ancient wood and magic stones, it just binds Jackson — the fastest Java json (un)marshalling library — to Mongo Java driver with the slightest glue code possible.
Since 0.3, Jongo uses Mongo Java driver at a deeper level to enhance performance. Instead of
converting
results into strings and unmarshalling strings into objects, it works at byte[]
level (query
marhalling works the same since 1.0).
Several microbenchmarks (using Caliper & Yourkit) highlight this release net improvements: Jongo 0.3 is 6 times faster than Jongo 0.2, which make it as fast as the driver! Have a look at find microbenchmark and save microbenchmark, and at their implementations (find & save).
Not all benchmarks accurately reflect real world performance; more on microbenchmarks.
Jongo comes with a custom Jackson configuration that can be extended.
DB db = new Mongo().getDB("dbname"); Jongo jongo = new Jongo(db, new JacksonMapper.Builder() .registerModule(new JodaModule()) .enable(MapperFeature.AUTO_DETECT_GETTERS) .withView(Views.Public.class) .build() );
Jongo constructor comes with an optional parameter: Mapper
. This interface has a
provided
JacksonMapper
implementation which defines the defaut (un)marshalling behavior of
Jongo.
JacksonMapper.Builder
allows to alter this configuration as you see fit.
Default JacksonMapper.Builder()
comes with predefined features for the embedded Jackson
ObjectMapper
(see VisibilityModifier and PropertyModifier).
Those can be overridden and extended with enable(..)
and disable(..)
methods.
Jackson Module
is a set of (un)marshalling configuration. Hanlding Joda dates, for
example,
can be done with this Jackson
module and the registerModule(..)
method.
Jackson @JsonView
allows to filter an object attributes on (un)marshalling. Once Jongo
is
configured to use the Public
view (as in the previous configuration), the gender
attribute will be ignored even if it exists in Mongo.
Careful, when no view is defined, Jackson ignore annotations — which means every attribute is visible. Views can be used for marshalling-only or unmarshalling-only if needed.
public class Friend { private String _id; @JsonView(Views.Private.class) private String gender; } public class Views { public static class Public {} public static class Private extends Public {} }
Jongo uses the MapperModifier
interface to configure a JacksonMapper
.
Looking
at one implementation give a good feeling of how it can be used to further
configure a JacksonMapper
.
In certain circumstances, modifying Jackson ObjectReader
or ObjectWriter
will
be useful. JacksonMapper
offers one default ReaderCallback
and one WriterCalback
for that purpose, they can be replaced if needed.
For more options, refer to Jackson documentation.
Instead of Jackson, one can provide his own implementation of Mapper
.
public class GsonMapper extends Mapper { Marshaller getMarshaller() { ... } Unmarshaller getUnmarshaller() { ... } ObjectIdUpdater getObjectIdUpdater() { ... } QueryFactory getQueryFactory() { ... } } Jongo jongo = new Jongo(mongo.getDB("dbname"), new GsonMapper());
If one prefers to manually handle objects (ie. without unmarshaller), he can implements
ResultHandler
. Each result entity will be passed to the inherited handle(DBObject
result)
method.
MongoCursor<Integer> ages = friends.find("{name: 'Joe'}").map( new ResultHandler<Integer>() { @Override public Integer handle(DBObject result) { //do manual stuff here return result.get("age"); } } );
Because Jongo uses custom Jackson annotations @MongoId
and @MongoObjectId
,
your pojos can be exposed through Jersey (or Spring or any web services library) without any
conflicts.