MongoDB With Jongo - Sleeves Up!
Abstract : In this post you will find a brief introduction to Jongo - a fast, easy-to-use, Java-based querying library for MongoDB. There are plenty of articles discussing MongoDB around the Internet and it’s documentation is quite good. Thus, here you won’t find any introduction to MongoDB nor am I going to convince you to use it or not. In this article I’m going to show you that querying MongoDB from Java can be easy. Don’t be afraid from the size of the post, most of it is simple code and data samples.
Goal : Basic use of Jongo for querying MongoDB.
Acknowledgement : My gratitude goes to the open source community and especially to:
Benoit Guérout creator of Jongo
Yves Amsellem co-creator of Jongo
Xavier Bourguignon for his open-mind and open-heart
Code : You can download the full source of the project from GitHub
Prerequisite
You’re going to need a MongoDB instance running on your local machine and on the default port, if you want to run the code on GitHub. Thus, first you need to download MongoDB. Once you’ve done that, you would have to unzip what you’ve downloaded and go to the bin folder and execute the mongod command … and you are ready to go. Just for information: next to the mongod there is an executable called mongo which is the MongoDB shell and will provide you with access to any MongoDB database.
Sleeves up!
Let’s get started. First our domain model: we are going to use Game of Thrones as our starting point. For those of you who don’t know Game of Thrones, I can only say (… shame on you): get the books or the TV series (even better, get them both), relax and enjoy. In short, Game of Thrones is a series of fantasy novels by George R. R. Martin. And as in any novel there are some heroes and heroines. Now, I must warn you that there are some heroic creatures in Game of Thrones such as wolves and dragons but to keep the post simple, I’m going to leave there character modeling for later. Right now we are going to concentrate on humans:
package org.ingini.jongo.example.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.util.Set;
import static com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = Human.GENDER)
@JsonSubTypes({
@JsonSubTypes.Type(value = Heroine.class, name = Gender.Constants.FEMALE_VALUE),
@JsonSubTypes.Type(value = Hero.class, name = Gender.Constants.MALE_VALUE)
})
public abstract class Human {
public static final String FIRST_NAME = "first_name";
public static final String LAST_NAME = "last_name";
public static final String GENDER = "gender";
public static final String ADDRESS = "address";
public static final String CHILDREN = "children";
private final String firstName;
private final String lastName;
private final Gender gender;
private final Address address;
private final Set<? extends Human> children;
protected Human(String firstName, String lastName, Gender gender, Address address, Set<? extends Human> children) {
this.firstName = firstName;
this.lastName = lastName;
this.gender = gender;
this.address = address;
this.children = children;
}
@JsonProperty(FIRST_NAME)
public String getFirstName() {
return firstName;
}
@JsonProperty(LAST_NAME)
public String getLastName() {
return lastName;
}
@JsonProperty(GENDER)
public Gender getGender() {
return gender;
}
@JsonProperty(ADDRESS)
public Address getAddress() {
return address;
}
@JsonProperty(CHILDREN)
public Set<? extends Human> getChildren() {
return children;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Human human = (Human) o;
return new EqualsBuilder().append(this.firstName, human.firstName).append(this.lastName, human.lastName)
.append(this.gender, human.gender).append(this.address, this.address).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(7, 61).append(firstName).append(lastName).append(gender).append(address).toHashCode();
}
}
For simplicity, we are going to consider that a human can be of a female or male gender:
package org.ingini.jongo.example.model;
import static org.ingini.jongo.example.model.Gender.Constants.*;
public enum Gender {
MALE(MALE_VALUE), FEMALE(FEMALE_VALUE);
Gender(String genderString) {
}
public static class Constants {
public static final String MALE_VALUE = "MALE";
public static final String FEMALE_VALUE = "FEMALE";
}
}
Thus we can have a human hierarchy based on gender:
package org.ingini.jongo.example.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Set;
import static org.ingini.jongo.example.model.Gender.MALE;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Hero extends Human {
@JsonCreator
public Hero(@JsonProperty(FIRST_NAME) String firstName,
@JsonProperty(LAST_NAME) String lastName,
@JsonProperty(ADDRESS) Address address,
@JsonProperty(CHILDREN) Set<Human> children) {
super(firstName, lastName, MALE, address, children);
}
public static Hero createHeroWithoutChildren(String firstName, String lastName, Address address) {
return new Hero(firstName, lastName, address, null);
}
}
… and a heroine:
package org.ingini.jongo.example.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Set;
import static org.ingini.jongo.example.model.Gender.FEMALE;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Heroine extends Human {
@JsonCreator
public Heroine(@JsonProperty(FIRST_NAME) String firstName,
@JsonProperty(LAST_NAME) String lastName,
@JsonProperty(ADDRESS) Address address,
@JsonProperty(CHILDREN) Set<Human> children) {
super(firstName, lastName, FEMALE, address, children);
}
public static Heroine createHeroineWithoutChildren(String firstName, String lastName, Address address) {
return new Heroine(firstName, lastName, address, null);
}
}
As you can see from our Human class, every human has an address:
package org.ingini.jongo.example.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Address {
public static final String CASTLE = "castle";
public static final String CONTINENT = "continent";
public static final String REGION = "region";
private final String castle;
private final String continent;
private final Region region;
@JsonCreator
public Address(@JsonProperty(CASTLE) String castle,
@JsonProperty(CONTINENT) String continent,
@JsonProperty(REGION) Region region) {
this.castle = castle;
this.continent = continent;
this.region = region;
}
@JsonProperty(CASTLE)
public String getCastle() {
return castle;
}
@JsonProperty(CONTINENT)
public String getContinent() {
return continent;
}
@JsonProperty(REGION)
public Region getRegion() {
return region;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return new EqualsBuilder()
.append(this.castle, address.castle)
.append(this.continent, address.continent)
.append(this.region, this.region).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(11, 43).append(castle).append(continent).append(region).toHashCode();
}
}
The one thing left is the realm’s regions. In case you still don’t know what the realm’s regions are : go back to the novels (or TV series) or just click here. I’ve used an enum to represent the regions but I’ve put only some of them:
package org.ingini.jongo.example.model;
public enum Region {
WESTEROS, ESSOS, DORNE, IRON_ISLANDS, THE_CROWNLANDS, THE_RIVERLANDS, THE_VALE_OF_ARRYN, THE_NORTH, THE_REACH
}
CRUD - the Jongo way
Creating/Inserting/Saving
Let’s start by creating a very important heroine called Arya Stark:
Jongo jongo = new Jongo(mongoDB);
MongoCollection heroes = jongo.getCollection("heroes");
Heroine aryaStark = Heroine.createHeroineWithoutChildren("Arya", "Stark", //
new Address("Winterfell", "Westeros", Region.THE_NORTH));
heroes.insert(aryaStark);
… and here is the MongoDB document:
{
"_id" : ObjectId("51865d8c21622e48542ea6ac"),
"gender" : "FEMALE",
"first_name" : "Arya",
"last_name" : "Stark",
"address" : {
"castle" : "Winterfell",
"continent" : "Westeros",
"region" : "THE_NORTH"
},
"gender" : "FEMALE"
}
Let’s also create and insert the father of Arya Stark - Eddard Stark:
Jongo jongo = new Jongo(mongoDB);
MongoCollection heroes = jongo.getCollection("heroes");
Address castleWinterfell = new Address("Winterfell", "Westeros", Region.THE_NORTH);
Set<Human> children = Sets.newHashSet();
children.add(Hero.createHeroWithoutChildren("Robb", "Stark", castleWinterfell));
children.add(Heroine.createHeroineWithoutChildren("Sansa", "Stark", castleWinterfell));
children.add(Heroine.createHeroineWithoutChildren("Arya", "Stark", castleWinterfell));
children.add(Hero.createHeroWithoutChildren("Bran", "Stark", castleWinterfell));
children.add(Hero.createHeroWithoutChildren("Rickon", "Stark", castleWinterfell));
children.add(Hero.createHeroWithoutChildren("Jon", "Snow", castleWinterfell));
Hero eddardStark = new Hero("Eddard", "Stark", castleWinterfell, children);
heroes.insert(eddardStark);
What I’ve done is a good example of a composite, tree structure. I have a main node document (Eddard Stark) and his children/leaves (Robb, Sansa, Arya, Bran, Rickon, Jon). For those of you who are new to the world of MongoDB, I hope you can easily see the power you’ve been given:
{
"_id": "5186a0da21622e48542ea6af",
"gender": "MALE",
"first_name": "Eddard",
"last_name": "Stark",
"address": {
"castle": "Winterfell",
"continent": "Westeros",
"region": "THE_NORTH"
},
"children": [
{
"gender": "MALE",
"first_name": "Bran",
"last_name": "Stark",
"address": {
"castle": "Winterfell",
"continent": "Westeros",
"region": "THE_NORTH"
},
"gender": "MALE"
},
{
"gender": "FEMALE",
"first_name": "Sansa",
"last_name": "Stark",
"address": {
"castle": "Winterfell",
"continent": "Westeros",
"region": "THE_NORTH"
},
"gender": "FEMALE"
},
{
"gender": "MALE",
"first_name": "Rickon",
"last_name": "Stark",
"address": {
"castle": "Winterfell",
"continent": "Westeros",
"region": "THE_NORTH"
},
"gender": "MALE"
},
{
"gender": "FEMALE",
"first_name": "Arya",
"last_name": "Stark",
"address": {
"castle": "Winterfell",
"continent": "Westeros",
"region": "THE_NORTH"
},
"gender": "FEMALE"
},
{
"gender": "MALE",
"first_name": "Jon",
"last_name": "Snow",
"address": {
"castle": "Winterfell",
"continent": "Westeros",
"region": "THE_NORTH"
},
"gender": "MALE"
},
{
"gender": "MALE",
"first_name": "Robb",
"last_name": "Stark",
"address": {
"castle": "Winterfell",
"continent": "Westeros",
"region": "THE_NORTH"
},
"gender": "MALE"
}
],
"gender": "MALE"
}
Note that up to now I’ve used the automatically generated _id by MongoDB which are ‘most likely unique’ (according to the docs ObjectId). I invite you to take a look at the docs and see for yourself if you are happy with the ‘most likely unique’ statement (personally, I’m satisfied). Of course, in certain scenarios you may have a natural unique identifier or you might want to generate the _id manually. Either way, Jongo is here for you and gives you the means to do it. In order to show you how, I will create a new collection called weapons which will hold different weapons belonging to the heroes and heroines in Game of Thrones. Here is our sword class:
package org.ingini.jongo.example.model.weapons;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.jongo.marshall.jackson.oid.Id;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Sword {
public static final String ID = "_id";
public static final String MATERIAL = "material";
@Id
private final String _id;
private final String material;
@JsonCreator
public Sword(@JsonProperty(ID) String _id, @JsonProperty(MATERIAL) String material) {
this._id = _id;
this.material = material;
}
@JsonProperty(ID)
public String getId() {
return _id;
}
@JsonProperty(MATERIAL)
public String getMaterial() {
return material;
}
}
… and here is how you can create and insert one such document:
Jongo jongo = new Jongo(mongoDB);
MongoCollection weapons = jongo.getCollection("weapons");
Sword lightbringer = new Sword("Lightbringer", null);
weapons.save(lightbringer);
… and here is what the MongoDB document looks like after being inserted:
{ "_id" : "Lightbringer" }
Note however, that I’ve used the .save() operation instead of .insert() that’s because there is a bug in the version 0.4 of Jongo which will be fixed in version 0.5.
Reading
Now let’s do some reading. Just as in the mongo shell, Jongo gives you the ability to read in two ways with find() and findOne(). Let’s do some reading of our Human’s hierarchy:
Hero hero = heroes.findOne("{_id : {$oid: #}}", "5186a0da21622e48542ea6af").as(Hero.class);
In the preceding query, I know that the target _id (“5186a0da21622e48542ea6af”) belongs to a male hero (Eddard Stark). Thus I can take a full advantage of Jongo and use .as(Hero.class). Note, that I’ve used the $oid operator. Instead, however, I can use an ObjectId (org.bson.types.ObjectId):
Hero hero = heroes.findOne(new ObjectId("5186a0da21622e48542ea6af")).as(Hero.class);
Of course, when you are not sure what is the exact type of the result you can always back-off to the parent class. In our case that would be using .as(Human.class) instead of .as(Hero.class), since the Human.class is the parent of both Hero and Heroine classes.
You can query using multiple parameters:
Heroine heroine = heroes.findOne("{" + Human.GENDER + ": #, " + Human.FIRST_NAME + ": #}",
Gender.FEMALE, "Arya").as(Heroine.class);
Did you notice how you can query using an enum and string parameters in the same way? Jongo does all the string escaping for you.
Now, let’s have somewhat more complex querying example. What if I want to get only one child of Eddard Stark (that is one element of the children array), say Sansa Stark. Here is how we can do that:
Heroine heroine = heroes.findOne("{_id : {$oid: #}}", "5186a0da21622e48542ea6af")
.projection("{children: {$elemMatch: {" + Human.FIRST_NAME + ": #, " +
Human.LAST_NAME + ": #}}}", "Sansa", "Stark")
.map(new ResultHandler<Heroine>() {
@Override
public Heroine map(DBObject result) {
LazyDBList o = (LazyDBList) result.get(Human.CHILDREN);
DBObject basicDbObject = (DBObject) o.get(0);
ObjectMapper objectMapper = new ObjectMapper();
String content = basicDbObject.toString();
try {
return objectMapper.readValue(content, Heroine.class);
} catch (IOException e) {
throw new IllegalStateException("Unable to deserialize " + content);
}
}
});
I’ve used the $elemMatch operator to project only the array element matching the first name and last name of Sansa Stark. In addition, I’ve took a full advantage of Jongos’s ResultHandler to build a custom mapping functionality.
Finding documents based on a regular expression is also supported. Let’s assume we have the following documents in the weapons collection:
{ "_id" : "Lightbringer" }
{ "_id" : "Longclaw", "material" : "Valyrian steel" }
{ "_id" : "Dark Sister", "material" : "Valyrian steel" }
{ "_id" : "Ice", "material" : "Valyrian steel" }
If you want to find all swords made of steel, you have two options. You can either use the $regex operator, like this:
weapons.find("{" + Sword.MATERIAL + ": {$regex: #}}", "steel.*").as(Sword.class)
… or you can go for Pattern.compile(…) :
weapons.find("{" + Sword.MATERIAL + ": #}", Pattern.compile("steel.*")).as(Sword.class)
Personally, I like using the # sing to keep the query logic separated from the actual data state of the query parameters. This adds to the readability of your code and makes refactoring easier.
Updating
Shall we update something then? How about adding a details field to the Lightbringer document which, as you might already guessed, will hold some details about the Lightbringer sword. The details will be represented by the following WeaponDetails class:
package org.ingini.jongo.example.model.weapons;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.builder.ToStringBuilder;
@JsonIgnoreProperties(ignoreUnknown = true)
public class WeaponDetails {
public static final String PROPHECY = "prophecy";
public static final String CREATOR = "creator";
private final String prophecy;
private final String creator;
@JsonCreator
public WeaponDetails( @JsonProperty(PROPHECY) String prophecy,
@JsonProperty(CREATOR) String creator) {
this.prophecy = prophecy;
this.creator = creator;
}
@JsonProperty(PROPHECY)
public String getProphecy() {
return prophecy;
}
@JsonProperty(CREATOR)
public String getCreator() {
return creator;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.appendSuper(super.toString())
.append("prophecy", prophecy)
.append("creator", creator)
.toString();
}
}
… and here is how you do it:
WeaponDetails details = new WeaponDetails("The one who pulls out this sword from fire will be named Lord's Chosen ...", "Azor Ahai");
weapons.update("{_id: #}", "Lightbringer").with("{$set: {details: #}}", details);
Now the Lightbringer MongoDB document looks like this:
{ "_id" : "Lightbringer", "details" : { "prophecy" : "The one who pulls out this sword from fire will be named Lord's Chosen ...", "creator" : "Azor Ahai" } }
Right, everything looks fine. Or does it? I’ve updated the MongoDB document, but I haven’t change the Sword.java. Meaning that if I do:
Sword lightbringer = weapons.findOne("{_id: 'Lightbringer'}").as(Sword.class);
I will have the lightbringer sword alright but it won’t have the details property. Nevertheless, adding a property to a MongoDB didn’t result in some exception. This shows two things:
If you do forget to change your Java classes, well, you won’t be able to get what you have in the database but this won’t cause your application to crash
You can implement multiple views/representations (i.e. by having multiple Java classes) over the same MongoDB document. A really nice description of what you can do with these multiple views/representations is given by Martin Fowler in his bliki about Command Query Responsibility Segregation Pattern
As for our Sword.java we can change it to this:
package org.ingini.jongo.example.model.weapons;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.jongo.marshall.jackson.oid.Id;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Sword {
public static final String ID = "_id";
public static final String MATERIAL = "material";
public static final String DETAILS = "details";
@Id
private final String _id;
private final String material;
private final WeaponDetails details;
@JsonCreator
public Sword( @JsonProperty(ID) String _id,
@JsonProperty(MATERIAL) String material,
@JsonProperty(DETAILS) WeaponDetails details) {
this._id = _id;
this.material = material;
this.details = details;
}
@JsonProperty(ID)
public String getId() {
return _id;
}
@JsonProperty(MATERIAL)
public String getMaterial() {
return material;
}
@JsonProperty(DETAILS)
public WeaponDetails getDetails() {
return details;
}
}
… and now we can get all the info in the Java class.
Let’s see how we can tackle the problem of adding a field to a Java class and updating the MongoDB document. As I mentioned earlier, there are some non-human heroes. The most famous ones are the six orphaned dire wolf cubs found in the Wolfswood which were adopted by the children of House of Stark and the three dragons of Daenerys Targaryen. For the moment we will take a closer look at the dire wolves. But what’s common between the dire wolves and the dragons is that they are beasts. Thus, let’s start with the Beast class:
package org.ingini.jongo.example.model.beasts;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = Beast.KIND)
@JsonSubTypes({
@JsonSubTypes.Type(value = DireWolf.class, name = Kind.Constants.DIRE_WOLF)
})
public abstract class Beast {
public static final String KIND = "kind";
public static final String NAME = "name";
private final Kind kind;
private final String name;
protected Beast(Kind kind, String name) {
this.kind = kind;
this.name = name;
}
@JsonProperty(KIND)
public Kind getKind() {
return kind;
}
@JsonProperty(NAME)
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Beast beast = (Beast) o;
return new EqualsBuilder().append(this.kind, beast.kind).append(this.name, beast.name).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(29, 67).append(kind).append(name).toHashCode();
}
@Override
public String toString() {
return new ToStringBuilder(this)
.appendSuper(super.toString())
.append("kind", kind)
.append("name", name)
.toString();
}
}
Each beast has its kind:
package org.ingini.jongo.example.model.beasts;
public enum Kind {
DIRE_WOLF(Constants.DIRE_WOLF), DRAGON(Constants.DRAGON);
Kind(String dragon) {
}
public static class Constants {
public static final String DIRE_WOLF = "DIRE_WOLF";
public static final String DRAGON = "DRAGON";
}
}
Now let’s create our DireWolf:
package org.ingini.jongo.example.model.beasts;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
public class DireWolf extends Beast {
@JsonCreator
public DireWolf(@JsonProperty(NAME) String name) {
super(Kind.DIRE_WOLF, name);
}
}
So far so good, what’s left is to add a beasts property of type Set<? extends Beast> in the Human Java class. I’m not going to list the entire Human.class but you can find the code on GitHub. Here is what I did to add a dire wolf to each of the Eddard Stark’s children:
Hero eddardStark = heroes.findOne(new ObjectId("5186a0da21622e48542ea6af")).as(Hero.class);
Set<Human> updatedChildren = // here goes some code to update each child
Hero updatedEddardStark = Hero.updateChildren(eddardStark, updatedChildren);
heroes.save(updatedEddardStark);
The most interesting part is the highlighted line 4. Now, make sure the updated object instance (in our case that’s updatedEddardStark) contains the _id property of the original document otherwise a new MongoDB document will be inserted. In the code above, I’ve used a updateChildren factory method which looks like this:
public static Hero updateChildren(Hero hero, Set<? extends Human> children) {
return new Hero(hero.getId(), hero.getFirstName(), hero.getLastName(),
hero.getAddress(), children, hero.getBeasts());
}
Deleting
A word of advice: reconsider several times before deleting.
Ok, so you’ve reached the point of saturation of your hard drive and you don’t have money for a larger one … or (which is more likely) you’ve erred something and now you have to delete it.
If you want to delete a single document of which you know the identifier, you can do it like this:
heroes.remove(new ObjectId("5186a0da21622e48542ea6af"));
What if you want to remove a field from a document? Well, that’s actually unset-ting it, here is how:
First, let’s say we have the document:
{
"_id": "5186a8a221622e48542ea6b0",
"gender": "FEMALE",
"first_name": "Arya",
"last_name": "Stark",
"address": {
"castle": "Winterfell",
"continent": "Westeros",
"region": "THE_NORTH"
}
}
Just to make it a bit more interesting we are going to delete the nested address field castle (highlighted on line 7) by doing the following:
heroes.update(new ObjectId("5186a8a221622e48542ea6b0")).with("{$unset: {" + Human.ADDRESS + "." + Address.CASTLE + ": 1}}");
Final note
Jongo and MongoDB are really great tools to solve some of our every day problems and my only hope is that this post brought some light on how you can do that. In conclusion to everything already said, I would like to add that although Jongo is young it has a great potential of becoming a very popular tool. That’s mainly because it is easy to solve problems with it. And when I say “it is easy”, I mean that you can write more understandable / readable code and create elegant solutions avoiding unnecessary complexity.
If you want to learn more about Jongo then go to its home page http://jongo.org/. The documentation is great and both Benoit and Yves are open to discussions and new ideas, so don’t hesitate to give Jongo a try and share your opinion. If you want to learn even more - write me a comment explaining what you are interested in and I promise to write another post about Jongo and your problem.