How to have custom mapper keys in your documents with Jongo

Posted: décembre 31st, 2013 | Author: | Filed under: Dev, Java, Jongo, MongoDB, NoSQL, Tutorial | No Comments »

I’m giving a try to Jongo to replace spring-data-mongodb in a project but I have to manage documents with ‘not really java compatible keys’.
For example, a key with a first upper case letter and some ‘-’ inside … (well ok that’s ugly but I can’t change that now).

In spring-data, I’m using MappingMongoConverter, it works but Jongo seems to be easier to use and deploy than spring data.

And yes, it’s very simple to customize the way Jongo manages your beans !
The reason is that Jongo uses Jackson Mapper (by default but you could use your own or favorite one).
So, you just have to create a PropertyNamingStrategy !!!

1 – Create a NamingStrategy

With this naming strategy, we just search in a map if there’s a replacement value for the provided key.

package com.ezakus.data.jongo.customs;

import static com.fasterxml.jackson.databind.PropertyNamingStrategy.PropertyNamingStrategyBase;

import java.util.Map;

public class MapTransformNamingStrategy extends PropertyNamingStrategyBase{

    private static final long serialVersionUID = 1L;

    private Map<String, String> mapping;

    public MapTransformNamingStrategy(Map<String, String> mapping) {
        this.mapping = mapping;
    }

    @Override
    public String translate(String property) {
        if (mapping.containsKey(property)) {
            return mapping.get(property);
        }

        return property;
    }
}

2 – Use it with Jongo

As you could see in the Jongo documentation :

Jongo comes with a custom Jackson configuration that can be extended.

So you just have to use addModifier and a MapperModifier implementation to set your naming strategy to the mapper.

package com.ezakus.data.jongo.customs;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.jongo.marshall.jackson.configuration.MapperModifier;

import java.util.HashMap;
import java.util.Map;

public class CustomMapperModifier implements MapperModifier{
    Map<String, String> mapping = new HashMap<String, String>();

    public CustomMapperModifier(){
        mapping.put("myProperty", "custom-key-for-my-property");
    }

    @Override
    public void modify(ObjectMapper objectMapper) {
        objectMapper.setPropertyNamingStrategy(new MapTransformNamingStrategy(mapping));
    }
}
Jongo jongo = new Jongo(db, new JacksonMapper.Builder().addModifier(new CustomMapperModifier()).build());

3 – Full example

Here is a DummyObject class to test :

package com.ezakus.data.jongo.document;

import org.bson.types.ObjectId;

public class DummyObject {

    private ObjectId id;
    private String myProperty;

    public ObjectId getId() {
        return id;
    }

    public void setId(ObjectId id) {
        this.id = id;
    }

    public String getMyProperty() {
        return myProperty;
    }

    public void setMyProperty(String myProperty) {
        this.myProperty = myProperty;
    }
}

And a unit test (I’m using an embedded mongo for the test)

package com.ezakus.data.jongo.document;

import com.ezakus.data.jongo.customs.CustomMapperModifier;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import de.flapdoodle.embed.mongo.MongodExecutable;
import de.flapdoodle.embed.mongo.MongodProcess;
import de.flapdoodle.embed.mongo.MongodStarter;
import de.flapdoodle.embed.mongo.config.MongodConfig;
import de.flapdoodle.embed.mongo.distribution.Version;
import de.flapdoodle.embed.process.runtime.Network;
import org.jongo.Jongo;
import org.jongo.MongoCollection;
import org.jongo.marshall.jackson.JacksonMapper;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import java.net.UnknownHostException;

import static org.fest.assertions.Assertions.assertThat;

public class NamingStrategyTest {

    public static MongodExecutable mongodExe;
    public static MongodProcess    mongod;

    @BeforeClass
    public static void runBeforeAllTests() throws Exception {
        MongodStarter runtime = MongodStarter.getDefaultInstance();
        NamingStrategyTest.mongodExe = runtime.prepare(new MongodConfig(Version.Main.V2_0, 12345, Network.localhostIsIPv6()));
        NamingStrategyTest.mongod = NamingStrategyTest.mongodExe.start();
    }

    @AfterClass
    public static void runAfterAllTests() {
        NamingStrategyTest.mongod.stop();
        NamingStrategyTest.mongodExe.cleanup();
    }

    @Test
    public void testNaming(){
        DB db = null;
        try {
            db = new MongoClient("localhost", 12345).getDB("myDB");

            Jongo jongo = new Jongo(db, new JacksonMapper.Builder().addModifier(new CustomMapperModifier()).build());

            MongoCollection dum = jongo.getCollection("dummies");
            assertThat(dum.count()).isEqualTo(0);

            DummyObject d = new DummyObject();
            String prop = "where is brian ?";
            d.setMyProperty(prop);

            dum.save(d);
            assertThat(dum.count()).isEqualTo(1);

            DummyObject check = dum.findOne("{custom-key-for-my-property: '"+prop+"'}").as(DummyObject.class);
            assertThat(check.getId()).isEqualTo(d.getId());
            assertThat(check.getMyProperty()).isEqualTo(prop);

        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

}