MongoDB et Java en pratique




  MongoFR   23/03/2011
Speaker



   Katia Aresti, Xebia
   Développeur Java : 5 ans d'expérience
   MongoDB : 6 mois



                        @karesti
                         blog.xebia.fr

                         jduchess.org/duchess-france




                                                       2
Agenda




         3
Agenda

 Pourquoi Mongo DB




                      3
Agenda

 Pourquoi Mongo DB
 Configuration et connexion




                               3
Agenda

 Pourquoi Mongo DB
 Configuration et connexion
 Objet-Mongo Mapper




                               3
Agenda

 Pourquoi Mongo DB
 Configuration et connexion
 Objet-Mongo Mapper
 Recherche textuelle




                               3
Agenda

 Pourquoi Mongo DB
 Configuration et connexion
 Objet-Mongo Mapper
 Recherche textuelle
 Recherche spatiale




                               3
Agenda

 Pourquoi Mongo DB
 Configuration et connexion
 Objet-Mongo Mapper
 Recherche textuelle
 Recherche spatiale
 Indexation




                               3
Agenda

 Pourquoi Mongo DB
 Configuration et connexion
 Objet-Mongo Mapper
 Recherche textuelle
 Recherche spatiale
 Indexation
 Conclusions




                               3
Agenda

 Pourquoi Mongo DB
 Configuration et connexion
 Objet-Mongo Mapper
 Recherche textuelle
 Recherche spatiale
 Indexation
 Conclusions
 Questions




                               3
Notre système (I)

                                                              Recherche




            Paris



            http://www.parisnet.com/images/parismap_new.gif
                                                                          4
Notre système (I)

                                                              Recherche




            Paris



            http://www.parisnet.com/images/parismap_new.gif
                                                                          4
Notre système (I)

                                                              Recherche
                                                              Bistro




            Paris



            http://www.parisnet.com/images/parismap_new.gif
                                                                          4
Notre système (I)

                                                              Recherche
                                                              Bistro

                                                              Bistro du Bastille

                                                              Bistro de Mer

                                                              Mon grand Bistro

                                                              MongoDB bistro

                                                              Boulanger Bistro

                                                              Jean Bistro votre Plombier

                                                              ...




            Paris



            http://www.parisnet.com/images/parismap_new.gif
                                                                                           4
Notre système (II)


                                                            POI : Point of Interest




    TOUTE LA FRANCE !!!


         http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif
                                                                                                              5
Notre système (II)


                                                            POI : Point of Interest

                                                               Monuments




    TOUTE LA FRANCE !!!


         http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif
                                                                                                              5
Notre système (II)


                                                            POI : Point of Interest

                                                               Monuments
                                                               Mairies




    TOUTE LA FRANCE !!!


         http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif
                                                                                                              5
Notre système (II)


                                                            POI : Point of Interest

                                                               Monuments
                                                               Mairies
                                                               Médecins




    TOUTE LA FRANCE !!!


         http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif
                                                                                                              5
Notre système (II)


                                                            POI : Point of Interest

                                                               Monuments
                                                               Mairies
                                                               Médecins
                                                               Restaurants




    TOUTE LA FRANCE !!!


         http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif
                                                                                                              5
Notre système (II)


                                                            POI : Point of Interest

                                                               Monuments
                                                               Mairies
                                                               Médecins
                                                               Restaurants
                                                               ...




    TOUTE LA FRANCE !!!


         http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif
                                                                                                              5
Notre système (II)


                                                            POI : Point of Interest

                                                               Monuments
                                                               Mairies
                                                               Médecins
                                                               Restaurants
                                                               ...

                                                               mais aussi le mode contributif
                                                               pour ajouter mes lieux
                                                               personnels ...




    TOUTE LA FRANCE !!!


         http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif
                                                                                                              5
Notre système (III)

En bref :




                      6
Notre système (III)

 En bref :
 Modèle de données très hétérogène




                                      6
Notre système (III)

 En bref :
 Modèle de données très hétérogène
 Relations simples (1 - 1) et absence de
 transactions complexes




                                            6
Notre système (III)

 En bref :
 Modèle de données très hétérogène
 Relations simples (1 - 1) et absence de
  transactions complexes
 Recherche par mot-clé (full-text à venir)




                                              6
Notre système (III)

 En bref :
 Modèle de données très hétérogène
 Relations simples (1 - 1) et absence de
  transactions complexes
 Recherche par mot-clé (full-text à venir)
 Recherche spatiale




                                              6
Notre système (III)

 En bref :
 Modèle de données très hétérogène
 Relations simples (1 - 1) et absence de
  transactions complexes
 Recherche par mot-clé (full-text à venir)
 Recherche spatiale
 Fort trafic et performance en temps réel




                                              6
Notre système (III)

 En bref :
 Modèle de données très hétérogène
 Relations simples (1 - 1) et absence de
  transactions complexes
 Recherche par mot-clé (full-text à venir)
 Recherche spatiale
 Fort trafic et performance en temps réel
 Scalabilité horizontale




                                              6
Notre système (III)

 En bref :
 Modèle de données très hétérogène
 Relations simples (1 - 1) et absence de
  transactions complexes
 Recherche par mot-clé (full-text à venir)
 Recherche spatiale
 Fort trafic et performance en temps réel
 Scalabilité horizontale
 Délais très courts de livraison



                                              6
Notre système (III)

 En bref :
 Modèle de données très hétérogène
 Relations simples (1 - 1) et absence de
  transactions complexes
 Recherche par mot-clé (full-text à venir)
 Recherche spatiale
 Fort trafic et performance en temps réel
 Scalabilité horizontale
 Délais très courts de livraison
 Equipe Java


                                              6
