Slide 1

Slide 1 text

Miguel Olivares y Leonardo Menezes Introducción a y Spring Data (@moliware) (@leonardomenezes)

Slide 2

Slide 2 text

¿Qué es MongoDB? • Base de datos orientada a documentos • Sin esquema • Escalable • DB => Colección => Documento

Slide 3

Slide 3 text

¿Qué es una colección? • Es un grupo de documentos. • Se identifica con un nombre • Es el “equivalente” a una tabla en un modelo relacional

Slide 4

Slide 4 text

¿Qué es un documento? • Un conjunto de pares clave-valor • Coloquialmente un “hash”, “map” o “dictionary” • Formato BSON

Slide 5

Slide 5 text

Arquitectura

Slide 6

Slide 6 text

Consola • Consola javascript completa • Cliente MongoDB

Slide 7

Slide 7 text

¿Por qué MongoDB? • Fácil escalabilidad • Flexibilidad • Alto rendimiento • Consultas potentes • MapReduce

Slide 8

Slide 8 text

Benchmark (45 M de documentos) Tiempo medio de inserción Tamaño en disco SolR 0.603(ms) 49 GB CouchDB 0.297(ms) 43 GB Cassandra 0.516(ms) 50 GB MongoDB 0.040(ms) 43 GB

Slide 9

Slide 9 text

Introducción a Spring Data

Slide 10

Slide 10 text

¿Por que Spring Data? • Mapeo/Conversión entre POJOs y docs MongoDB • MongoTemplate • Implementación automática de Repository(DAO) • DSL basado en Java para Query, Criteria y Update • Soporte a persistencia mixta(Cross-store persistance) • Integración con GeoSpatial de Mongo • Integración con Map Reduce de Mongo • Administración y monitorización por JMX

Slide 11

Slide 11 text

Diseño de una aplicación • Diseño de documentos • Consultas de la aplicación • Optimización de las consultas

Slide 12

Slide 12 text

Diseño de documentos • Identificar tipos de documentos • Separar cada tipo de documento en distintas colecciones • Referenciar vs Duplicar

Slide 13

Slide 13 text

Diseño de documentos • Identificar tipos de documentos • Separar cada tipo de documento en distintas colecciones • Referenciar vs Duplicar • Consistencia • Consultas extras • Inconsistencia • Documento autodescriptivo

Slide 14

Slide 14 text

Documentos _id name following _id text date user geo Usuario Tweet

Slide 15

Slide 15 text

Usuarios • name : Nombre de usuario • following: Array de referencias a usuarios

Slide 16

Slide 16 text

Tweets • text : texto • date : objeto Date de javascript. • user: referencia al usuario • geo: coordenadas

Slide 17

Slide 17 text

Modelo MongoDB

Slide 18

Slide 18 text

Modelo Relacional

Slide 19

Slide 19 text

Inserción de usuarios var miguel = {"name" : "miguel", "following" : []} db.user.save(miguel) var leonardomenezes = {"name" : "leonardomenezes", "following" : [new DBRef("user", miguel._id)]} db.user.save(leonardomenezes)

Slide 20

Slide 20 text

Inserción de Tweets var tweet = {"text" : "Tweet ejemplo", "date" : new Date(), "user" : new DBRef("user", leonardomenezes._id), "geo" : { "x" : 1, "y" : 1 } } db.tweet.save(tweet)

Slide 21

Slide 21 text

Actualización de documentos • Update tipo SQL • Posibilidad de upsert, es decir, si no existe lo crea • Existen muchos “update modifiers” : $inc, $set, $unset, $push, $pushAll, $addToSet, $pop, $pull, $pullAll, $rename, $bit

Slide 22

Slide 22 text

Actualización de documentos El usuario miguel ahora sigue a leonardomenezes: var ref_leo = new DBRef("user", leonardomenezes._id) db.user.update({"name" : "miguel"}, {$push: {"following" : ref_leo}})

Slide 23

Slide 23 text

MongoDB Java Driver public void ejemplo1() throws UnknownHostException, MongoException { Mongo mongo = new Mongo("localhost"); DB db = mongo.getDB("database"); db.dropDatabase(); DBCollection users = db.getCollection("users"); BasicDBObject usuario = new BasicDBObject(); usuario.put("nombre", "Marco Martinez"); users.insert(usuario); BasicDBObject usuario2 = new BasicDBObject(); usuario2.put("nombre", "Leonardo Menezes"); usuario2.put("following", new DBRef(db, "users", usuario)); users.insert(usuario2); }

Slide 24

Slide 24 text

