LinqToSolr — используем LINQ для получения данных из Solr

от автора

image
В силу того, что в нашей компании в качестве платформы полнотекстового поиска выбор пал на Solr, возникло сильное желание упростить работу с запросами к Solr через использование LINQ выражений.

Перешерстив интернет на наличие альтернатив, я пришел к выводу, что на данный момент необходимой мне библиотеки в общем доступе нет. Максимум, что удалось найти, это очень частичную реализацию запросов в Solr.NET (и скептический комментарий самого автора).

Результатом стала маленькая библиотека LinqToSolr, которая содержит в себе реализацию интерфейса IQueriable<> с возможностью конвертации запросов в понятный Solr API и обратно.

Реализованные методы Enumerable

На данный момент доступны реализации следующих методов:

  • Where
  • First
  • FirstOrDefault
  • Select
  • GroupBy
  • GroupByFacets — дополнительный метод для работы с Facets
  • Take
  • Skip
  • OrderBy
  • ThenBy
  • OrderByDescending
  • ThenByDescending

Конфигурация

Итак, для начала необходимо сконфигурировать подключение и настроить соотношение нашей модели данных к индексам Solr.

Начнем с конфигурации, но сперва определим представление документа Solr в виде обычного класса:

public class MyProduct{          [JsonProperty("ProductId")]  	 public int Id{get;set;}  	 public string Name{get;set}   	 public string Group{get;set}          public double Price {get;set;}          public bool IsDeleted{get;set} } 

Выше представлен класс, описывающий индекс Solr, в котором есть типизированные поля Id, Name, Group, Price и IsDeleted. При сильном желании, можно воспользоваться свойством JsonProperty, точбы переопределить названия полей (на примере поля Id).

Созданим конфигурацию:

var config = new LinqToSolrRequestConfiguration("http://localhost:1433/") .MapIndexFor<MyProduct>("MyProductIndex"); 

Что мы здесь видим? Во-первых, мы предоставляем адрес к Solr.
Далее, мы указываем проекцию наших классов к индексам Солар. Естественно предположить, что мы можем указывать сколько угодно классов к разным индексам. Единственное условие — один класс должен соотноситься с единственным индексом. Минимальная конфигурация готова.

Инициализируем сам сервис

var service = new LinqToSolrService(config); 

Все. Мы выполнили все условия, чтобы начать использовать Linq к Solr.

Примеры использования

Метод FirstOrDefault

service.AsQueriable<MyProduct>().FirstOrDefalult(x=> x.Id == 1); 

Метод Where

Выбираем все документы по группе

service.AsQueriable<MyProduct>().Where(x=>x.Group == "Group1").ToList(); 

Пример реализации использования функций внутри linq запросов

service.AsQueriable<MyProduct>().Where(x=>x.Group.Contains("roup")).ToList();  service.AsQueriable<MyProduct>().Where(x=>x.Group.StartsWith("Gro")).ToList();  service.AsQueriable<MyProduct>().Where(x=>x.Group.EndsWith("up1")).ToList(); 

Пример поиска в массиве

var groupsArr= new[] { "Group1", "Group2", "Group3" }; service.AsQueriable<MyProduct>().Where(x=> groupsArr.Contains(x.Group)).ToList(); 

Пример выборки с «больше-меньше»

service.AsQueriable<MyProduct>().Where(x=> x.Price >= 500 && x.Price < 1000).ToList(); 

Используем несколько Where

service.AsQueriable<MyProduct>() .Where(x=> !x.IsDeleted) .Where(x=>x.Name.Contains("somepartofthename")) .ToList(); 

Сортируем документы

service.AsQueriable<MyProduct>() .Where(x=> !x.IsDeleted) .OrderByDescending(x=> x.Group) // DESC .ThenBy(x=>x.Name) // ASC .ToList(); 

Выбираем определенное кол-во

service.AsQueriable<MyProduct>().Where(x=> !x.IsDeleted).Take(100).Skip(400).ToList(); 

Метод Select

//Выбрать одно поле service.AsQueriable<MyProduct>().Where(x=> !x.IsDeleted).Select(x=> x.Name).ToList();  //Выбрать несколько полей в dynamic-объект service.AsQueriable<MyProduct>().Where(x=> !x.IsDeleted).Select(x=> new {x.Name, x.Group}).ToList(); 

Работа с Facets

service.AsQueriable<MyProduct>().Where(x=> !x.IsDeleted).GroupByFacets(x=>x.Name, x=>x.Group).ToList(); 

В примере выше мы запрашиваем Solr вернуть нам 2 группы — Name и Group.
Также можно использовать GroupBy метод. Он же в свою очередь вернет похожий вариант, но и добавит сгруппированные документы. Что лучше использовать — выбирать вам. Facets более быстрый, но нужно сделать 2 запрос к серверу, чтобы получить список документов. GroupBy — работает медленнее, так как возвращает, помимо групп, и сами документы уже отсортированные по группам.

Отладка и проверка

В любом случае взникает необходимость проверить запрос и ответ. Это можно сделать с помощью встроенного в сервис объекта LastResponse. Фактически, это представление ответа сервера Solr. Плюс, там же расположен Url запроса (LastRequestUrl), который можно использовать в брауезере, чтобы проверить, что на самом деле возвращает Solr.

Сервис

Естественно, использовать LinqToSolrService напрямую не очень удобно. Мы создаем наш собственный сервис, унаследованный от LinqToSolrService.

public class MySolrService : LinqToSolrService  {     public MySolrService(LinqToSolrRequestConfiguration config) : base (config)     {    }      public IQueryable<MyProduct> NotDeleted()     {             return AsQueryable<MyProduct>().Where(x=> !x.IsDeleted);     }      public ICollection<MyProduct> GetProducts(params int[] ids)     {             return NotDeleted().Where(x=> ids.Contains(x.Id)).OrderBy(x=>x.Name).ToList();     }      public MyProduct GetProduct(id)     {             return NotDeleted().FirstOrDefault(x=> x.Id == id);     }      public string[] GetGroups(id)     {             return NotDeleted().GroupBy(x=> x.Group).ToArray();     } } 

Итог

Надеюсь, кому-то, кто исползует Solr в .NET проектах, пригодятся и статься и библиотека.
На данный момент реализованы самые очевидные запросы.
В планах добавить поддержку Boost, Proximity и функции запросов.
ссылка на оригинал статьи https://habrahabr.ru/post/327002/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *