Compartir a través de


.NET Framework

Creación de un motor de búsqueda Enterprise Search para .NET

Damian Zapart

Descargar el código de muestra

La aparición en los últimos años de la informática en la nube ha sido una gran ayuda tanto para las organizaciones como para los usuarios. Las organizaciones pueden conocer a sus clientes como nunca antes y tratarlos con comunicaciones personalizadas. Los usuarios pueden obtener sus datos desde prácticamente cualquier lugar, por lo que se vuelven mucho más útiles y accesibles. Se han incorporado grandes centros de datos de todo el mundo para almacenar todos esos datos. Pero Big Data implica grandes desafíos.

La conocida cita de John Naisbitt, "nos estamos ahogando en datos, pero tenemos hambre de información", en su libro, "Megatrends: Ten New Directions Transforming Our Lives"(Warner libros, 1982), describe perfectamente la situación actual en el mercado de Big Data. Las empresas son capaces de almacenar petabytes de datos, pero poder dar sentido a esos datos y permitir que se pueda buscar en ellos es mucho más difícil; en particular porque la mayoría de los almacenes de datos guardan datos de una forma no estructurada (NoSQL), en múltiples colecciones dentro de almacenes de datos de Big Data determinados o incluso de forma distribuida entre distintos almacenes. Además, hay una variedad de formatos de datos, como los documentos JSON, los archivos de Microsoft Office y otros. La búsqueda a través de una sola colección no estructurada normalmente no supone un problema, pero es mucho más difícil buscar todos los datos no estructurados en múltiples colecciones para encontrar un determinado subconjunto pequeño de resultados cuando el usuario no tiene idea donde podría estar. Es aquí donde el motor de búsqueda Enterprise Search entra en juego.

Búsqueda empresarial

Este es el reto esencial del motor de búsqueda Enterprise Search: ¿cómo puede proporcionar una organización grande con muchos orígenes de datos a los usuarios internos y externos la capacidad de buscar todos los orígenes de datos públicos de la empresa a través de una interfaz? Esa interfaz única podría ser una API, un sitio web empresarial o incluso un cuadro de texto simple con funcionalidad de autocompletar integrada. Independientemente de la interfaz que elige una compañía, debe proporcionar la capacidad para buscar a través de su universo de datos completo, que puede incluir bases de datos estructuradas y no estructuradas, documentos de intranet en diferentes formatos, otras API y otros tipos de orígenes de datos.

Dado que la búsqueda a través de varios conjuntos de datos es bastante compleja, solo hay unas pocas soluciones de motor de búsqueda Enterprise Search reconocidas, y el listón está alto. Una solución de motor de búsqueda Enterprise Search debe incluir las siguientes características:

  • Reconocimiento de contenido: conocer dónde se pueden ubicar determinados tipos de datos.
  • Indexación en tiempo real: mantener todos los datos indexados.
  • Procesamiento de contenido: conseguir que los diferentes orígenes de datos sean accesibles.

Una de las soluciones de motor de búsqueda Enterprise Search más populares y de código abierto es Elasticsearch (elasticsearch.org). Este servidor basado en Java creado a partir de Apache Lucene (lucene.apache.org) proporciona búsqueda de texto completo escalable a partir de varios orígenes de datos, con compatibilidad con JSON y una interfaz web de REST, así como alta disponibilidad, administración de conflictos y análisis en tiempo real. Visite bit.ly/1vzoUrR para ver su conjunto de características completo.

En un nivel alto, la forma en que Elasticsearch almacena los datos es muy sencilla. El elemento superior de la estructura dentro de un servidor se denomina un índice, y puede haber varios índices en el mismo almacén de datos. El propio índice es simplemente un contenedor de documentos (uno o varios) y cada documento es una colección de uno o más campos (sin estructuras definidas). Cada índice puede contener datos agregados en unidades denominadas tipos, que representan grupos lógicos de datos dentro de un índice determinado.

Puede ser útil pensar en Elasticsearch como una tabla del mundo de las bases de datos relacionales. Existe la misma correlación entre las filas y columnas de una tabla, y los documentos y campos de un índice, donde un documento corresponde a una fila y un campo a una columna. Sin embargo, con Elasticsearch, no hay ningún esquema de base de datos o estructura de datos fijos.