MongoTemplate Mapeo/Conversión de POJO´s

Slide 25

Slide 25 text

Mapeo de los Documentos @Document(collection = "tweets") public class Tweet { private ObjectId id; private String text; private Date date; @DBRef private User user; private double[] coordinate; ... getters y constructor ... @Document(collection = "users") public class User { private ObjectId id; private String name; @DBRef private Set following; ... getters y constructor ...

Slide 26

Slide 26 text

Configuración de MongoTemplate @Configuration public class AppConfig { @Bean public Mongo mongo() throws UnknownHostException, MongoException { return new Mongo("localhost"); } @Bean public MongoTemplate mongoTemplate() throws UnknownHostException, MongoException { return new MongoTemplate(mongo(), "database"); } }

Slide 27

Slide 27 text

Inserción con MongoTemplate /** * Ejemplo sencillo de inserción */ private void ejemplo1() { User user = new User("Marco Martinez"); User user2 = new User("Alejandro Marqués"); User user3 = new User("Javier Alba"); mongoTemplate.save(user); logger.info("Users " + user + " saved"); mongoTemplate.save(user2); logger.info("Users " + user2 + " saved"); mongoTemplate.save(user3); logger.info("Users " + user3 + " saved"); user.follow(user2); mongoTemplate.save(user); logger.info("Users " + user + " updated"); } INFO - Users User [id=4eb846971a8868b98ed3d7c3, name=Marco Martinez, following=0] saved INFO - Users User [id=4eb846971a8868b98ed3d7c4, name=Alejandro Marqués, following=0] saved INFO - Users User [id=4eb846971a8868b98ed3d7c5, name=Javier Alba, following=0] saved INFO - Users User [id=4eb846971a8868b98ed3d7c3, name=Marco Martinez, following=1] updated

Slide 28

Slide 28 text

Update Modifiers con Spring Data /** * Ejemplo de acutalización */ public void ejemplo2() { mongoTemplate.updateFirst(new Query(new Criteria("name").is("Marc Martinez")), new Update().set("name", "Marco Martinez"), User.class); User user = mongoTemplate.findOne(new Query(new Criteria("name").is("Marco Martinez")), User.class); User user3 = mongoTemplate.findOne(new Query(new Criteria("name").is("Javier Alba")), User.class); mongoTemplate.updateFirst(new Query(new Criteria("name").is("Marco Martinez")), new Update().addToSet("following", user3), User.class); user = mongoTemplate.findOne(new Query(new Criteria("name").is("Marco Martinez")), User.class); logger.info("User found: " + user); } INFO - User found: User [id=4eb84a0e1a885514a5745b2d, name=Marco Martinez, following=2]

Slide 29

Slide 29 text

Diseño de una aplicación • Diseño de documentos • Consultas de la aplicación • Optimización de las consultas

Slide 30

Slide 30 text

Consultas en MongoDB SELECT a,b FROM users db.users.find({}, {a:1,b:1}) SELECT * FROM users WHERE age=33 db.users.find({age:33}) SELECT * FROM users WHERE age=33 ORDER BY name db.users.find({age:33}).sort({name:1}) SELECT * FROM users WHERE age=33 ORDER BY name LIMIT=10 OFFSET=0 db.users.find({age:33}).sort({name:1}).limit(10).skip(0)

Slide 31

Slide 31 text

Perfil de usuario • Seguidores • Mis tweets

Slide 32

Slide 32 text

Consultas - Seguidores SELECT user_following.following_id FROM user, user_following WHERE user.name = “leonardomenezes” AND user.id = user_following.user_id db.user.findOne({"name" : "leonardomenezes"}, {“following” : 1});

Slide 33

Slide 33 text

Consultas - Mis Tweets select * from tweet where tweet.user_id=X order by date DESC db.tweet.find({"user.$id" : X}).sort({'date' : -1})

Slide 34

Slide 34 text

Más consultas MongoDB SELECT * FROM users WHERE age>33 db.users.find({age:{$gt:33}}) SELECT * FROM users WHERE name LIKE "Joe%" db.users.find({name:/^Joe/}) SELECT * FROM users WHERE a=1 or b=2 db.users.find({$or:[{a:1} ,{b:2}]}) SELECT COUNT(*) FROM users db.users.count()

Slide 35

Slide 35 text

Más operadores • $where + código javascript • $gt, $lt, $gte, $lte • $all, $exists, $mod, $ne, $in, $nin, $nor, $or, $and, $size, $type .....

Slide 36

Slide 36 text

Timeline SELECT * FROM tweet WHERE user_id = X OR user_id = ...... ORDER BY date db.tweet.find({"user.$id" : {$in : ids}}).sort({'date' : -1})

Slide 37

Slide 37 text

MongoRepository(DAO) • CRUD(CrudRepository) • count, delete, deleteAll, exists, findOne, save... • Paginación/Ordenación(PagingAndSortingRepository) • Métodos de búsqueda dinámicos

Slide 38

Slide 38 text

Repositorios count() Returns the number of entities available. delete(ID id) Deletes the entity with the given id. delete(Iterable extends T> entities) Deletes the given entities. delete(T entity) Deletes a given entity. deleteAll() Deletes all entities managed by the repository. exists(ID id) Returns whether an entity with the given id exists. findAll() Returns all instances of the type. findOne(ID id) Retrives an entity by its primary key. save(Iterable extends T> entities) Saves all given entities. save(T entity) Saves a given entity. findAll(Pageable pageable) Returns a Page of entities meeting the paging restriction provided in the Pageable object. findAll(Sort sort) Returns all entities sorted by the given options.

Slide 39

Slide 39 text

UserRepository y TweetRepository @Repository public interface UserRepository extends MongoRepository { public User findByName(String name); } @Repository public interface TweetRepository extends MongoRepository { public List findByTextLike(String text); public List findByDateLessThan(Date date); @Query("{ 'user': {'$ref': 'users', '$id': { '$oid': ?0 } } }") public List findByUserId(String id); }

Slide 40

Slide 40 text

Configuración de los Repositorios

Slide 41

Slide 41 text

Inserción y Consulta con Repositorios /** * Ejemplo de consultas con Repositorios */ public void ejemplo3() { User user = userRepository.findByName("Alejandro Marqués"); logger.info("User found:" + user); tweetRepository.save(new Tweet("Hola mundo", new Date(), user, new double[] { 1.0, 0.1 })); List tweets = tweetRepository.findByUserId(user.getId().toStringMongod()); logger.info("Found " + tweets.size() + " tweets"); List tweetsHola = tweetRepository.findByTextLike("Hola"); logger.info("Found " + tweetsHola.size() + " tweets"); } INFO - User found:User [id=4eb84a0e1a885514a5745b2e, name=Alejandro Marqués, following=0] INFO - Found 1 tweets INFO - Found 1 tweets

Slide 42

Slide 42 text

Customizando Repositorios public interface IAdvancedTweetRepository { public List timeline(List ids); } public class AdvancedTweetRepositoryImpl implements IAdvancedTweetRepository { @Override public List timeline(List ids) { return mongoTemplate.find(new Query(new Criteria("user.$id").in(ids)), Tweet.class);} } } public interface TweetRepository extends MongoRepository, IAdvancedTweetRepository { }

Slide 43

Slide 43 text

Consultando el Timeline /** * Ejemplo de consulta timeline/repositorios customizados */ public void ejemplo4() { User user = userRepository.findByName("Marco Martinez"); List following = new LinkedList(); for (User currentFollowing : user.getFollowing()) { following.add(currentFollowing.getId()); } following.add(user.getId()); List tweets = tweetRepository.timeline(following); logger.info("Total tweets found: " + tweets.size()); }

Slide 44

Slide 44 text

Diseño de una aplicación • Diseño de documentos • Consultas de la aplicación • Optimización de las consultas

Slide 45

Slide 45 text

Índices • Son muy similares a los índices MySQL • Un índice es un B-Tree • Necesarios cuando necesitas ordenar • Para hacer un campo único

Slide 46

Slide 46 text

Índices • Optimiza el uso de memoria • Son útiles cuando las consultas devuelven parte de los documentos

Slide 47

Slide 47 text

Índices de la aplicación • db.tweet.ensureIndex({“user.$id” : 1 , “date” : -1}) • db.user.ensureIndex({“name” : 1}, {“unique” : true})

Slide 48

Slide 48 text

Índices en Spring Data /** * Creando indices */ public void ejemplo5() { IndexDefinition nameIndex = new Index().on("name", Order.ASCENDING).unique(Duplicates.DROP); mongoTemplate.ensureIndex(nameIndex, User.class); GeospatialIndex geoIndex = new GeospatialIndex("coordinates"); mongoTemplate.ensureIndex(geoIndex, Tweet.class); }

Slide 49

Slide 49 text

Temas Avanzados • Geolocalización • Trending Topics • Retweets

Slide 50

Slide 50 text

Geolocalización • Requiere un índice especial • El campo tiene que tener el formato: • {“x” : 2, “y” : 3} • [2, 3] • {“latitude” : 2, “longitude” : 3}

Slide 51

Slide 51 text

Geolocalización • db.tweet.ensureIndex({“geo” : “2d”}) • db.tweet.find({“geo” : {“$near” : [1,0]}}).limit(10) • db.tweet.find({“geo” : {“$within” : {“$center” : [{‘x’ : 0, ‘y’ : 0},5]}}})

Slide 52

Slide 52 text

Geolocalización en Spring Data /** * Busquedas Geo */ public void ejemplo6() { List tweets = mongoTemplate.find(new Query(new Criteria("coordinate") .within(new Circle(3.0, 3.0, 5))), Tweet.class); logger.info("Tweets found: " + tweets.size()); tweets = mongoTemplate.find(new Query(new Criteria("coordinate") .within(new Circle(3.0, 3.0, 1))), Tweet.class); logger.info("Tweets found: " + tweets.size()); } INFO - Tweets found: 3 INFO - Tweets found: 0

Slide 53

Slide 53 text

Trending Topics • Repeticiones de cadenas de palabras • Analizar los tweets de la última hora

Slide 54

Slide 54 text

MapReduce • “MapReduce is the Uzi of aggregation tools” • Proceso en background • Dos pasos: map y reduce ( y finalize)

Slide 55

Slide 55 text

Map • Procedimiento que se ejecuta una vez sobre cada documento. • Cada map emite 0 o más pares clave, valor

Slide 56

Slide 56 text

Reduce • Procedimiento que se ejecuta una vez por cada clave • La entrada es la clave y la lista de valores que fueron emitidas con esa clave • La salida puede ser cualquier estructura de datos

Slide 57

Slide 57 text

Finalize • Se ejecuta justo antes de guardar los resultados • Útil para construir un resultado final, borrar elementos redundantes, etc

Slide 58

Slide 58 text

Resultado • El resultado final se almacena en una colección • Documentos con el formato: { _id : clave_emitida, value: resultado_reduce }

Slide 59

Slide 59 text

Map var map = function() { var words = this.text.split(" "); for (var i = 0; i < words.length; i += 1){ for (var j = i; j < words.length; j += 1){ // Slice [inclusive, exclusive) sub_words = words.slice(i, j + 1); sub_sentence = sub_words.join(" "); emit(sub_sentence, sub_words.length * sub_words.length); } } };

Slide 60

Slide 60 text

Map Entrada: { text : “hola que tal” } Se emite: hola, 1 hola que, 4 hola que tal, 9 que tal, 4 que, 1 tal, 1

Slide 61

Slide 61 text

Reduce var reduce = function(key, emits) { var score = 0; for (var i in emits){ score += emits[i]; } return score; };

Slide 62

Slide 62 text

Ejecución Ejecución: db.tweet.mapReduce(map, reduce, {"out" : "TTs"}) Consulta: db.TTs.find().sort({value: -1}).limit(10)

Slide 63

Slide 63 text

Finalize • Reducir de palabras (preposiciones, conjunciones, etc) • Filtrado de trending topics que están contenidos en otros

Slide 64

Slide 64 text

Map Reduce Spring Data /** * Ejemplo de MapReduce */ public void ejemplo7() { MapReduceOptions opts = new MapReduceOptions(); opts.outputCollection("TT"); opts.limit(15); MapReduceResults results = mongoTemplate.mapReduce("tweets", "classpath:map.js", "classpath:reduce.js", opts, TrendingTopic.class); }

Slide 65

Slide 65 text

Map Reduce Spring Data @Document(collection = "TT") public class TT { @Id private String id; private float value; } @Repository public interface TrendingTopicRepository extends MongoRepository { } /** * Consultando resultado del MapReduce */ public void ejemplo8() { Page tt = trendingTopicRepository.findAll(new PageRequest(0, 10, new Sort(Direction.DESC, "value"))); logger.info("Trending topics found: " + tt.getNumberOfElements()); }

Slide 66

Slide 66 text

Retweets • En twitter es un poco caótico (RT, via ...) • Insertar tweet indicando que es un retweet haciendo referencia al tweet original • Modificar el tweet original para llevar una cuenta de retweets

Slide 67

Slide 67 text

Otros usos en aplicaciones • Analytics • Logs • Minería de twitter

Slide 68

Slide 68 text

¿Preguntas? ? ? ? ? ? ? ?

Slide 69

Slide 69 text

Referencias • MongoDB: The definitive guide • 50 Tips and Tricks for MongoDB Developers • Spring Data MongoDB • Código de los ejemplos Spring Data