Notre système (IV)




         HTTP          Java Driver



                                                       ...
                 REST             BD de POI
                Services                                       XML
                                                               CSV
                                                               SOAP
                                                               REST
                                                     Sources   ...




                       http://www.openclipart.org/
                                                                      7
Geoentity Java


 public class Geoentity {                         public class Message {


     private String id;                               private String id;


     private long updated;                            private String text;


     private String name;
                                       1 .. N         private String userId;


     private String userId;                           private long created;
                                                  }
     private Set keywords;

     private List<Message> messages;              public class Location {
                                         1 .. 1
     private Location location;                       private double lat;
 }
                                                      private double lng;
                                                  }




                                                                               8
Geoentity Java


 public class Geoentity {                         public class Message {


     private String id;                               private String id;


     private long updated;                            private String text;


     private String name;
                                       1 .. N         private String userId;


     private String userId;                           private long created;
                                                  }
     private Set keywords;

     private List<Message> messages;              public class Location {
                                         1 .. 1
     private Location location;                       private double lat;
 }
                                                      private double lng;
                                                  }



                            Une collection : «places»
                                                                               8
Configurer la connexion (I)

 Maven


          <dependency>
          	   <groupId>org.mongodb</groupId>
          	   <artifactId>mongo-java-driver</artifactId>
              <version>2.5</version>
          </dependency>




                                                           9
Configurer la connexion (I)

 Maven


          <dependency>
          	   <groupId>org.mongodb</groupId>
          	   <artifactId>mongo-java-driver</artifactId>
              <version>2.5</version>
          </dependency>




          Suivez le projet sur GitHub !



                                                           9