Como he indicado, los desarrolladores pueden comunicarse con el servidor de Elasticsearch mediante una interfaz web de REST. Lo que significa que pueden consultar índices, tipos, datos u otra información de sistema sencillamente mediante el envío de solicitudes web de REST desde un explorador o cualquier otro tipo de cliente web. A continuación se muestran algunos ejemplos de solicitudes GET:

  • Consulta de todos los índices:
           http://localhost:9200/_cat/indices/?v
  • Consulta de metadatos del índice:
           http://localhost:9200/clients/_stats
  • Consulta de todos los datos de índice:
           http://localhost:9200/clients/_search?q=*:*
  • Búsqueda del valor de un campo concreto en el índice:
           http://localhost:9200/clients/_search?q=field:value
  • Obtención de todos los datos del tipo de asignación de índice:
           http://localhost:9200/clients/orders/_search?q=*:*

Creación de una búsqueda

Para demostrar cómo crear una solución sencilla y de varios orígenes, usaré Elasticsearch 1.3.4 con documentos JSON, documentos PDF y una base de datos de SQL Server. Para comenzar, describiré brevemente el programa de instalación de Elasticsearch y mostraré cómo conectar cada origen de datos para que se puedan buscar los datos. Para simplificar las cosas, presentaré un ejemplo cercano al mundo real que utiliza orígenes de datos de la conocida empresa Contoso.

Usaré una base de datos de SQL Server 2014 con varias tablas, aunque solo usaré una de ellas, dbo.Orders. Como sugiere su nombre, se almacenan registros sobre los pedidos de los clientes de la compañía (un gran número de registros y, aun así, fáciles de administrar):

CREATE TABLE [dbo].[Orders]
(
  [Id] [int] IDENTITY(1,1) NOT NULL primary key,
  [Date] [datetime] NOT NULL,
  [ProductName] [nvarchar](100) NOT NULL,
  [Amount] [int] NOT NULL,
  [UnitPrice] [money] NOT NULL
);

También tengo un recurso compartido de red con varios documentos de la empresa organizados en una jerarquía de carpetas. Los documentos están relacionados con distintas campañas de marketing de productos que la compañía organizó en el pasado y se almacenan en varios formatos, como PDF y Microsoft Office Word. El tamaño medio de los documentos es de aproximadamente 1 MB.

Por último, tengo una API interna de la empresa que expone la información de los clientes de la compañía en formato JSON; dado que conozco la estructura de la respuesta, puedo deserializarla fácilmente en un objeto de tipo cliente. Mi objetivo es hacer que en todos los orígenes de datos se pueda buscar con el motor de Elasticsearch. Además, quiero crear un servicio web basado en Web API 2 que, internamente, realizará una consulta de la empresa a través de todos los índices mediante una llamada única al servidor Elasticsearch. (Puede obtener más información sobre Web API 2 en bit.ly/1ae6uya.) El servicio web devolverá los resultados como una lista de sugerencias con recomendaciones potenciales para el usuario final; esta lista la puede consumir más tarde un control de autocompletar incrustado en la aplicación ASP.NET MVC o cualquier otro tipo de sitio web.

Configuración

Lo primero que debo hacer es instalar al servidor de Elasticsearch. Para Windows, puede hacerse de forma automática o manual, con el mismo resultado: un servicio en ejecución de Windows que hospeda el servidor Elasticsearch. La instalación automática es muy rápida y sencilla; lo único que debe hacer es descargar y ejecutar el instalador de MSI Elasticsearch (bit.ly/12RkHDz). Lamentablemente, no hay ninguna manera de elegir una versión de Java ni, lo que es mucho más importante, una versión de Elasticsearch. El proceso de instalación manual, en cambio, requiere un poco más de esfuerzo, pero permite mucho más control sobre los componentes, por lo que es más adecuado en este caso.

La configuración manual de Elasticsearch como un servicio de Windows requiere que se lleven a cabo los siguientes pasos:

  1. Descargue e instale la versión más reciente de Java SE Runtime Environment (bit.ly/1m1oKlp).
  2. Agregue la variable de entorno llamada JAVA_HOME. Su valor será la ruta de la carpeta en la que haya instalado Java (por ejemplo, C:\Archivos de programa\Java\jre7), como se muestra en la figura 1.
  3. Descargue el archivo Elasticsearch (bit.ly/1upadla) y descomprímalo.
  4. Mueva los orígenes descomprimidos a Archivos de programa | Elasticsearch (opcional).
  5. Ejecute el símbolo del sistema como administrador e inicie service.bat con el parámetro de instalación:
           C:\Archivos de programa\Elasticsearch\elasticsearch-1.3.4\bin>service.bat install

