Query con MongoDB y Java

De ChuWiki


En los ejemplos de este tutorial vamos a ver detalles sobre las posibilidades de consulta a MongoDB desde Java.

Tienes el código completo de este ejemplo en QueryExample.java, incluyendo conexión de Java con MongoDB e inserción de algunos documentos bson en la colección MongoDB. Esos documentos insertados nos servirán para hacer las consultas en los ejemplos de este tutorial. Los documentos insertados representan personas y tienen los siguientes campos: "name" (string), "email" (string), "city" (string), "height" (double), "brothers" (int), "birthday" (java.util.Date).

No detallamos aquí la parte de establecer la conexión ni hacer las inserciones. En el código sí tienes cómo hacerlo y el enlace de conexión de Java con MongDB da explicaciones de cómo hacerlo. En este tutorial nos centraremos en las consultas.

find() todos los documentos de una colección[editar]

Para obtener todos los documentos bson de una colección de MongoDB, usamos la función find() sin parámetros de nuestra instancia de collection. El resultado es un iterable que podemos recorrer con un bucle para ir obteniendo los documentos uno a uno.

System.out.println("Obtener todos los documentos de una colección.");
FindIterable<Document> documents = collection.find();
documents.forEach(document -> {
   System.out.println(document.toJson());
});

Obtener los campos del documento bson[editar]

En el código anterior hemos sacado por pantalla, usando el método toJson(), el documento bson convertido a String Json. Pero en un código más complejo necesitarmos acceder a los valores de los campos. El siguiente código saca los valores accediendo a ellos uno por uno, cada uno con su correspondiente tipo Java, según fueron insertados en MongoDB.

documents.forEach(document -> {
   System.out.println(document.getObjectId("_id"));
   System.out.println(document.getString("name"));
   System.out.println(document.getString("email"));
   System.out.println(document.getDate("birthday"));
   System.out.println(document.getDouble("height"));
   System.out.println(document.getInteger("brothers"));

Vemos que la clase Document de bson tiene métodos getString(), getDate(), getDouble(), getInteger() y varios más con diferentes tipos. En cada uno de ellos se pasa como parámetro el nombre del campo dentro del documento bson. Obtendremos una excepción si intentamos obtener un tipo incorrecto, por lo que es importante saber a priori qué tipo de campo se almacenó en la colección MongoDB. Existe un método get() que devuelve un Object de Java. Si no sabemos exactamente de qué tipo es el valor almacenado en la colección, podemos usar este método y luego usar instanceof para tratar de identificarlo.

find por ObjectId[editar]

Cuando insertamos documentos bson en MongoDB, se asigna automáticamente a cada objeto un identificador único. Este identificador se guarda en un campo "_id" por defecto y es de tipo ObjectId. Sabiendo el ObjectId, podemos usar la función find() pasando de parámetro un Filter que diga que buscamos un documento concreto: el que el campo "_id" tiene un valor (ObjectId) concreto.

System.out.println("Obtener resultado por ObjectId");
ObjectId objectId = new ObjectId("65259662f632644780cb614b");
Bson filter = Filters.eq("_id", objectId);
documents = collection.find(filter);
documents.forEach (document -> System.out.println(document));

El ObjectId es una cadena tan extraña como la mostrada en el ejemplo. Debemos saberla bien porque hemos hecho otras consultas previas para obtenerla, bien porque al hacer la inserción nos la hemos guardado.

La clase Filters tiene métodos que nos permiten construir filtros comparando campos. Algunas de las opciones:

  • eq(key,value) para indicar que un determinado campo debe ser igual (equals to) a un determinado valor. Es el caso del ejemplo, el campo "_id" debe tener un valor ObjectId("65259662f632644780cb614b")
  • neq(key,value) para indicar que un determinado campo debe ser no igual (not equals to) a un determinado valor.
  • gt(key,value) para indicar que un determinado campo debe ser mayor (greater than) un valor.
  • gte(key,value) para indicar que un determinado campo debe ser mayor o igual (greater than or equals) un valor.
  • lt(key,value) para indicar que un determinado campo debe ser menor (less than) un valor.
  • lte(key,value) para indicar que un determinado campo debe ser menor o igual (less than or equals) un valor.

y hay bastantes más tipos de condiciones: geográficas, que el valor esté dentro de un conjunto de valores, etc.

Una vez construido el filtro, nos basta con pasarlo como parámetro al método find()

find por campo[editar]

Igual que hemos hecho la comparación por ObjectId, podemos hacerla por cualquier otro campo. En el ejemplo buscamos un documento con un campo "nombre" que tenga de valor "Pedro"

System.out.println("Obtener resultado por campo");
filter = Filters.eq("name", "Pedro");
documents = collection.find(filter);
documents.forEach(document -> System.out.println(document));

Como hemos comentado, hay muchos métodos posibles para Filter. Otro interesante a la hora de consultar por campo es que el campo no exista en el documento. En nuestro ejemplo, alguna persona que no tenga el email relleno

System.out.println("Obtener resultado de campo no relleno");
filter = Filters.exists("email", false);
documents = collection.find(filter);
documents.forEach(document -> System.out.println(document));

Hemos usado Filter.exists() indicando el nombre del campo y false para decir que no exista. Con true obtendríamos los documentos que sí tienen ese campo relleno.

find por Date[editar]

Consultar por una fecha es el mismo problema. Podemos insertar en un campo instancias de java.util.Date y luego usarlo para la consulta.

Calendar date = Calendar.getInstance();
date.set(1993, 5, 3);
filter = Filters.gt("birthday", date.getTime());
documents = collection.find(filter);
documents.forEach(document -> System.out.println(document));

Hemos usado la clase Calendar para construir una fecha concreta (3 de Junio de 1993). Recuerda que en las clases Java de fechas, el mes suele ir de 0 a 11. Luego simplemente usarlo con Filter.eq() o cualquiera de las otras variantes para fechas mayores o menores que la indicada.

find con múltiples condiciones[editar]

Filter solo admite una condición. Pero tiene métodos and(), or(), nor() (OR exclusivo) y not() para construir filtros más complejos, con varias condiciones. Veamos un ejemplo con fechas

System.out.println("Obtener resultado con varias condiciones");
date = Calendar.getInstance();
date.set(1993, 3, 3);
Bson filter1 = Filters.gt("birthday", date.getTime());
date.set(1993,7,3);
Bson filter2 = Filters.lt("birthday", date.getTime());
documents = collection.find(Filters.and(filter1, filter2));
documents.forEach(document -> System.out.println(document));

Hemos obtenido con Calendar dos fechas (Date) de Abril (mes 3 según Java) y Agosto (mes 7 según Java). Las hemos metido en dos filtros (filter1 y filter2). El primero para fechas mayores (gt()) que la fecha de ABril, el segundo para fechas menores (lt()) que la de Agosto. Y combinando ambas con un and() obtendremos las personas cuyo naciemiento está entre la fecha de Abril y la de Agosto.

Estos métodos admiten un número indefinido de parámetros, no solo dos. Y si los parámetros son a su vez filtros AND, OR, NOR, podemos construir consultas muy complejas.