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
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. Diseño de una aplicación
• Diseño de documentos
• Consultas de la aplicación
• Optimización de las consultas
12. Diseño de documentos
• Identificar tipos de documentos
• Separar cada tipo de documento en distintas
colecciones
• Referenciar vs Duplicar
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. Documentos
Usuario Tweet
_id
_id
text
name date
following user
geo
15. Usuarios
• name : Nombre de usuario
• following: Array de referencias a usuarios
16. Tweets
• text : texto
• date : objeto Date de javascript.
• user: referencia al usuario
• geo: coordenadas
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. 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. 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. 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. 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);
}
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. 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. 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
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. Diseño de una aplicación
• Diseño de documentos
• Consultas de la aplicación
• Optimización de las consultas
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
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. 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. 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()
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. UserRepository y TweetRepository
@Repository
public interface UserRepository extends MongoRepository<User, ObjectId> {
public User findByName(String name);
}
@Repository
public 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. Configuración de los Repositorios
<context:annotation-config />
<context:component-scan base-package="com.paradigmatecnologico" />
<mongo:repositories base-package="com.paradigmatecnologico" />
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 tweets
INFO - Found 1 tweets
42. Customizando Repositorios
public 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. 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. Diseño de una aplicación
• Diseño de documentos
• Consultas de la aplicación
• Optimización de las consultas
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. Índices
• Optimiza el uso de
memoria
• Son útiles cuando
las consultas
devuelven parte de
los documentos
47. Índices de la aplicación
• db.tweet.ensureIndex({“user.$id” : 1 , “date” : -1})
• db.user.ensureIndex({“name” : 1}, {“unique” : true})
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);
}
50. Geolocalización
• Requiere un índice especial
• El campo tiene que tener el formato:
• {“x” : 2, “y” : 3}
• [2, 3]
• {“latitude” : 2, “longitude” : 3}
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: 3
INFO - Tweets found: 0
53. Trending Topics
• Repeticiones de cadenas de palabras
• Analizar los tweets de la última hora
54. MapReduce
• “MapReduce is the Uzi of aggregation tools”
• Proceso en background
• Dos pasos: map y reduce ( y finalize)
55. Map
• Procedimiento que se ejecuta una vez sobre cada
documento.
• Cada map emite 0 o más pares clave, valor
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. Finalize
• Se ejecuta justo antes de guardar los resultados
• Útil para construir un resultado final, borrar
elementos redundantes, etc
58. Resultado
• El resultado final se almacena en una
colección
• Documentos con el formato:
{
_id : clave_emitida,
value: resultado_reduce
}
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. 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. Reduce
var reduce = function(key, emits) {
var score = 0;
for (var i in emits){
score += emits[i];
}
return score;
};
63. Finalize
• Reducir de palabras (preposiciones, conjunciones,
etc)
• Filtrado de trending topics que están contenidos
en otros
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. 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. 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. Otros usos en aplicaciones
• Analytics
• Logs
• Minería de twitter