Configurer la connexion (II)

 IoC : Guice
   @Singleton
   public class MongoDB {

       private Mongo mongo;
       private DB db;

       @Inject
       public MongoDB(String host, int port, int nb) {

         MongoOptions mongoOpts = new MongoOptions();
         mongoOptions.connectionsPerHost = nb;

         try {
                mongo = new Mongo(new ServerAddress(host, port), mongoOpts);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        db = mongo.getDB(DATABASE_NAME);
        }
   }



                                                                               10
Configurer la connexion (II)

 IoC : Guice
   @Singleton
   public class MongoDB {

       private Mongo mongo;
       private DB db;

       @Inject                                                          Attention !
       public MongoDB(String host, int port, int nb) {

         MongoOptions mongoOpts = new MongoOptions();
         mongoOptions.connectionsPerHost = nb;

         try {
                mongo = new Mongo(new ServerAddress(host, port), mongoOpts);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        db = mongo.getDB(DATABASE_NAME);
        }
   }



                                                                                  10
Objet - BSON (I)

 API driver Java (notre choix initial)

      public DBObject toBasicDBObject() {
      	      BasicDBObject dbObject = new BasicDBObject();
      	      if (this.id != null && ObjectId.isValid(this.id)) {
      	           dbObject.put("_id", new ObjectId(this.id));
      	      }
      	      dbObject.put("name", this.name);
      	      dbObject.put("keywords", new BasicDBObject(this.hobbies));
          ...
      	      dbObject.put("messages", messages);
      	
              return dbObject;
      	 }




                                                                          11
Objet - BSON (II)

 Morphia (notre choix actuel)
        @Entity(noClassnameStored = true)        @Embedded
        public class Geoentity {                 public class Message {


               private String id;                     private String id;
                                                      ...
               private long updated;
        ...                                           // get - set
               private Set keywords;             }

                                                 @Embedded
              @Embedded
                                                 public class Location {
               private List<Message> messages;

                                                      private double lat;
              @Embedded
               private Location location;
                                                      private double lng;

              //get-set
                                                     //get - set
        }
                                                 }



                                                                            12
Objet - BSON (III)

 Morphia Object Wrapper
@Singleton
public class MapperMorphia {
    private Morphia morphia;

    @Inject
    public MapperMorphia() {
        this.morphia = new Morphia();
        this.morphia.mapPackage("com.myproject.model");
    }

    public <T extends Object> T from(Class<T> entityClass, DBObject dbObject) {
        return this.morphia.fromDBObject(entityClass, dbObject);
    }

    public <T extends Object> DBObject toDBObject(T modelObject) {
        return this.morphia.toDBObject(modelObject);
    }

}




                                                                                  13
Objet - BSON (III)

 Morphia Object Wrapper
@Singleton
public class MapperMorphia {
    private Morphia morphia;

    @Inject
    public MapperMorphia() {
        this.morphia = new Morphia();
        this.morphia.mapPackage("com.myproject.model");                  Attention !
    }

    public <T extends Object> T from(Class<T> entityClass, DBObject dbObject) {
        return this.morphia.fromDBObject(entityClass, dbObject);
    }

    public <T extends Object> DBObject toDBObject(T modelObject) {
        return this.morphia.toDBObject(modelObject);
    }

}




                                                                                   13
Objet - BSON (IV)

 Morphia Wrapper ... Re-Wrapper !
public class GeoentityMapper {
    private MapperMorphia mapperMorphia;

    @Inject
    public GeoentityMapper(MapperMorphia mapperMorphia) {
        this.mapperMorphia = mapperMorphia;
    }

    public BasicDBObject map(Geoentity geoentity) {
        BasicDBObject dbObject = (BasicDBObject) mapperMorphia.toDBObject(geoentity);

        if (!Strings.isNullOrEmpty(geoentity.getId())) {
            dbObject.put("_id", new ObjectId(geoentity.getId()));
        }
        return dbObject;
    }

    public Geoentity map(BasicDBObject rootObject) {
        return mapperMorphia.fromDBObject(Geoentity.class, rootObject);
    }
}

                                                                                        14
Objet - BSON (IV)

 Morphia Wrapper ... Re-Wrapper !
public class GeoentityMapper {
    private MapperMorphia mapperMorphia;

    @Inject
    public GeoentityMapper(MapperMorphia mapperMorphia) {
        this.mapperMorphia = mapperMorphia;
    }
                                                               Pour ObjectId -> String !
    public BasicDBObject map(Geoentity geoentity) {
        BasicDBObject dbObject = (BasicDBObject) mapperMorphia.toDBObject(geoentity);

        if (!Strings.isNullOrEmpty(geoentity.getId())) {
            dbObject.put("_id", new ObjectId(geoentity.getId()));
        }
        return dbObject;
    }

    public Geoentity map(BasicDBObject rootObject) {
        return mapperMorphia.fromDBObject(Geoentity.class, rootObject);
    }
}

                                                                                           14
CRUD (I)

 Créer un repository
   public class GeoentityRepository {
     private final MongoDB mongoDB;

       private final GeoentityMapper geoentityMapper;

       @Inject
       public GeoentityRepository(MongoDB mongoDB, GeoentityMapper geoentityMapper) {
             this.mongoDB = mongoDB;
             this.geoentityMapper = geoentityMapper;
       }

       protected DBCollection getCollection() {
          DBCollection collection = mongoDB.getDB().getCollection(NAME);
          return collection;
       }



       // autres méthodes ici

   }


                                                                                        15
CRUD (II)

 insert/save
> db.places.insert({"name" : "my place" , "location" :
{ "lat": 48.0130, "lng" : 21.050505}})

DBObject dbObject = mapper.map(geoentity);

getCollection("places").insert(dbObject);

ObjectId id = (ObjectId) dbObject.get("_id");

geoentity.setId(id.toString());




                                                         16
CRUD (II)

 insert/save
> db.places.insert({"name" : "my place" , "location" :
{ "lat": 48.0130, "lng" : 21.050505}})

DBObject dbObject = mapper.map(geoentity);

getCollection("places").insert(dbObject);

ObjectId id = (ObjectId) dbObject.get("_id");

geoentity.setId(id.toString());


                  Si l’écriture échoue, Mongo ne dira rien ...




                                                                 16
CRUD (II)

 insert/save
> db.places.insert({"name" : "my place" , "location" :
{ "lat": 48.0130, "lng" : 21.050505}})

DBObject dbObject = mapper.map(geoentity);

getCollection("places").insert(dbObject);

ObjectId id = (ObjectId) dbObject.get("_id");

geoentity.setId(id.toString());


                  Si l’écriture échoue, Mongo ne dira rien ...
             ... sauf si vous activez le «Write Concern» à SAFE !

             collection.setWriteConcern(WriteConcern.SAFE);

                                                                    16
CRUD (III)

 find by Id
> db.places.find({"_id" : ObjectId("4d6d42abf22737543cc01c0b")})




DBObject query = QueryBuilder.start("_id").is(new ObjectId(id))

BasicDBObject dbObject = getCollection("places").findOne(query.get());

if (null == dbObject) {
       return null;
}




                                                                         17
CRUD (IV)

 Remove
> db.places.remove({"_id" : ObjectId("4d6d42abf22737543cc01c0b")})




QueryBuilder builder = QueryBuilder.start("_id").is(new ObjectId(id));

getCollection().remove(builder.get());




                                                                         18
CRUD (V)

 Modifier un message et la date de mise à jour
 > db.places.update({"_id" : ObjectId("foo") , "messages.id" :
 "bar" }, {$set : { "message.$.text" : "my new text", "updated" :
 "dateFoo"} })

 DBObject searchQuery =
        QueryBuilder.start("_id").is(new ObjectId(id))
        .and("messages.id").is(messageId)
        .get();

 BasicDBObject updateObject =
          new BasicDBObject("messages.$.text", "my new text");
 updateObject.put("updated", Calendar.getInstance().getTimeInMillis());

 DBObject modificatorQuery = new BasicDBObject("$set", updateObject);

 getCollection().update(searchQuery, modificatorQuery);


                                                                          19
Recherche textuelle

 Recherche la valeur dans «name» OR «keywords»
 > db.places.find({"name" : "foo" , $or : [{"keywords" : "bar"}]})



 List<Geoentity> result = new ArrayList<Geoentity>();

 QueryBuilder.start().queryBuilder.put("name").is("foo");

 queryBuilder.or(new BasicDBObject("keywords", "bar"));

 DBCursor dbCursor = queryBuilder.get().find();

 while (dbCursor.hasNext()) {
      result.add(geoentityMapper.map(dbCursor.next()));
 }



                                                                     20
Recherche Spatiale (II)

 Near Search




 public List<Geoentity> circleSearch(Coordinate coordinate,
                                     Double radius) {

    QueryBuilder queryBuilder = QueryBuilder.start().and(LOCATION)
                                .near(coordinate.getLatitude(),
                                 coordinate.getLongitude(), radius);
    ...

    getCollection().find( queryBuilder.get()) )
                   .limit(100)
                   .sort("importance");

                                                                       21
Recherche Spatiale (II)

 Near Search

     35.000 éléments


 public List<Geoentity> circleSearch(Coordinate coordinate,
                                     Double radius) {

    QueryBuilder queryBuilder = QueryBuilder.start().and(LOCATION)
                                .near(coordinate.getLatitude(),
                                 coordinate.getLongitude(), radius);
    ...

    getCollection().find( queryBuilder.get()) )
                   .limit(100)
                   .sort("importance");

                                                                       21
Recherche Spatiale (II)

 Near Search

                                              limit(100) chargera 100
     35.000 éléments                          et sort(«champ») triera les résultats



 public List<Geoentity> circleSearch(Coordinate coordinate,
                                     Double radius) {

    QueryBuilder queryBuilder = QueryBuilder.start().and(LOCATION)
                                .near(coordinate.getLatitude(),
                                 coordinate.getLongitude(), radius);
    ...

    getCollection().find( queryBuilder.get()) )
                   .limit(100)
                   .sort("importance");

                                                                               21
Recherche Spatiale (I)

 BOX ou Circle Search



 public List<Geoentity> boxSearch (Coordinate bottomLeft,
                                   Coordinate topRight) {
     QueryBuilder queryBuilder =
             QueryBuilder.start().and(LOCATION)
                         .withinBox(bottomLeft.getLatitude(),
                                    bottomLeft.getLongitude(),
                                    topRight.getLatitude(),
                                    topRight.getLongitude());

    getCollection().find(queryBuilder.get()))
                   .limit(100)



                                                                 22
Recherche Spatiale (I)

 BOX ou Circle Search



 public List<Geoentity> boxSearch (Coordinate bottomLeft,
                                   Coordinate topRight) {
     QueryBuilder queryBuilder =
             QueryBuilder.start().and(LOCATION)
                         .withinBox(bottomLeft.getLatitude(),
                                    bottomLeft.getLongitude(),
                                    topRight.getLatitude(),
                                    topRight.getLongitude());

    getCollection().find(queryBuilder.get()))
                   .limit(100)
                   .sort("importance");


                                                                 22
Recherche Spatiale (I)

 BOX ou Circle Search

  35.000 éléments

 public List<Geoentity> boxSearch (Coordinate bottomLeft,
                                   Coordinate topRight) {
     QueryBuilder queryBuilder =
             QueryBuilder.start().and(LOCATION)
                         .withinBox(bottomLeft.getLatitude(),
                                    bottomLeft.getLongitude(),
                                    topRight.getLatitude(),
                                    topRight.getLongitude());

    getCollection().find(queryBuilder.get()))
                   .limit(100)
                   .sort("importance");


                                                                 22
Recherche Spatiale (I)

 BOX ou Circle Search
                                             sort() charge les 35.000 éléments.
  35.000 éléments                            Index 2D trie uniquement par
                                             distance


 public List<Geoentity> boxSearch (Coordinate bottomLeft,
                                   Coordinate topRight) {
     QueryBuilder queryBuilder =
             QueryBuilder.start().and(LOCATION)
                         .withinBox(bottomLeft.getLatitude(),
                                    bottomLeft.getLongitude(),
                                    topRight.getLatitude(),
                                    topRight.getLongitude());

    getCollection().find(queryBuilder.get()))
                   .limit(100)
                   .sort("importance");


                                                                              22
Indexation (I)

 Avant de chercher, les indexes doivent exister

 Index mot-clé


    DBObject indexObject = BasicDBObjectBuilder.start()
                           .add("name", 1)
                           .add("updated", -1).get();

    getCollection().ensureIndex(indexObject);




                                                          23
Indexation (II)

 Index 2D
 ▶ valeur «2D»
 ▶ toujours le premier


   public class Geoentity {                public class Location {

       private Location location;                private double lat;
   }                                             private double lng;

                                           }


   DBObject indexObject2d = BasicDBObjectBuilder.start()
                           .add(LOCATION, "2d")
                           .add("keywords", 1).get();

   getCollection().createIndex(indexObject2d);



                                                                       24
Indexation (II)

 Index 2D
 ▶ valeur «2D»
 ▶ toujours le premier


   public class Geoentity {                public class Location {

       private Location location;                private double lat;
   }                                             private double lng;
                                                 private double alt;
                                           }


   DBObject indexObject2d = BasicDBObjectBuilder.start()
                           .add(LOCATION, "2d")
                           .add("keywords", 1).get();

   getCollection().createIndex(indexObject2d);



                                                                       24
Indexation (II)

 Index 2D
 ▶ valeur «2D»
 ▶ toujours le premier                            L’indexation n’aura pas d’effet


   public class Geoentity {                public class Location {

       private Location location;                private double lat;
   }                                             private double lng;
                                                 private double alt;
                                           }


   DBObject indexObject2d = BasicDBObjectBuilder.start()
                           .add(LOCATION, "2d")
                           .add("keywords", 1).get();

   getCollection().createIndex(indexObject2d);



                                                                               24
Conclusions




              25
Conclusions

 Prise en main de MongoDB rapide et ludique




                                               25
Conclusions

 Prise en main de MongoDB rapide et ludique
 Montée en compétence rapide




                                               25
Conclusions

 Prise en main de MongoDB rapide et ludique
 Montée en compétence rapide
 Adaptation naturelle à la philosophie




                                               25
Conclusions

 Prise en main de MongoDB rapide et ludique
 Montée en compétence rapide
 Adaptation naturelle à la philosophie
 Test unitaires / d’intégration sont plus faciles à
 maintenir




                                                       25
Conclusions

 Prise en main de MongoDB rapide et ludique
 Montée en compétence rapide
 Adaptation naturelle à la philosophie
 Test unitaires / d’intégration sont plus faciles à
  maintenir
 Fonctionne bien avec Java




                                                       25
Conclusions

 Prise en main de MongoDB rapide et ludique
 Montée en compétence rapide
 Adaptation naturelle à la philosophie
 Test unitaires / d’intégration sont plus faciles à
  maintenir
 Fonctionne bien avec Java
 Le driver (avec et sans Morphia) reste un peu verbeux
  mais il s’améliore en continu




                                                       25
Conclusions

 Prise en main de MongoDB rapide et ludique
 Montée en compétence rapide
 Adaptation naturelle à la philosophie
 Test unitaires / d’intégration sont plus faciles à
  maintenir
 Fonctionne bien avec Java
 Le driver (avec et sans Morphia) reste un peu verbeux
  mais il s’améliore en continu
 Il est important de mener des tests de performance
  au plus tôt dans le cycle de développement (JMeter)




                                                       25
Conclusions

 Prise en main de MongoDB rapide et ludique
 Montée en compétence rapide
 Adaptation naturelle à la philosophie
 Test unitaires / d’intégration sont plus faciles à
  maintenir
 Fonctionne bien avec Java
 Le driver (avec et sans Morphia) reste un peu verbeux
  mais il s’améliore en continu
 Il est important de mener des tests de performance
  au plus tôt dans le cycle de développement (JMeter)
 Il faut bien connaître les options et la configuration


                                                       25
Questions / Réponses




                       26
@karesti
blog.xebia.fr

jduchess.org/duchess-france
    http://www.toutcaen.com/images/merci.jpg
                                               27

Mongo db et java en pratique

  • 1.
    MongoDB et Javaen pratique MongoFR 23/03/2011
  • 2.
    Speaker Katia Aresti, Xebia Développeur Java : 5 ans d'expérience MongoDB : 6 mois @karesti blog.xebia.fr jduchess.org/duchess-france 2
  • 3.
  • 4.
  • 5.
    Agenda  Pourquoi MongoDB  Configuration et connexion 3
  • 6.
    Agenda  Pourquoi MongoDB  Configuration et connexion  Objet-Mongo Mapper 3
  • 7.
    Agenda  Pourquoi MongoDB  Configuration et connexion  Objet-Mongo Mapper  Recherche textuelle 3
  • 8.
    Agenda  Pourquoi MongoDB  Configuration et connexion  Objet-Mongo Mapper  Recherche textuelle  Recherche spatiale 3
  • 9.
    Agenda  Pourquoi MongoDB  Configuration et connexion  Objet-Mongo Mapper  Recherche textuelle  Recherche spatiale  Indexation 3
  • 10.
    Agenda  Pourquoi MongoDB  Configuration et connexion  Objet-Mongo Mapper  Recherche textuelle  Recherche spatiale  Indexation  Conclusions 3
  • 11.
    Agenda  Pourquoi MongoDB  Configuration et connexion  Objet-Mongo Mapper  Recherche textuelle  Recherche spatiale  Indexation  Conclusions  Questions 3
  • 12.
    Notre système (I) Recherche Paris http://www.parisnet.com/images/parismap_new.gif 4
  • 13.
    Notre système (I) Recherche Paris http://www.parisnet.com/images/parismap_new.gif 4
  • 14.
    Notre système (I) Recherche Bistro Paris http://www.parisnet.com/images/parismap_new.gif 4
  • 15.
    Notre système (I) Recherche Bistro Bistro du Bastille Bistro de Mer Mon grand Bistro MongoDB bistro Boulanger Bistro Jean Bistro votre Plombier ... Paris http://www.parisnet.com/images/parismap_new.gif 4
  • 16.
    Notre système (II) POI : Point of Interest TOUTE LA FRANCE !!! http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif 5
  • 17.
    Notre système (II) POI : Point of Interest Monuments TOUTE LA FRANCE !!! http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif 5
  • 18.
    Notre système (II) POI : Point of Interest Monuments Mairies TOUTE LA FRANCE !!! http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif 5
  • 19.
    Notre système (II) POI : Point of Interest Monuments Mairies Médecins TOUTE LA FRANCE !!! http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif 5
  • 20.
    Notre système (II) POI : Point of Interest Monuments Mairies Médecins Restaurants TOUTE LA FRANCE !!! http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif 5
  • 21.
    Notre système (II) POI : Point of Interest Monuments Mairies Médecins Restaurants ... TOUTE LA FRANCE !!! http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif 5
  • 22.
    Notre système (II) POI : Point of Interest Monuments Mairies Médecins Restaurants ... mais aussi le mode contributif pour ajouter mes lieux personnels ... TOUTE LA FRANCE !!! http://lh3.ggpht.com/_ViFLVzjIPSk/TR7lvqmu8lI/AAAAAAAAEAg/dHEuf1Dvz6o/Map_France_French-cities.gif 5
  • 23.
  • 24.
    Notre système (III) En bref :  Modèle de données très hétérogène 6
  • 25.
    Notre système (III) En bref :  Modèle de données très hétérogène  Relations simples (1 - 1) et absence de transactions complexes 6
  • 26.
    Notre système (III) En bref :  Modèle de données très hétérogène  Relations simples (1 - 1) et absence de transactions complexes  Recherche par mot-clé (full-text à venir) 6
  • 27.
    Notre système (III) En bref :  Modèle de données très hétérogène  Relations simples (1 - 1) et absence de transactions complexes  Recherche par mot-clé (full-text à venir)  Recherche spatiale 6
  • 28.
    Notre système (III) En bref :  Modèle de données très hétérogène  Relations simples (1 - 1) et absence de transactions complexes  Recherche par mot-clé (full-text à venir)  Recherche spatiale  Fort trafic et performance en temps réel 6
  • 29.
    Notre système (III) En bref :  Modèle de données très hétérogène  Relations simples (1 - 1) et absence de transactions complexes  Recherche par mot-clé (full-text à venir)  Recherche spatiale  Fort trafic et performance en temps réel  Scalabilité horizontale 6
  • 30.
    Notre système (III) En bref :  Modèle de données très hétérogène  Relations simples (1 - 1) et absence de transactions complexes  Recherche par mot-clé (full-text à venir)  Recherche spatiale  Fort trafic et performance en temps réel  Scalabilité horizontale  Délais très courts de livraison 6
  • 31.
    Notre système (III) En bref :  Modèle de données très hétérogène  Relations simples (1 - 1) et absence de transactions complexes  Recherche par mot-clé (full-text à venir)  Recherche spatiale  Fort trafic et performance en temps réel  Scalabilité horizontale  Délais très courts de livraison  Equipe Java 6
  • 32.
    Notre système (IV) HTTP Java Driver ... REST BD de POI Services XML CSV SOAP REST Sources ... http://www.openclipart.org/ 7
  • 33.
    Geoentity Java publicclass Geoentity { public class Message { private String id; private String id; private long updated; private String text; private String name; 1 .. N private String userId; private String userId; private long created; } private Set keywords; private List<Message> messages; public class Location { 1 .. 1 private Location location; private double lat; } private double lng; } 8
  • 34.
    Geoentity Java publicclass Geoentity { public class Message { private String id; private String id; private long updated; private String text; private String name; 1 .. N private String userId; private String userId; private long created; } private Set keywords; private List<Message> messages; public class Location { 1 .. 1 private Location location; private double lat; } private double lng; } Une collection : «places» 8
  • 35.
    Configurer la connexion(I)  Maven <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>2.5</version> </dependency> 9
  • 36.
    Configurer la connexion(I)  Maven <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>2.5</version> </dependency> Suivez le projet sur GitHub ! 9
  • 37.
    Configurer la connexion(II)  IoC : Guice @Singleton public class MongoDB { private Mongo mongo; private DB db; @Inject public MongoDB(String host, int port, int nb) { MongoOptions mongoOpts = new MongoOptions(); mongoOptions.connectionsPerHost = nb; try { mongo = new Mongo(new ServerAddress(host, port), mongoOpts); } catch (Exception e) { throw new RuntimeException(e); } db = mongo.getDB(DATABASE_NAME); } } 10
  • 38.
    Configurer la connexion(II)  IoC : Guice @Singleton public class MongoDB { private Mongo mongo; private DB db; @Inject Attention ! public MongoDB(String host, int port, int nb) { MongoOptions mongoOpts = new MongoOptions(); mongoOptions.connectionsPerHost = nb; try { mongo = new Mongo(new ServerAddress(host, port), mongoOpts); } catch (Exception e) { throw new RuntimeException(e); } db = mongo.getDB(DATABASE_NAME); } } 10
  • 39.
    Objet - BSON(I)  API driver Java (notre choix initial) public DBObject toBasicDBObject() { BasicDBObject dbObject = new BasicDBObject(); if (this.id != null && ObjectId.isValid(this.id)) { dbObject.put("_id", new ObjectId(this.id)); } dbObject.put("name", this.name); dbObject.put("keywords", new BasicDBObject(this.hobbies)); ... dbObject.put("messages", messages); return dbObject; } 11
  • 40.
    Objet - BSON(II)  Morphia (notre choix actuel) @Entity(noClassnameStored = true) @Embedded public class Geoentity { public class Message { private String id; private String id; ... private long updated; ... // get - set private Set keywords; } @Embedded @Embedded public class Location { private List<Message> messages; private double lat; @Embedded private Location location; private double lng; //get-set //get - set } } 12
  • 41.
    Objet - BSON(III)  Morphia Object Wrapper @Singleton public class MapperMorphia { private Morphia morphia; @Inject public MapperMorphia() { this.morphia = new Morphia(); this.morphia.mapPackage("com.myproject.model"); } public <T extends Object> T from(Class<T> entityClass, DBObject dbObject) { return this.morphia.fromDBObject(entityClass, dbObject); } public <T extends Object> DBObject toDBObject(T modelObject) { return this.morphia.toDBObject(modelObject); } } 13
  • 42.
    Objet - BSON(III)  Morphia Object Wrapper @Singleton public class MapperMorphia { private Morphia morphia; @Inject public MapperMorphia() { this.morphia = new Morphia(); this.morphia.mapPackage("com.myproject.model"); Attention ! } public <T extends Object> T from(Class<T> entityClass, DBObject dbObject) { return this.morphia.fromDBObject(entityClass, dbObject); } public <T extends Object> DBObject toDBObject(T modelObject) { return this.morphia.toDBObject(modelObject); } } 13
  • 43.
    Objet - BSON(IV)  Morphia Wrapper ... Re-Wrapper ! public class GeoentityMapper { private MapperMorphia mapperMorphia; @Inject public GeoentityMapper(MapperMorphia mapperMorphia) { this.mapperMorphia = mapperMorphia; } public BasicDBObject map(Geoentity geoentity) { BasicDBObject dbObject = (BasicDBObject) mapperMorphia.toDBObject(geoentity); if (!Strings.isNullOrEmpty(geoentity.getId())) { dbObject.put("_id", new ObjectId(geoentity.getId())); } return dbObject; } public Geoentity map(BasicDBObject rootObject) { return mapperMorphia.fromDBObject(Geoentity.class, rootObject); } } 14
  • 44.
    Objet - BSON(IV)  Morphia Wrapper ... Re-Wrapper ! public class GeoentityMapper { private MapperMorphia mapperMorphia; @Inject public GeoentityMapper(MapperMorphia mapperMorphia) { this.mapperMorphia = mapperMorphia; } Pour ObjectId -> String ! public BasicDBObject map(Geoentity geoentity) { BasicDBObject dbObject = (BasicDBObject) mapperMorphia.toDBObject(geoentity); if (!Strings.isNullOrEmpty(geoentity.getId())) { dbObject.put("_id", new ObjectId(geoentity.getId())); } return dbObject; } public Geoentity map(BasicDBObject rootObject) { return mapperMorphia.fromDBObject(Geoentity.class, rootObject); } } 14
  • 45.
    CRUD (I)  Créerun repository public class GeoentityRepository { private final MongoDB mongoDB; private final GeoentityMapper geoentityMapper; @Inject public GeoentityRepository(MongoDB mongoDB, GeoentityMapper geoentityMapper) { this.mongoDB = mongoDB; this.geoentityMapper = geoentityMapper; } protected DBCollection getCollection() { DBCollection collection = mongoDB.getDB().getCollection(NAME); return collection; } // autres méthodes ici } 15
  • 46.
    CRUD (II)  insert/save >db.places.insert({"name" : "my place" , "location" : { "lat": 48.0130, "lng" : 21.050505}}) DBObject dbObject = mapper.map(geoentity); getCollection("places").insert(dbObject); ObjectId id = (ObjectId) dbObject.get("_id"); geoentity.setId(id.toString()); 16
  • 47.
    CRUD (II)  insert/save >db.places.insert({"name" : "my place" , "location" : { "lat": 48.0130, "lng" : 21.050505}}) DBObject dbObject = mapper.map(geoentity); getCollection("places").insert(dbObject); ObjectId id = (ObjectId) dbObject.get("_id"); geoentity.setId(id.toString()); Si l’écriture échoue, Mongo ne dira rien ... 16
  • 48.
    CRUD (II)  insert/save >db.places.insert({"name" : "my place" , "location" : { "lat": 48.0130, "lng" : 21.050505}}) DBObject dbObject = mapper.map(geoentity); getCollection("places").insert(dbObject); ObjectId id = (ObjectId) dbObject.get("_id"); geoentity.setId(id.toString()); Si l’écriture échoue, Mongo ne dira rien ... ... sauf si vous activez le «Write Concern» à SAFE ! collection.setWriteConcern(WriteConcern.SAFE); 16
  • 49.
    CRUD (III)  findby Id > db.places.find({"_id" : ObjectId("4d6d42abf22737543cc01c0b")}) DBObject query = QueryBuilder.start("_id").is(new ObjectId(id)) BasicDBObject dbObject = getCollection("places").findOne(query.get()); if (null == dbObject) { return null; } 17
  • 50.
    CRUD (IV)  Remove >db.places.remove({"_id" : ObjectId("4d6d42abf22737543cc01c0b")}) QueryBuilder builder = QueryBuilder.start("_id").is(new ObjectId(id)); getCollection().remove(builder.get()); 18
  • 51.
    CRUD (V)  Modifierun message et la date de mise à jour > db.places.update({"_id" : ObjectId("foo") , "messages.id" : "bar" }, {$set : { "message.$.text" : "my new text", "updated" : "dateFoo"} }) DBObject searchQuery = QueryBuilder.start("_id").is(new ObjectId(id)) .and("messages.id").is(messageId) .get(); BasicDBObject updateObject = new BasicDBObject("messages.$.text", "my new text"); updateObject.put("updated", Calendar.getInstance().getTimeInMillis()); DBObject modificatorQuery = new BasicDBObject("$set", updateObject); getCollection().update(searchQuery, modificatorQuery); 19
  • 52.
    Recherche textuelle  Recherchela valeur dans «name» OR «keywords» > db.places.find({"name" : "foo" , $or : [{"keywords" : "bar"}]}) List<Geoentity> result = new ArrayList<Geoentity>(); QueryBuilder.start().queryBuilder.put("name").is("foo"); queryBuilder.or(new BasicDBObject("keywords", "bar")); DBCursor dbCursor = queryBuilder.get().find(); while (dbCursor.hasNext()) { result.add(geoentityMapper.map(dbCursor.next())); } 20
  • 53.
    Recherche Spatiale (II) Near Search public List<Geoentity> circleSearch(Coordinate coordinate, Double radius) { QueryBuilder queryBuilder = QueryBuilder.start().and(LOCATION) .near(coordinate.getLatitude(), coordinate.getLongitude(), radius); ... getCollection().find( queryBuilder.get()) ) .limit(100) .sort("importance"); 21
  • 54.
    Recherche Spatiale (II) Near Search 35.000 éléments public List<Geoentity> circleSearch(Coordinate coordinate, Double radius) { QueryBuilder queryBuilder = QueryBuilder.start().and(LOCATION) .near(coordinate.getLatitude(), coordinate.getLongitude(), radius); ... getCollection().find( queryBuilder.get()) ) .limit(100) .sort("importance"); 21
  • 55.
    Recherche Spatiale (II) Near Search limit(100) chargera 100 35.000 éléments et sort(«champ») triera les résultats public List<Geoentity> circleSearch(Coordinate coordinate, Double radius) { QueryBuilder queryBuilder = QueryBuilder.start().and(LOCATION) .near(coordinate.getLatitude(), coordinate.getLongitude(), radius); ... getCollection().find( queryBuilder.get()) ) .limit(100) .sort("importance"); 21
  • 56.
    Recherche Spatiale (I) BOX ou Circle Search public List<Geoentity> boxSearch (Coordinate bottomLeft, Coordinate topRight) { QueryBuilder queryBuilder = QueryBuilder.start().and(LOCATION) .withinBox(bottomLeft.getLatitude(), bottomLeft.getLongitude(), topRight.getLatitude(), topRight.getLongitude()); getCollection().find(queryBuilder.get())) .limit(100) 22
  • 57.
    Recherche Spatiale (I) BOX ou Circle Search public List<Geoentity> boxSearch (Coordinate bottomLeft, Coordinate topRight) { QueryBuilder queryBuilder = QueryBuilder.start().and(LOCATION) .withinBox(bottomLeft.getLatitude(), bottomLeft.getLongitude(), topRight.getLatitude(), topRight.getLongitude()); getCollection().find(queryBuilder.get())) .limit(100) .sort("importance"); 22
  • 58.
    Recherche Spatiale (I) BOX ou Circle Search 35.000 éléments public List<Geoentity> boxSearch (Coordinate bottomLeft, Coordinate topRight) { QueryBuilder queryBuilder = QueryBuilder.start().and(LOCATION) .withinBox(bottomLeft.getLatitude(), bottomLeft.getLongitude(), topRight.getLatitude(), topRight.getLongitude()); getCollection().find(queryBuilder.get())) .limit(100) .sort("importance"); 22
  • 59.
    Recherche Spatiale (I) BOX ou Circle Search sort() charge les 35.000 éléments. 35.000 éléments Index 2D trie uniquement par distance public List<Geoentity> boxSearch (Coordinate bottomLeft, Coordinate topRight) { QueryBuilder queryBuilder = QueryBuilder.start().and(LOCATION) .withinBox(bottomLeft.getLatitude(), bottomLeft.getLongitude(), topRight.getLatitude(), topRight.getLongitude()); getCollection().find(queryBuilder.get())) .limit(100) .sort("importance"); 22
  • 60.
    Indexation (I)  Avantde chercher, les indexes doivent exister  Index mot-clé DBObject indexObject = BasicDBObjectBuilder.start() .add("name", 1) .add("updated", -1).get(); getCollection().ensureIndex(indexObject); 23
  • 61.
    Indexation (II)  Index2D ▶ valeur «2D» ▶ toujours le premier public class Geoentity { public class Location { private Location location; private double lat; } private double lng; } DBObject indexObject2d = BasicDBObjectBuilder.start() .add(LOCATION, "2d") .add("keywords", 1).get(); getCollection().createIndex(indexObject2d); 24
  • 62.
    Indexation (II)  Index2D ▶ valeur «2D» ▶ toujours le premier public class Geoentity { public class Location { private Location location; private double lat; } private double lng; private double alt; } DBObject indexObject2d = BasicDBObjectBuilder.start() .add(LOCATION, "2d") .add("keywords", 1).get(); getCollection().createIndex(indexObject2d); 24
  • 63.
    Indexation (II)  Index2D ▶ valeur «2D» ▶ toujours le premier L’indexation n’aura pas d’effet public class Geoentity { public class Location { private Location location; private double lat; } private double lng; private double alt; } DBObject indexObject2d = BasicDBObjectBuilder.start() .add(LOCATION, "2d") .add("keywords", 1).get(); getCollection().createIndex(indexObject2d); 24
  • 64.
  • 65.
    Conclusions  Prise enmain de MongoDB rapide et ludique 25
  • 66.
    Conclusions  Prise enmain de MongoDB rapide et ludique  Montée en compétence rapide 25
  • 67.
    Conclusions  Prise enmain de MongoDB rapide et ludique  Montée en compétence rapide  Adaptation naturelle à la philosophie 25
  • 68.
    Conclusions  Prise enmain de MongoDB rapide et ludique  Montée en compétence rapide  Adaptation naturelle à la philosophie  Test unitaires / d’intégration sont plus faciles à maintenir 25
  • 69.
    Conclusions  Prise enmain de MongoDB rapide et ludique  Montée en compétence rapide  Adaptation naturelle à la philosophie  Test unitaires / d’intégration sont plus faciles à maintenir  Fonctionne bien avec Java 25
  • 70.
    Conclusions  Prise enmain de MongoDB rapide et ludique  Montée en compétence rapide  Adaptation naturelle à la philosophie  Test unitaires / d’intégration sont plus faciles à maintenir  Fonctionne bien avec Java  Le driver (avec et sans Morphia) reste un peu verbeux mais il s’améliore en continu 25
  • 71.
    Conclusions  Prise enmain de MongoDB rapide et ludique  Montée en compétence rapide  Adaptation naturelle à la philosophie  Test unitaires / d’intégration sont plus faciles à maintenir  Fonctionne bien avec Java  Le driver (avec et sans Morphia) reste un peu verbeux mais il s’améliore en continu  Il est important de mener des tests de performance au plus tôt dans le cycle de développement (JMeter) 25
  • 72.
    Conclusions  Prise enmain de MongoDB rapide et ludique  Montée en compétence rapide  Adaptation naturelle à la philosophie  Test unitaires / d’intégration sont plus faciles à maintenir  Fonctionne bien avec Java  Le driver (avec et sans Morphia) reste un peu verbeux mais il s’améliore en continu  Il est important de mener des tests de performance au plus tôt dans le cycle de développement (JMeter)  Il faut bien connaître les options et la configuration 25
  • 73.
  • 74.
    @karesti blog.xebia.fr jduchess.org/duchess-france http://www.toutcaen.com/images/merci.jpg 27