Consultas com QueryBuilder MongoDB
O driver-mongo-java do banco de dados MongoDB, fornece uma classe muito interessante para montar as consultas a banco. Esta classe é a QueryBuilder
. Para quem já trabalhou com Hibernate, conhece a diferença entre uma consulta em HQL e uma consulta usando a API Criteria. A QueryBuilder
do MongoDB é semelhante a API Criteria, porque ela possui métodos direcionados a consulta. Por exemplo, se você deseja fazer uma consulta usando o operador $and
, basta chamar o método and()
, se pretende usar o operador $gt
, basta então usar o método greaterThan()
e assim por diante. Neste tutorial será demonstrado como usar a classe QueryBuilder
e alguns de seus métodos.
1. Preparando o projeto
Para este tutorial vamos usar a versão 2.10.1 do driver MongoDB para Java:
- Mongo-driver-java – Versão 2.10.1: https://github.com/mongodb/mongo-java-driver/downloads.
2. Iniciando o projeto
Os exemplos de consultas serão executados na coleção contatos
, que será disponibilizada para download junto com o projeto. Seus documentos serão semelhantes ao seguinte documento:
{ '_id' : '03', 'nome' : 'Joao Carlos Rios Ruzinsky', 'idade' : 28, 'tarefas' : [ 'Futebol', 'Basquete' ] }
Veja na Listagem 1 a classe Contato
que representará o documento no projeto Java.
package com.wp.mb.tutorial.entity; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * http://www.mballem.com/ */ public class Contato implements Serializable { private String id; private String nome; private Integer idade; private Listtarefas; //gere os métodos get/set @Override public String toString() { return "Contato{" + "id='" + id + ''' + ", nome='" + nome + ''' + ", idade='" + idade + ''' + ", tarefas=" + tarefas + '}'; } }
Em seguida teremos a classe ContatoConverter
, responsável em converter o resultado da consulta em um objeto Java do tipo Contato
, veja na Listagem 2.
package com.wp.mb.tutorial.converter; import com.mongodb.DBObject; import com.wp.mb.tutorial.entity.Contato; import java.util.List; /** * http://www.mballem.com/ */ public class ContatoConverter { public static Contato converterToContato(DBObject dbo) { Contato contato = new Contato(); contato.setId((String) dbo.get("_id")); contato.setNome((String) dbo.get("nome")); contato.setIdade((Integer) dbo.get("idade")); contato.setTarefas((List) dbo.get("tarefas")); return contato; } }
Na Listagem 3 temos a interface IContatoDao
, que possui apenas um método de consulta. Este método tem como retorno uma lista de contatos.
package com.wp.mb.tutorial.dao; import com.mongodb.DBObject; import java.io.Serializable; import java.util.List; /** * http://www.mballem.com/ */ public interface IContatoDao{ List findContatos(DBObject dbo); }
A implementação da interface IContatoDao
fica por conta da classe ContatoDao
na Listagem 4. O método findContatos()
recebe um objeto do tipo DBObject
(nativo do mongo-driver) como argumento, o qual conterá a consulta montada através da QueryBuilder
.
package com.wp.mb.tutorial.dao; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.wp.mb.tutorial.converter.ContatoConverter; import com.wp.mb.tutorial.entity.Contato; import java.util.ArrayList; import java.util.List; /** * http://www.mballem.com/ */ public class ContatoDao implements IContatoDao{ private DBCollection db; public ContatoDao() { this.db = MongoConnection.getInstance().getDB().getCollection("contatos"); } public List findContatos(DBObject dbo) { List contatos = new ArrayList (); DBCursor cursor = db.find(dbo); while (cursor.hasNext()) { contatos.add(ContatoConverter.converterToContato(cursor.next())); } return contatos; } }
Na Listagem 5 é possível conferir a classe de conexão com o MongoDB, chamada MongoConnection
. Esta classe foi construída com base no padrão Singleton, o qual tem como objetivo fornecer uma única instancia da classe durante todo o ciclo de vida da aplicação. O acesso a esta instancia fica por conta de um método estático chamado getInstance()
. O nome do banco de dados será: db-contato
.
package com.wp.mb.tutorial.dao; import com.mongodb.DB; import com.mongodb.MongoClient; import java.net.UnknownHostException; /** * http://www.mballem.com/ */ public class MongoConnection { private static final String HOST = "localhost"; private static final int PORT = 27017; private static final String DB_NAME = "db-contato"; private static MongoConnection uniqInstance; private static int mongoInstance = 1; private MongoClient mongo; private DB db; private MongoConnection() { //construtor privado } //garante sempre uma unica instancia da classe public static synchronized MongoConnection getInstance() { if (uniqInstance == null) { uniqInstance = new MongoConnection(); } return uniqInstance; } //garante um unico objeto mongo public DB getDB() { if (mongo == null) { try { mongo = new MongoClient(HOST, PORT); db = mongo.getDB(DB_NAME); System.out.println("Mongo instance equals :> " + mongoInstance++); } catch (UnknownHostException e) { e.printStackTrace(); } } return db; } }
3. Implementando as consultas com QueryBuilder
A primeira consulta que vamos testar será baseada em uma busca por nome, veja na Listagem 6. Devemos criar um objeto do tipo DBObject
que receberá a consulta QueryBuilder
. O primeiro método a ser usado é o start()
que indica por qual chave será a consulta. O método is()
recebe o parametro da consulta, no caso o nome do contato. Este método seria equivalente ao like
do SQL. Já método get()
transforma a consulta em um objeto DBObject
. Em seguida fazemos uma chamada ao método findContatos()
, da interface IUserDao
, e passamos como par metro a variável query
, que contem a consulta.
public class ContatoTeste { private IContatoDaodao = new ContatoDao(); public static void main(String[] args) { ContatoTeste t = new ContatoTeste(); t.findByName(); //t.findByNameAndIdade(); //t.findByTarefas(); } private void findByName() { DBObject query = QueryBuilder .start("nome") .is("Bruna de Melo Pereira") .get(); List contatos = dao.findContatos(query); for (Contato contato : contatos) { System.out.println(contato.toString()); } } }
Contato{id='44', nome='Bruna de Melo Pereira', idade='25', tarefas=[ "Musica"]}
A próxima consulta (Listagem 7) a ser analisada usará os métodos greaterThanEquals()
– maior igual que – o método lessThanEquals()
– menor igual que – e o método and()
. Veja que o método and()
é seguido por um método regex()
, que possui a função de expressão regular. Esta consulta então irá selecionar todos os contatos entre ‘25’ e ‘30’ de idade, e que contenham como parte do nome o sobrenome ‘Souza’.
private void findByNomeAndIdade() { DBObject query = QueryBuilder.start("idade") .greaterThanEquals(25) .lessThanEquals(30) .and("nome").regex(Pattern.compile("Souza")) .get(); Listcontatos = dao.findContatos(query); for (Contato contato : contatos) { System.out.println(contato.toString()); } }
Contato{id='04', nome='Ana Carla de Souza', idade='25', tarefas=[ "Culinaria" , "Filmes"]} Contato{id='07', nome='Joao Antonio de Souza', idade='25', tarefas=[ "Veterinaria" , "Voo Livre"]} Contato{id='18', nome='Marus Daniel Souza e Castro', idade='26', tarefas=[ "Cinema" , "Teatro"]} Contato{id='23', nome='Miguel Antunes de Souza', idade='29', tarefas=[ "Poker" , "Bilhar"]} Contato{id='32', nome='Milton Onorio de Souza', idade='27', tarefas=[ "Pescaria" , "Surf"]}
Na Listagem 8 vamos trabalhar com o campo tarefas
do documento, que é um campo do tipo array. No MongoDB temos três possibilidades de consulta sobre um campo do tipo array, veremos a seguir a diferença entre as três.
Na primeira consulta, referente ao objeto is
, foi informado como parametros um array contendo duas tarefas. O retorno dessa consulta será documento(s) que tenham no campo tarefas a seqüência exata de tarefas passadas como parametro. Ou seja, “Teatro e Cinema”. Se existir um documento que contenha “Cinema, Teatro” ou “Teatro” ou “Cinema” ou “Teatro, Novela, Cinema”, estes não serão retornar, porque não existe a seqüência informada como parametro.
A próxima consulta, do objeto in
, irá retornar todos os documentos que contenham as tarefas “Teatro” E/OU “Cinema”.
A última consulta, do objeto all
, irá retornar apenas os documentos que contenham “Teatro” e “Cinema”, independentemente da ordem que eles se encontrem nos documentos.
private void findByTarefas() { // Busca Apenas quando houver a sequencia Teatro e Cinema DBObject is = QueryBuilder.start("tarefas") .is( new String[] {"Teatro", "Cinema"} ).get(); Listcontatos = dao.findContatos(is); for (Contato contato : contatos) { System.out.println("is : " + contato.toString()); } // Busca Teatro E/Ou Cinema DBObject in = QueryBuilder.start("tarefas") .in( new String[] {"Teatro", "Cinema"} ).get(); contatos = dao.findContatos(in); for (Contato contato : contatos) { System.out.println("in : " + contato.toString()); } // Busca Teatro E Cinema DBObject all = QueryBuilder.start("tarefas") .all( new String[] {"Teatro", "Cinema"} ).get(); contatos = dao.findContatos(all); for (Contato contato : contatos) { System.out.println("all : " + contato.toString()); } }
Saída no console:
is : Contato{id='35', nome='Marta Fill Nunes', idade='23', tarefas=[ "Teatro" , "Cinema"]} in : Contato{id='08', nome='Mario Nelson Nunes', idade='26', tarefas=[ "Teatro"]} in : Contato{id='12', nome='Nicole Rios da Silva', idade='22', tarefas=[ "Voleibol" , "Cinema"]} in : Contato{id='14', nome='Bruna Roque Santos', idade='24', tarefas=[ "Cinema" , "Novelas" , "Teatro"]} in : Contato{id='18', nome='Marus Daniel Souza e Castro', idade='26', tarefas=[ "Cinema" , "Teatro"]} in : Contato{id='27', nome='Crisitina Oliveira', idade='33', tarefas=[ "Medicina" , "Cinema"]} in : Contato{id='30', nome='Antonia Cristina Mel', idade='34', tarefas=[ "Cinema" , "Teatro"]} in : Contato{id='35', nome='Marta Fill Nunes', idade='23', tarefas=[ "Teatro" , "Cinema"]} in : Contato{id='40', nome='Lisiane de Almeida', idade='21', tarefas=[ "Artes" , "Teatro"]} all : Contato{id='14', nome='Bruna Roque Santos', idade='24', tarefas=[ "Cinema" , "Novelas" , "Teatro"]} all : Contato{id='18', nome='Marus Daniel Souza e Castro', idade='26', tarefas=[ "Cinema" , "Teatro"]} all : Contato{id='30', nome='Antonia Cristina Mel', idade='34', tarefas=[ "Cinema" , "Teatro"]} all : Contato{id='35', nome='Marta Fill Nunes', idade='23', tarefas=[ "Teatro" , "Cinema"]}
4. Importando a coleção contatos
A coleção contatos
está disponível dentro da pasta file
, que se encontra com o projeto. O nome do arquivo a ser importado é contatos.json
. Para importar esta coleção inicialize o MongoDB e em seguida, em uma nova janela do console use o seguinte comando:
C:mongo>mongoimport -d db-contato -c contatos < contatos.json
O resultado deverá informar que foram importados 46 documentos, como este:
connected to: 127.0.0.1 Mon Jan 07 18:21:59 imported 46 objects
Saiba mais em: