Exemplos de filtros no MongoDB (API C#)

Neste post vou demonstrar alguns exemplos simples de pesquisa no MongoDB utilizando a API .NET. Para o exemplo, eu vou considerar uma base local contendo a collection restaurants, a mesma que é usada na própria documentação do MongoDB. Além disso, vou construir uma tela Windows Forms.

A primeira coisa a ser feita é adicionar o MongoDB.Driver, e a melhor forma de fazer isso é através do Nuget. Basta selecionar a opção “Manage Nuget packages” ao se clicar com o botão direito em cima do projeto, no Solution Explorer. Como vemos na tela abaixo, basta preencher “MongoDB” na pesquisa que uma lista de pacotes é retornada. Note que se deve instalar o pacote MongoDB.Driver, tomando cuidado para não colocar a API antiga.


No nosso exemplo, faremos uma filtragem de restaurantes de acordo com vários parâmetros. Para isso, teremos uma ListBox para receber os resultados, uma combo para se escolher o filtro e um botão para executar a ação.

Esses filtros vamos carregar de forma dinâmica no exemplo, através de Reflection. Nosso critério será todos os métodos privados que sejam de instância (não sejam estáticos) e que cujo nome se inicie com “Filter”.

Para conectar na base do MongoDB, primeiro precisamos de um objeto do tipo MongoClient. Ele recebe como parâmetro a string de conexão, que no caso aponta para o MongoDB que eu tenho instalado na minha máquina. A partir desse objeto, usando o método GetDatabase nós obtemos uma referência a base que vamos trabalhar no MongoBD.

De forma a evitar repetição de código, eu criei um método genérico que recebe um objeto do tipo FilterDefinition e que obtém uma referência à collection que será pesquisada; executa a pesquisa passando o objeto FilterDefinition, obtendo uma lista através do método ToListAsync; e por fim formata essa lista resultante exibindo o nome, tipo de cozinha e localização. Este método é chamado ExecuteFilter.

O primeiro exemplo de filtro é do método FilterBrazilianRestaurants. Como o nome diz, ele faz a filtragem dos dados que trabalham com cozinha brasileira. O objeto filter é obtido a partir da classe Builders. Esse objeto possui um método Eq que cria uma cláusula de igualdade. No exemplo, serão procurados todos os registros que possuem o campo cuisine com valor Brazilian.

No método FilterBrazilianRestaurantsInManhattan as coisas começam a ficar interessantes. Veja que estamos juntando dois critérios de igualdade, com o operador &. Isso faz com que sejam pesquisados todos os registros com o campo cuisine igual a Brazilian e também todos que possuem o campo borough como Manhattan.

O terceiro método é o FilterBrazilianRestaurantsInManhattanOrDelicatessen, onde vemos um exemplo de como fazemos uma pesquisa usando o “ou”. Neste caso buscamos todos os restaurantes brasileiros em Manhattan ou todas as delicatessens. 

Até agora usamos somente o operador Eq, mas a API permite usar também operações de maior, menor, etc.

Por fim, temos o método FilterBrazilianRestaurantsNearCoord, onde vamos fazer um exemplo de pesquisa por localização. O método Near recebe um ponto (coordenada no globo terrestre), a distância máxima e mínima, e retorna todos os registros que estejam próximos considerando este critério. Mas para isso funcionar corretamente, é necessário criar um índice no MongoDB com o comando abaixo.

db.restaurants.createIndex( { "address.coord": "2dsphere" } )

Com isso, eu termino por aqui o exemplo. Até a próxima.

using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.GeoJsonObjectModel;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MongoDBAPIExample
{
    public partial class Form1 : Form
    {
        private static IMongoClient _client;
        private static IMongoDatabase _database;

        public Form1()
        {
            InitializeComponent();
            _client = new MongoClient("mongodb://localhost:27017");
            _database = _client.GetDatabase("test");
            var methods = GetType().GetTypeInfo().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
                .Where(m => m.Name.StartsWith("Filter"));
            foreach (var method in methods)
                comboBox1.Items.Add(method.Name);
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            var items = await (Task<List<string>>)GetType().GetTypeInfo()
                    .GetDeclaredMethod(comboBox1.SelectedItem.ToString())
                    .Invoke(this, null);
            listBox1.Items.Clear();
            foreach (var item in items)
            {
                listBox1.Items.Add(item);
            }
            label1.Text = string.Format("Total: {0}", items.Count);
        }

        private async Task<List<string>> ExecuteFilter(FilterDefinition<BsonDocument> filter)
        {
            var collection = _database.GetCollection<BsonDocument>("restaurants");
            var result = await collection.Find(filter).ToListAsync();
            return result.Select(i => string.Format("{0} ({1}) in {2}", i["name"], i["cuisine"], i["borough"])).ToList();
        }

        private async Task<List<string>> FilterBrazilianRestaurants()
        {
            var builder = Builders<BsonDocument>.Filter;
            var filter = builder.Eq("cuisine", "Brazilian");
            return await ExecuteFilter(filter);
        }

        private async Task<List<string>> FilterBrazilianRestaurantsInManhattan()
        {
            var builder = Builders<BsonDocument>.Filter;
            var filter = builder.Eq("cuisine", "Brazilian") & builder.Eq("borough", "Manhattan");
            return await ExecuteFilter(filter);
        }

        private async Task<List<string>> FilterBrazilianRestaurantsInManhattanOrDelicatessen()
        {
            var builder = Builders<BsonDocument>.Filter;
            var filter = (builder.Eq("cuisine", "Brazilian") & builder.Eq("borough", "Manhattan"))
                | builder.Eq("cuisine", "Delicatessen");
            return await ExecuteFilter(filter);
        }

        private async Task<List<string>> FilterBrazilianRestaurantsNearCoord()
        {
            double lat = -73.9617039999, lng = 40.6629420000;
            double maxDist = 1000, minDist = 10;
            var point = new GeoJsonPoint<geojson2dcoordinates>(new GeoJson2DCoordinates(lat, lng));
            var builder = Builders<BsonDocument>.Filter;
            var filter = builder.Eq("cuisine", "Brazilian") &
                builder.Near("address.coord", point, maxDist, minDist);
            return await ExecuteFilter(filter);
        }
    }
}


Comentários

Postagens mais visitadas deste blog

Trocando configurações padrão do Live TIM

Uma proposta de Clean Architecure com Modelo de Atores

Testes automatizados em sistemas autenticados com certificados digitais, usando Selenium e PhantomJS