Establecimiento de la variable de entorno Java_Home
Figura 1 Establecimiento de la variable de entorno Java_Home

Eso es todo que lo necesario para disponer del servicio de Windows en ejecución, con el servidor Elasticsearch accesible en localhost, en el puerto 9200. Ahora puedo realizar una solicitud web en la dirección URL http://localhost:9200/ a través de cualquier explorador web y obtendré una respuesta que tiene este aspecto:

{
  "status" : 200,
  "name" : "Washout",
  "version" : {
    "number" : "1.3.4",
    "build_hash" : "a70f3ccb52200f8f2c87e9c370c6597448eb3e45",
    "build_timestamp" : "2014-09-30T09:07:17Z",
    "build_snapshot" : false,
    "lucene_version" : "4.9"
  },
  "tagline" : "You Know, for Search"
}

Ahora mi instancia local de Elasticsearch está lista para usar; sin embargo, la versión sin procesar no me da la capacidad para conectar con SQL Server o ejecutar una búsqueda de texto completo a través de los archivos de datos. Para que estas características estén disponibles, también debo instalar varios complementos.

Extender Elasticsearch

Tal y como he mencionado, la versión sin procesar de Elasticsearch no permite indexar un origen de datos externo como SQL Server, Office o incluso un PDF. Para que se pueda buscar en todos estos orígenes de datos, debo instalar algunos complementos, lo cual es bastante fácil.

Mi primer objetivo es proporcionar compatibilidad con búsqueda de texto completo para los datos adjuntos. Con datos adjuntos, quiero decir una representación codificada en base64 del archivo de origen que se ha cargado en un almacén de datos Elasticsearch como un documento JSON. (Consulte bit.ly/12RGmvg para obtener más información sobre el tipo de datos adjuntos.) El complemento que necesito para este fin es la versión 2.3.2 de Mapper Attachments Type for Elasticsearch, disponible en bit.ly/1Alj8sy. Esta es la extensión de Elasticsearch que permite una búsqueda de texto completo de documentos, y se basa en el proyecto Apache Tika (tika.apache.org), que detecta y extrae el contenido del texto y los metadatos de distintos tipos de documentos y proporciona compatibilidad con los formatos de archivo que se indican en bit.ly/1qEyVmr.

Como sucede con la mayoría de complementos para Elasticsearch, esta instalación es muy sencilla y todo lo que necesito hacer es ejecutar el símbolo del sistema como administrador y ejecutar el siguiente comando:

bin>plugin --install elasticsearch/elasticsearch-mapper-attachments/2.3.2

Después de descargar y extraer el complemento, es necesario reiniciar el servicio de Windows de Elasticsearch.

Cuando lo haya hecho, es necesario configurar la compatibilidad de SQL Server. Por supuesto, también hay un complemento para esto. Se denomina JDBC River (bit.ly/12CK8Zu) y permite capturar los datos de un origen JDBC, como SQL Server, para indexar en Elasticsearch. El complemento se instala y configura con facilidad, aunque el proceso de instalación tiene tres etapas que deben completarse.

  1. En primer lugar, instalar Microsoft JDBC Driver 4.0, un proveedor de datos basados en Java para SQL Server que se puede descargar desde bit.ly/1maiM2j. Es importante recordar que hay que extraer el contenido del archivo descargado en la carpeta denominada Microsoft JDBC Driver 4.0 for SQL Server (que es necesario crear si no existe), directamente en la carpeta Archivos de programa, por lo que la ruta resultante tiene el siguiente aspecto: C:\Archivos de programa\Microsoft JDBC Driver 4.0 para SQL Server.
  2. A continuación, se debe instalar el complemento mediante el comando siguiente:
           bin> plugin --install
           jdbc --url "http://xbib.org/repository/org/xbib/elasticsearch/plugin/elasticsearch-river-jdbc/1.3.4.4/elasticsearch-river-jdbc-1.3.4.4-plugin.zip"
  3. Por último, se debe copiar el archivo SQLJDBC4.jar extraído en el primer paso (C:\Archivos de programa\Microsoft JDBC DRIVER 4.0 para SQL Server\sqljdbc_4.0\enu\SQLJDBC4.jar) en la carpeta lib en el directorio Elasticsearch (C:\Archivos de programa\Elasticsearch\lib). Cuando esté listo, hay que reiniciar el servicio de Windows.

