SlideShare une entreprise Scribd logo
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

Contenu connexe

Mongo db et java en pratique

  • 1. MongoDB et Java en 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. Agenda 3
  • 5. Agenda  Pourquoi Mongo DB  Configuration et connexion 3
  • 6. Agenda  Pourquoi Mongo DB  Configuration et connexion  Objet-Mongo Mapper 3
  • 7. Agenda  Pourquoi Mongo DB  Configuration et connexion  Objet-Mongo Mapper  Recherche textuelle 3
  • 8. Agenda  Pourquoi Mongo DB  Configuration et connexion  Objet-Mongo Mapper  Recherche textuelle  Recherche spatiale 3
  • 9. Agenda  Pourquoi Mongo DB  Configuration et connexion  Objet-Mongo Mapper  Recherche textuelle  Recherche spatiale  Indexation 3
  • 10. Agenda  Pourquoi Mongo DB  Configuration et connexion  Objet-Mongo Mapper  Recherche textuelle  Recherche spatiale  Indexation  Conclusions 3
  • 11. Agenda  Pourquoi Mongo DB  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
  • 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 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
  • 34. 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
  • 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é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
  • 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)  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
  • 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)  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
  • 52. 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
  • 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)  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
  • 61. 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
  • 62. 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
  • 63. 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
  • 65. Conclusions  Prise en main de MongoDB rapide et ludique 25
  • 66. Conclusions  Prise en main de MongoDB rapide et ludique  Montée en compétence rapide 25
  • 67. Conclusions  Prise en main de MongoDB rapide et ludique  Montée en compétence rapide  Adaptation naturelle à la philosophie 25
  • 68. 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
  • 69. 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
  • 70. 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
  • 71. 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
  • 72. 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
  • 74. @karesti blog.xebia.fr jduchess.org/duchess-france http://www.toutcaen.com/images/merci.jpg 27

Notes de l'éditeur

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n