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()
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}
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;
};
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