Ya se han instalado todos los complementos necesarios. Sin embargo, para comprobar que las instalaciones han terminado correctamente, se debe enviar el siguiente comando como una solicitud GET de HTTP:

http://localhost:9200/_nodes/_all/plugins

En la respuesta, deben aparecer mostrarse los dos complementos instalados, como se muestra en la figura 2.

Los complementos instalados
Figura 2 Los complementos instalados

Configuración de SQL Server

Para utilizar JDBC River con SQL Server, debe poderse acceder a la instancia de SQL Server a través de TCP/IP, que está deshabilitado de forma predeterminada,. Sin embargo, es sencillo habilitarlo, lo único que hay que hacer es abrir el Administrador de configuración de SQL Server y, en la configuración de red de SQL Server, debe cambiarse el valor de Status a Enable para el protocolo TCP/IP de la instancia de SQL Server a la que se desea conectarse, como se muestra en la figura 3. Después de hacer esto, se debe poder iniciar sesión en la instancia de SQL Server mediante Management Studio con localhost 1433 como el nombre del servidor (el puerto 1433 es el puerto predeterminado para tener acceso a SQL Server a través de TCP/IP).

Habilitación de TCP/IP para la instancia de SQL Server
Figura 3 Habilitación de TCP/IP para la instancia de SQL Server

Conseguir que se pueda buscar en los orígenes

Todos los complementos necesarios se han instalado, ahora es el momento de cargar los datos. Como se mencionó anteriormente, tengo tres orígenes de datos diferentes (documentos JSON, archivos y una tabla de pedidos de una base de datos de SQL Server) que deseo tener indexados en Elasticsearch. Puedo indexar estos orígenes de datos de muchas maneras diferentes, pero me gustaría mostrar lo fácil que es mediante el uso de una aplicación basada en .NET que he implementado. Por lo tanto, como una condición previa para la indexación, tengo que instalar una biblioteca externa denominada NEST (bit.ly/1vZjtCf) para mi proyecto, que es solo un contenedor administrado alrededor de una interfaz web de Elasticsearch. Como que esta biblioteca está disponible en NuGet, para que forme parte de mi proyecto, solo debo ejecutar un comando único en la consola del administrador de paquetes:

PM> Install-Package NEST

Ahora, con la biblioteca NEST disponible en mi solución, puedo crear un nuevo proyecto de biblioteca de clases denominado ElasticSearchRepository. He usado este nombre porque he decidido mantener todas las llamadas a funciones de la clase ElasticClient (que forma parte de la biblioteca NEST) independientes del resto de la solución. Al hacerlo, el proyecto se vuelve parecido al patrón de diseño del repositorio que se aplica ampliamente en las aplicaciones basadas en Entity Framework, por lo que debería ser fácil de entender. Además, en este proyecto solo tengo tres clases: la clase BaseRepository, que inicializa y expone las clases heredadas y la instancia de ElasticClient, y otras dos clases del repositorio:

  • IndexRepository: una clase de lectura y escritura que usaré para manipular índices, establecer asignaciones y cargar documentos.
  • DiscoveryRepository: una clase de solo lectura que usaré durante las operaciones de búsqueda basadas en API.

En la figura 4 se muestra la estructura de la clase BaseRepository con una propiedad protegida de tipo ElasticClient. Este tipo, que se proporciona como parte de la biblioteca NEST, centraliza la comunicación entre el servidor de Elasticsearch y la aplicación cliente. Para crear una instancia de él, puedo pasar una dirección URL al servidor Elasticsearch, que paso como un parámetro de constructor de clase opcional. Si el parámetro es nulo, se usará un valor predeterminado de http://localhost:9200.

Figura 4 La clase BaseRepository

namespace ElasticSearchRepository
{
  using System;
  using Nest;
  public class BaseRepository
  {
    protected ElasticClient client;
    public BaseRepository(Uri elastiSearchServerUrl = null)
    {
      this.client = elastiSearchServerUrl != null ?
        new ElasticClient(new ConnectionSettings(elastiSearchServerUrl)) :
        : new ElasticClient();
    }
  }
}

