Páginas

quinta-feira, 22 de outubro de 2015

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

Nenhum comentário:

Postar um comentário