Introducción a                           y Spring Data     Miguel Olivares y Leonardo Menezes        (@moliware)   (@leona...
¿Qué es MongoDB?•   Base de datos orientada a documentos•   Sin esquema•   Escalable•   DB => Colección => Documento
¿Qué es una colección?•   Es un grupo de documentos.•   Se identifica con un nombre•   Es el “equivalente” a una tabla en u...
¿Qué es un documento?•   Un conjunto de pares clave-valor•   Coloquialmente un “hash”, “map” o “dictionary”•   Formato BSON
Arquitectura
Consola•   Consola javascript completa•   Cliente MongoDB
¿Por qué MongoDB?•   Fácil escalabilidad•   Flexibilidad•   Alto rendimiento•   Consultas potentes•   MapReduce
Benchmark       (45 M de    Tiempo medio de                                     Tamaño en disco     documentos)       inse...
Introducción a Spring Data
¿Por que Spring Data?•   Mapeo/Conversión entre POJOs y docs MongoDB•   MongoTemplate•   Implementación automática de Repo...
Diseño de una aplicación•   Diseño de documentos•   Consultas de la aplicación•   Optimización de las consultas
Diseño de documentos•   Identificar tipos de documentos•   Separar cada tipo de documento en distintas    colecciones•   Re...
Diseño de documentos•       Identificar tipos de documentos•       Separar cada tipo de documento en distintas        colec...
Documentos      Usuario      Tweet                  _id      _id                  text      name        date      followin...
Usuarios•   name : Nombre de usuario•   following: Array de referencias a usuarios
Tweets•   text : texto•   date : objeto Date de javascript.•   user: referencia al usuario•   geo: coordenadas
Modelo MongoDB
Modelo Relacional
Inserción de usuarios  var miguel = {"name" : "miguel", "following" : []}  db.user.save(miguel)  var leonardomenezes = {"n...
Inserción de Tweets   var tweet = {"text" : "Tweet ejemplo", "date" : new Date(),                "user" : new DBRef("user"...
Actualización de documentos•   Update tipo SQL•   Posibilidad de upsert, es decir, si no existe lo crea•   Existen muchos ...
Actualización de documentos  El usuario miguel ahora sigue a leonardomenezes:  var ref_leo = new DBRef("user", leonardomen...
MongoDB Java Driver    public void ejemplo1() throws UnknownHostException, MongoException {	   Mongo mongo = new Mongo("lo...
MongoTemplate                Mapeo/Conversión                   de POJO´s
Mapeo de los Documentos @Document(collection = "tweets")    @Document(collection = "users") public class Tweet {          ...
Configuración de MongoTemplate @Configuration public class AppConfig {     @Bean     public Mongo mongo() throws UnknownHos...
Inserción con MongoTemplate    /**     * Ejemplo sencillo de inserción     */    private void ejemplo1() {	   User user = ...
Update Modifiers con Spring Data    /**     * Ejemplo de acutalización     */    public void ejemplo2() {	   mongoTemplate....
Diseño de una aplicación•   Diseño de documentos•   Consultas de la aplicación•   Optimización de las consultas
Consultas en MongoDB SELECT a,b FROM users                            db.users.find({}, {a:1,b:1}) SELECT * FROM users WHER...
Perfil de usuario•   Seguidores•   Mis tweets
Consultas - Seguidores SELECT user_following.following_id FROM user, user_following WHERE user.name = “leonardomenezes” AN...
Consultas - Mis Tweets  select * from tweet where tweet.user_id=X order by  date DESC  db.tweet.find({"user.$id" : X}).sort...
Más consultas MongoDBSELECT * FROM users WHERE age>33             db.users.find({age:{$gt:33}})SELECT * FROM users WHERE na...
Más operadores•   $where + código javascript•   $gt, $lt, $gte, $lte•   $all, $exists, $mod, $ne, $in, $nin, $nor, $or, $a...
Timeline SELECT * FROM tweet WHERE user_id = X OR user_id = ...... ORDER BY date db.tweet.find({"user.$id" : {$in : ids}})....
MongoRepository(DAO)•   CRUD(CrudRepository)    •   count, delete, deleteAll, exists, findOne, save...•   Paginación/Ordena...
Repositorios                                                                      count()                                 ...
UserRepository y TweetRepository@Repositorypublic interface UserRepository extends MongoRepository<User, ObjectId> {    pu...
Configuración de los Repositorios   	   <context:annotation-config />   	   <context:component-scan base-package="com.parad...
Inserción y Consulta con Repositorios    /**     * Ejemplo de consultas con Repositorios     */    public void ejemplo3() ...
Customizando Repositoriospublic interface IAdvancedTweetRepository {    public List<Tweet> timeline(List<ObjectId> ids);} ...
Consultando el Timeline    /**      * Ejemplo de consulta timeline/repositorios customizados      */    public void ejempl...
Diseño de una aplicación•   Diseño de documentos•   Consultas de la aplicación•   Optimización de las consultas
Índices •   Son muy similares a los índices MySQL •   Un índice es un B-Tree •   Necesarios cuando necesitas ordenar •   P...
Índices •   Optimiza el uso de     memoria •   Son útiles cuando     las consultas     devuelven parte de     los documentos
Índices de la aplicación •   db.tweet.ensureIndex({“user.$id” : 1 , “date” : -1}) •   db.user.ensureIndex({“name” : 1}, {“...
Índices en Spring Data    /**     * Creando indices     */    public void ejemplo5() {	   IndexDefinition nameIndex = new ...
Temas Avanzados•   Geolocalización•   Trending Topics•   Retweets
Geolocalización•   Requiere un índice especial•   El campo tiene que tener el formato:    •   {“x” : 2, “y” : 3}    •   [2...
Geolocalización•   db.tweet.ensureIndex({“geo” : “2d”})•   db.tweet.find({“geo” : {“$near” : [1,0]}}).limit(10)•   db.tweet...
Geolocalización en Spring Data    /**     * Busquedas Geo     */    public void ejemplo6() {	   List<Tweet> tweets = mongo...
Trending Topics•   Repeticiones de cadenas de palabras•   Analizar los tweets de la última hora
MapReduce•   “MapReduce is the Uzi of aggregation tools”•   Proceso en background•   Dos pasos: map y reduce ( y finalize)
Map•   Procedimiento que se ejecuta una vez sobre cada    documento.•   Cada map emite 0 o más pares clave, valor
Reduce•   Procedimiento que se ejecuta una vez por cada    clave•   La entrada es la clave y la lista de valores que    fu...
Finalize •   Se ejecuta justo antes de guardar los resultados •   Útil para construir un resultado final, borrar     elemen...
Resultado  •   El resultado final se almacena en una      colección  •   Documentos con el formato:                  {     ...
Map var map = function() {    var words = this.text.split(" ");      for (var i = 0; i < words.length; i += 1){         fo...
Map Entrada: {     text : “hola que tal” } Se emite:     hola, 1     hola que, 4     hola que tal, 9     que tal, 4     qu...
Reduce var reduce = function(key, emits) {    var score = 0;    for (var i in emits){       score += emits[i];    }    ret...
Ejecución Ejecución: db.tweet.mapReduce(map, reduce, {"out" : "TTs"}) Consulta: db.TTs.find().sort({value: -1}).limit(10)
Finalize •   Reducir de palabras (preposiciones, conjunciones,     etc) •   Filtrado de trending topics que están contenid...
Map Reduce Spring Data    /**      * Ejemplo de MapReduce      */    public void ejemplo7() {	   MapReduceOptions opts = n...
Map Reduce Spring Data    @Repository    public interface TrendingTopicRepository extends MongoRepository<TT, String> {   ...
Retweets•   En twitter es un poco caótico (RT, via ...)•   Insertar tweet indicando que es un retweet    haciendo referenc...
Otros usos en aplicaciones•   Analytics•   Logs•   Minería de twitter
¿Preguntas??        ? ??       ?    ?          ?
Referencias•   MongoDB: The definitive guide•   50 Tips and Tricks for MongoDB Developers•   Spring Data MongoDB•   Código ...
Upcoming SlideShare
Loading in …5
×

Seminario mongo db springdata 10-11-2011

1,919 views

Published on

Material del Seminario "Paradigma noSQL con MongoDB y Spring Data" impartido por Leonardo Menezes y Miguel Olivares el 10 de noviembre de 2011.

Published in: Technology, Business
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,919
On SlideShare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
39
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Seminario mongo db springdata 10-11-2011

  1. 1. Introducción a y Spring Data Miguel Olivares y Leonardo Menezes (@moliware) (@leonardomenezes)
  2. 2. ¿Qué es MongoDB?• Base de datos orientada a documentos• Sin esquema• Escalable• DB => Colección => Documento
  3. 3. ¿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
  4. 4. ¿Qué es un documento?• Un conjunto de pares clave-valor• Coloquialmente un “hash”, “map” o “dictionary”• Formato BSON
  5. 5. Arquitectura
  6. 6. Consola• Consola javascript completa• Cliente MongoDB
  7. 7. ¿Por qué MongoDB?• Fácil escalabilidad• Flexibilidad• Alto rendimiento• Consultas potentes• MapReduce
  8. 8. Benchmark (45 M de Tiempo medio de Tamaño en disco documentos) inserción SolR 0.603(ms) 49 GB CouchDB 0.297(ms) 43 GB Cassandra 0.516(ms) 50 GB MongoDB 0.040(ms) 43 GB
  9. 9. Introducción a Spring Data
  10. 10. ¿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
  11. 11. Diseño de una aplicación• Diseño de documentos• Consultas de la aplicación• Optimización de las consultas
  12. 12. Diseño de documentos• Identificar tipos de documentos• Separar cada tipo de documento en distintas colecciones• Referenciar vs Duplicar
  13. 13. Diseño de documentos• Identificar tipos de documentos• Separar cada tipo de documento en distintas colecciones• Referenciar vs Duplicar • Consistencia • Inconsistencia • Consultas extras • Documento autodescriptivo
  14. 14. Documentos Usuario Tweet _id _id text name date following user geo
  15. 15. Usuarios• name : Nombre de usuario• following: Array de referencias a usuarios
  16. 16. Tweets• text : texto• date : objeto Date de javascript.• user: referencia al usuario• geo: coordenadas
  17. 17. Modelo MongoDB
  18. 18. Modelo Relacional
  19. 19. 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)
  20. 20. 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)
  21. 21. 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
  22. 22. 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}})
  23. 23. 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); }
  24. 24. MongoTemplate Mapeo/Conversión de POJO´s
  25. 25. Mapeo de los Documentos @Document(collection = "tweets") @Document(collection = "users") public class Tweet { public class User { private ObjectId id; private ObjectId id; private String text; private String name; private Date date; @DBRef private Set<User> following; @DBRef private User user; ... getters y constructor ... private double[] coordinate; ... getters y constructor ...
  26. 26. 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"); } }
  27. 27. 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] savedINFO - Users User [id=4eb846971a8868b98ed3d7c4, name=Alejandro Marqués, following=0] savedINFO - Users User [id=4eb846971a8868b98ed3d7c5, name=Javier Alba, following=0] savedINFO - Users User [id=4eb846971a8868b98ed3d7c3, name=Marco Martinez, following=1] updated
  28. 28. 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]
  29. 29. Diseño de una aplicación• Diseño de documentos• Consultas de la aplicación• Optimización de las consultas
  30. 30. 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 db.users.find({age:33}).sort({name:1}).limit(10).skip(0) LIMIT=10 OFFSET=0
  31. 31. Perfil de usuario• Seguidores• Mis tweets
  32. 32. 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});
  33. 33. Consultas - Mis Tweets select * from tweet where tweet.user_id=X order by date DESC db.tweet.find({"user.$id" : X}).sort({date : -1})
  34. 34. Más consultas MongoDBSELECT * 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()
  35. 35. Más operadores• $where + código javascript• $gt, $lt, $gte, $lte• $all, $exists, $mod, $ne, $in, $nin, $nor, $or, $and, $size, $type .....
  36. 36. Timeline SELECT * FROM tweet WHERE user_id = X OR user_id = ...... ORDER BY date db.tweet.find({"user.$id" : {$in : ids}}).sort({date : -1})
  37. 37. MongoRepository(DAO)• CRUD(CrudRepository) • count, delete, deleteAll, exists, findOne, save...• Paginación/Ordenación(PagingAndSortingRepository)• Métodos de búsqueda dinámicos
  38. 38. Repositorios  count() findAll(Pageable pageable)          Returns the number of entities available.           Returns a Page of entities meeting the paging restriction provided in the Pageable object.  delete(ID id) findAll(Sort sort)          Deletes the entity with the given id.           Returns all entities sorted by the given options.    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. 
  39. 39. UserRepository y TweetRepository@Repositorypublic interface UserRepository extends MongoRepository<User, ObjectId> { public User findByName(String name);}@Repositorypublic interface TweetRepository extends MongoRepository<Tweet, ObjectId> { public List<Tweet> findByTextLike(String text); public List<Tweet> findByDateLessThan(Date date); @Query("{ user: {$ref: users, $id: { $oid: ?0 } } }") public List<Tweet> findByUserId(String id);}
  40. 40. Configuración de los Repositorios <context:annotation-config /> <context:component-scan base-package="com.paradigmatecnologico" /> <mongo:repositories base-package="com.paradigmatecnologico" />
  41. 41. 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<Tweet> tweets = tweetRepository.findByUserId(user.getId().toStringMongod()); logger.info("Found " + tweets.size() + " tweets"); List<Tweet> 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 tweetsINFO - Found 1 tweets
  42. 42. Customizando Repositoriospublic interface IAdvancedTweetRepository { public List<Tweet> timeline(List<ObjectId> ids);} public class AdvancedTweetRepositoryImpl implements IAdvancedTweetRepository { @Override public List<Tweet> timeline(List<ObjectId> ids) { return mongoTemplate.find(new Query(new Criteria("user.$id").in(ids)), Tweet.class);} } }public interface TweetRepository extends MongoRepository<Tweet, ObjectId>, IAdvancedTweetRepository {}
  43. 43. Consultando el Timeline /** * Ejemplo de consulta timeline/repositorios customizados */ public void ejemplo4() { User user = userRepository.findByName("Marco Martinez"); List<ObjectId> following = new LinkedList<ObjectId>(); for (User currentFollowing : user.getFollowing()) { following.add(currentFollowing.getId()); } following.add(user.getId()); List<Tweet> tweets = tweetRepository.timeline(following); logger.info("Total tweets found: " + tweets.size()); }
  44. 44. Diseño de una aplicación• Diseño de documentos• Consultas de la aplicación• Optimización de las consultas
  45. 45. Índices • Son muy similares a los índices MySQL • Un índice es un B-Tree • Necesarios cuando necesitas ordenar • Para hacer un campo único
  46. 46. Índices • Optimiza el uso de memoria • Son útiles cuando las consultas devuelven parte de los documentos
  47. 47. Índices de la aplicación • db.tweet.ensureIndex({“user.$id” : 1 , “date” : -1}) • db.user.ensureIndex({“name” : 1}, {“unique” : true})
  48. 48. Í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); }
  49. 49. Temas Avanzados• Geolocalización• Trending Topics• Retweets
  50. 50. Geolocalización• Requiere un índice especial• El campo tiene que tener el formato: • {“x” : 2, “y” : 3} • [2, 3] • {“latitude” : 2, “longitude” : 3}
  51. 51. 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]}}})
  52. 52. Geolocalización en Spring Data /** * Busquedas Geo */ public void ejemplo6() { List<Tweet> 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: 3INFO - Tweets found: 0
  53. 53. Trending Topics• Repeticiones de cadenas de palabras• Analizar los tweets de la última hora
  54. 54. MapReduce• “MapReduce is the Uzi of aggregation tools”• Proceso en background• Dos pasos: map y reduce ( y finalize)
  55. 55. Map• Procedimiento que se ejecuta una vez sobre cada documento.• Cada map emite 0 o más pares clave, valor
  56. 56. 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
  57. 57. Finalize • Se ejecuta justo antes de guardar los resultados • Útil para construir un resultado final, borrar elementos redundantes, etc
  58. 58. Resultado • El resultado final se almacena en una colección • Documentos con el formato: { _id : clave_emitida, value: resultado_reduce }
  59. 59. 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); } } };
  60. 60. Map Entrada: { text : “hola que tal” } Se emite: hola, 1 hola que, 4 hola que tal, 9 que tal, 4 que, 1 tal, 1
  61. 61. Reduce var reduce = function(key, emits) { var score = 0; for (var i in emits){ score += emits[i]; } return score; };
  62. 62. Ejecución Ejecución: db.tweet.mapReduce(map, reduce, {"out" : "TTs"}) Consulta: db.TTs.find().sort({value: -1}).limit(10)
  63. 63. Finalize • Reducir de palabras (preposiciones, conjunciones, etc) • Filtrado de trending topics que están contenidos en otros
  64. 64. Map Reduce Spring Data /** * Ejemplo de MapReduce */ public void ejemplo7() { MapReduceOptions opts = new MapReduceOptions(); opts.outputCollection("TT"); opts.limit(15); MapReduceResults<TrendingTopic> results = mongoTemplate.mapReduce("tweets", "classpath:map.js","classpath:reduce.js", opts, TrendingTopic.class); }
  65. 65. Map Reduce Spring Data @Repository public interface TrendingTopicRepository extends MongoRepository<TT, String> { } @Document(collection = "TT") public class TT { @Id private String id; private float value; } /** * Consultando resultado del MapReduce */ public void ejemplo8() { Page<TT> tt = trendingTopicRepository.findAll(new PageRequest(0, 10, new Sort(Direction.DESC, "value"))); logger.info("Trending topics found: " + tt.getNumberOfElements()); }
  66. 66. 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
  67. 67. Otros usos en aplicaciones• Analytics• Logs• Minería de twitter
  68. 68. ¿Preguntas?? ? ?? ? ? ?
  69. 69. Referencias• MongoDB: The definitive guide• 50 Tips and Tricks for MongoDB Developers• Spring Data MongoDB• Código de los ejemplos Spring Data

×