Con el cliente listo, primero deberá indexar los datos del cliente; este es el escenario más sencillo porque no se requieren complementos adicionales:

public class Client
{
  public int Id { get; set; }
  public string Name { get; set; }
  public string Surname { get; set; }
  public string Email { get; set; }
}

Para indexar este tipo de datos, llamo a la instancia de mi cliente Elasticsearch, así como a la función Index<T>, donde T es el tipo de la clase de mi cliente, que representa los datos serializados devueltos desde la API. Esta función genérica tiene tres parámetros: la instancia de la clase de objeto T, un nombre de índice de destino y un nombre de asignación en el índice:

public bool IndexData<T>(T data, string indexName =
  null, string mappingType = null)
  where T : class, new()
  {
    if (client == null)
    {
      throw new ArgumentNullException("data");
    }
    var result = this.client.Index<T>(data,
      c => c.Index(indexName).Type(mappingType));
    return result.IsValid;
  }

Los dos últimos parámetros son opcionales porque NEST aplicará la lógica predeterminada para la creación de un nombre de índice de destino basado en el tipo genérico.

Ahora deseo indexar los documentos de marketing, que están relacionados con los productos de la empresa. Como tengo estos archivos almacenados en un recurso compartido de red, puedo incluir información sobre cada documento concreto en una clase simple de MarketingDocument. Es importante mencionar aquí que si deseo que un documento se indexe en Elasticsearch, debo cargarlo como una cadena codificada en Base64:

public class MarketingDocument
{
  public int Id { get; set; }
  public string Title { get; set; }
  public string ProductName { get; set; }
  // Base64-encoded file content.
  public string Document { get; set; }
}

La clase está lista, así que puedo usar ElasticClient para marcar un campo determinado en mi clase MarketingDocument como datos adjuntos. Puedo conseguirlo mediante la creación de un índice nuevo llamado "products" y agregarle una nueva asignación de marketing (por motivos de simplicidad, el nombre de clase será el nombre de asignación):

private void CreateMarketingIndex()
  {
    client.CreateIndex("products", c =>
      c.AddMapping<Marketing>
      (m => m.Properties(ps =>ps.Attachment(a =>
            a.Name(o =>o.Document).TitleField(t =>
            t.Name(x => x.Name)
            TermVector(TermVectorOption.WithPositionsOffsets)
        )))));
  }

Ahora que tengo tanto tipo .NET como la asignación de los datos de marketing, así como la definición de los datos adjuntos, puedo empezar la indexación de los archivos de la misma manera que he indexado los datos de cliente:

var documents = GetMarketingDocumentsMock();
documents.ForEach((document) =>
{
  indexRepository.IndexData<MarketingDocument>(document, "marketing");
});

El paso final es la configuración de JDBC River en Elasticsearch. Lamentablemente, NEST aún no es compatible con JDBC River. En teoría, puedo crear una asignación de JDBC River con una función sin formato para enviar una solicitud sin procesar con JSON, pero no quiero complicar las cosas en exceso. Por tanto, para completar el proceso de creación de la asignación, especificaré los parámetros siguientes:

  • Una cadena de conexión a la base de datos de SQL Server
  • Una consulta SQL, que se usará para consultar los datos
  • Programación de la actualización
  • Nombre y tipo del índice de destino (opcional)

(Encontrará una lista completa de los parámetros configurables en bit.ly/12CK8Zu.)

Para crear una nueva asignación de JDBC River, tengo que enviar una solicitud PUT con un cuerpo de la solicitud especificado a la dirección URL siguiente:

http://localhost:9200/_river/{river_name}/_meta

En el ejemplo de la figura 5, pongo un cuerpo de solicitud para crear una nueva asignación de JDBC River, que se conecta a la base de datos de Contoso alojada en la instancia local de SQL Server, que está accesible en el puerto 1433 a través de TCP/IP.

Figura 5 solicitud PUT de HTTP para crear una nueva asignación de JDBC River

PUT http://localhost:9200/_river/orders_river/_meta
{
"type":"jdbc",
"jdbc":
  {
  "driver": "com.microsoft.sqlserver.jdbc.SQLServerDriver",
  "url":"jdbc:sqlserver://127.0.0.1:1433;databaseName=Contoso",
  "user":"elastic",
  "password":"asd",
  "sql":"SELECT *  FROM dbo.Orders",
  "index" : "clients",
  "type" : "orders",
  "schedule": "0/30 0-59 0-23 ? * *"
  }
}

Usa el inicio de sesión "elastic" y la contraseña "asd" para autenticar al usuario y ejecutar el siguiente comando SQL:

SELECT * FROM dbo.Orders

Cada fila de datos que devuelve esta consulta SQL se indexará en el índice de clientes de los pedidos del tipo de asignación y la indexación se llevará a cabo cada 30 segundos (representados en la notación de Cron, consulte bit.ly/1hCcmnN para obtener más información).

Cuando este proceso finalice, aparecerá en el archivo de registro de Elasticsearch (/logs/ elasticsearch.log) información similar a la siguiente:

[2014-10-2418:39:52,190][INFO][river.jdbc.RiverMetrics]
pipeline org.xbib.elasticsearch.plugin.jdbc.RiverPipeline@70f0a80d
complete: river jdbc/orders_river metrics: 34553 rows, 6.229481683638776 mean, 
  (0.0 0.0 0.0), ingest metrics: elapsed 2 seconds, 
  364432.0 bytes bytes, 1438.0 bytes avg, 0.1 MB/s

Si ocurre algún problema con la configuración de river, el mensaje de error también estará en el registro.

Búsqueda de los datos

Cuando todos los datos se hayan indexado en el motor de Elasticsearch, se puede empezar a realizar consultas. Por supuesto, puedo enviar solicitudes sencillas al servidor Elasticsearch para consulta uno o varios índices y tipos de asignación al mismo tiempo, pero quiero crear algo más útil y más parecido a un caso real. Así que voy a dividir el proyecto en tres componentes distintos. El primer componente, que ya he presentado, es Elasticsearch, que está disponible a través de http://localhost:9200/. El segundo componente es una API que voy a crear con la tecnología Web API 2. El último componente es una aplicación de consola que usaré para configurar mis índices en Elasticsearch, así como para llenarlos con datos.

Para crear el nuevo proyecto Web API 2, en primer lugar necesito crear un proyecto de aplicación web de ASP.NET vacío y, a continuación, desde la consola del administrador de paquetes, ejecutar el siguiente comando de instalación:

Install-Package Microsoft.AspNet.WebApi

Una vez creado el proyecto, el siguiente paso es agregar un nuevo controlador, que usaré para procesar solicitudes de consulta desde el cliente y pasarlas a Elasticsearch. Para agregar un nuevo controlador denominado DiscoveryController solo es necesario agregar un nuevo elemento, una clase ApiController de Web API (v2.1). Y tengo que implementar una función Search, que se expondrá a través de la dirección URL: http://website/api/discovery/search?searchTerm=user_input:

[RoutePrefix("api/discovery")]
public class DiscoveryController : ApiController
{
  [HttpGet]
  [ActionName("search")]
  public IHttpActionResult Search(string searchTerm)
  {
    var discoveryRepository = new DiscoveryRepository();
    var result = discoveryRepository.Search(searchTerm);
    return this.Ok(result);
  }
}

Si el motor de Web API 2 no puede serializar una respuesta por un bucle que hace referencia a sí mismo, se tendrá que agregar lo siguiente en el archivo WebApiConfig.cs, que se encuentra en la carpeta AppStart:

GlobalConfiguration.Configuration
.Formatters
.JsonFormatter
.SerializerSettings
.ReferenceLoopHandling =
      ReferenceLoopHandling.Ignore;

Como se muestra en la figura 6, en el cuerpo del controlador que he creado, he instanciado una clase de tipo DiscoveryRepository, que es simplemente un contenedor alrededor del tipo ElasticClient desde la biblioteca NEST. Dentro de este repositorio no genérico de solo lectura, he implementado dos tipos de funciones de búsqueda y ambas devuelven un tipo dinámico. Esta parte es importante porque al hacerlo en los cuerpos de ambas funciones, no limito las consultas a un índice; en su lugar, se consultan al mismo tiempo todos los índices y todos los tipos. Esto significa que los resultados tendrán una estructura diferente (será de distintos tipos). La única diferencia entre las funciones es el método de consulta. En la primera función simplemente uso un método QueryString (bit.ly/1mQEEg7), que es una búsqueda de coincidencia exacta, y en el segundo, un método parcial (bit.ly/1uCk7Ba), que realiza una búsqueda aproximada a través de índices.

Figura 6 Implementación de dos tipos de búsqueda

namespace ElasticSearchRepository
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  public class DiscoveryRepository : BaseRepository
  {
    public DiscoveryRepository(Uri elastiSearchServerUrl = null)
      : base(elastiSearchServerUrl)
    {
    }
    ///<summary>  
    public List<Tuple<string, string>> SearchAll(string queryTerm)
    {
      var queryResult=this.client.Search<dynamic>(d =>
        d.AllIndices()
        .AllTypes()
        .QueryString(queryTerm));
      return queryResult
        .Hits
        .Select(c => new Tuple<string, string>(
          c.Indexc.Source.Name.Value))
        .Distinct()
        .ToList();
     }
     ///<summary>  
     public dynamic FuzzySearch(string queryTerm)
     {
       return this.client.Search<dynamic>(d =>
         d.AllIndices()
         .AllTypes()
         .Query(q => q.Fuzzy(f =>
           f.Value(queryTerm))));
     }
  }
}

Ahora que mi API está lista, puedo ejecutarla y comenzar a probarla simplemente enviando solicitudes GET a http://website:port/api/discovery/search?searchTerm=user_input, y pasar entradas de los usuarios como el valor del parámetro de consulta searchTerm. Por eso, en la figura 7 se muestran los resultados que genera mi API para el término de búsqueda "scrum". Como he destacado en la captura de pantalla, una función de búsqueda realiza una consulta sobre todos los índices de los almacenes de datos y devuelve las coincidencias de varios índices al mismo tiempo.

Habilitación de TCP/IP para la instancia de SQL Server
Figura 7 Resultados de búsqueda de API para el término "scrum"

Mediante la implementación de la capa de API, he creado la posibilidad de implementar varios clientes (como un sitio web o aplicación móvil), que podrán consumirla. Esto proporciona la capacidad de ofrecer funcionalidad de motor de búsqueda Enterprise Search para los usuarios finales. Puede encontrar un ejemplo de implementación del control de autocompletar para un cliente web basado en ASP.NET MVC 4 en mi blog, en bit.ly/1yThHiZ.

Resumen

El Big Data ha contribuido mucho al mercado de la tecnología, en cuanto a oportunidades y desafíos. Uno de los desafíos, que también es potencialmente una gran oportunidad, es la posibilidad de implementar una búsqueda rápida en petabytes de datos sin tener que conocer la ubicación exacta de los datos en el universo de datos. En este artículo he explicado cómo implementar el motor de búsqueda Enterprise Search y he mostrado cómo hacerlo en .NET Framework en combinación con las bibliotecas Elasticsearch y NEST.


Damian Zapart es responsable de desarrollo en Citigroup Inc. y se centra principalmente en soluciones empresariales. Al mismo tiempo es un experto en programación al que le interesan las tecnologías de vanguardia, los patrones de diseño y el Big Data. Visite su blog en bit.ly/1suoKN0 para obtener más información sobre él.

Gracias a los siguientes expertos técnicos por revisar este artículo: Evren Onem (D&B) y Bruno Terkaly (Microsoft)
Evren Onem (d.&B) es ingeniero de software en D&B. Su trabajo consiste en diseñar y crear las API de REST masivamente escalables. Durante la noche también se dedica a investigar sobre redes ad hoc mediante radios cognitivas.

Bruno Terkaly es un ingeniero de software principal de Microsoft cuyo objetivo es hacer posible el desarrollo de aplicaciones y servicios líderes del sector entre dispositivos. Es responsable de impulsar las principales oportunidades móviles y de la nube en todo Estados Unidos y más allá, a partir de una perspectiva que haga posible la tecnología. Ayuda a los socios a llevar sus aplicaciones al mercado al proporcionarles orientación sobre arquitectura y un profundo compromiso técnico durante la evaluación, el desarrollo y la implementación de ISV. También trabaja en estrecha colaboración con los grupos de ingeniería móvil y de nube, a los que proporciona comentarios e influye en el plan.