Поиск ElasticSearch / Nest - Адресное предложение Api с фильтром категории - PullRequest
0 голосов
/ 07 сентября 2018

Пользователь, который вызывает мой API отдыха, должен иметь возможность указать тип места, а затем предложить название.

Тип места должен быть точным, в то время как поиск по названию использует весь потенциал поиска elaticsearch.

Я использую NEST (последняя версия nuget) и Elastic 6.4.

My Api имеет 2 параметра: 1. запрос: поиск текстового поиска 2. placetypes: массив для определения желаемых «категорий» документов в предложении

Предложение названия места с параметром Query работает хорошо, но я не знаю, как добавить условие placetype.

Отображение гнезда:

return map
  .Dynamic(false)
  .Properties(props => props
    .Keyword(n => n
      .Name(p => p.Id))
    .Text(n => n
      .Name(p => p.PlaceType))
    .Completion(n => n
      .Name(p => p.PlaceName)
      .Analyzer("autocompletion_indexation"))
    .Completion(n => n
      .Name(p => p.Address)
      .Analyzer("autocompletion_indexation"))
    .GeoPoint(loc =>
      {
      loc.Name(location => location.Coordinates);
      return loc;
      }));

Анализ гнездаDescriptor:

return analysis
    .CharFilters(c => c
        .HtmlStrip("html_strip")
    )
    .Tokenizers(t => t
        .EdgeNGram("custom_ngram", descriptor =>
            {
            descriptor.MinGram(2);
            descriptor.MaxGram(10);
            descriptor.TokenChars(new List<TokenChar> { TokenChar.Letter, TokenChar.Digit });
            return descriptor;
            }
        ))
    .TokenFilters(tf => tf
        .Lowercase("lowercase")
        .WordDelimiter("word_delimiter", wd =>wd
        .SplitOnNumerics()
        .SplitOnCaseChange()
    )
    .AsciiFolding("asciifolding", af => af
        .PreserveOriginal(false)
    )
    .Elision("elision", e => e
        .Articles("l", "d", "o")
    )
    .Synonym("address_synonym", sy => sy
        .Synonyms(GetSynonyms())
        .Tokenizer("standard")
        .Tokenizer("whitespace")
    )
    .Stop("french_stop", fs => fs
        .StopWords("_french_"))
    .Stemmer("french_stemmer", fs => fs
    .Language("light_french")
)
)
    .Analyzers(an => an
        .Custom("autocompletion_indexation", c => c
        .Tokenizer("custom_ngram")
        .Tokenizer("standard")
        .Tokenizer("whitespace")
        .CharFilters("html_strip")
        .Filters("address_synonym",
        "lowercase",
        "asciifolding",
        "elision",
        "word_delimiter",
        "stop",
        "french_stemmer",
        "french_stop")
));    

Функция подсказки / поиска:

public Task<List<Place>> SuggestDocuments(CancellationToken cancellationToken, string query, params string[] placeTypes)
{
var search = new SearchDescriptor<Place>()
    .From(0)
    .Size(10)
    .Index(PlaceDataService.DefaultPostalAddressIndexName)
    .Query(q => q
        .MultiMatch(mm => mm
        .Query(query)
        .Fuzziness(Fuzziness.Auto)
        .Fields(fields => fields
            .Field(f => f.PlaceName)
            )));

var searchResults = _elasticClient.SearchAsync<Place>(search, cancellationToken);
return Task.Run(() => searchResults.Result.Documents.ToList(), cancellationToken);
}

1 Ответ

0 голосов
/ 10 сентября 2018

Вы можете объединить запрос multi_match с запросом terms, заключив их в запрос bool для достижения этого

var query = "this is the place name";
var places = new [] { "Place 1", "Place 2" };

var search = new SearchDescriptor<Place>()
    .From(0)
    .Size(10)
    .Index("index")
    .Query(q => q
        .MultiMatch(mm => mm
            .Query(query)
            .Fuzziness(Fuzziness.Auto)
            .Fields(fields => fields
                .Field(f => f.PlaceName)
            )
        ) && +q
        .Terms(t => t
            .Field(f => f.PlaceType)
            .Terms(places)
        )
    );

var searchResults = await _elasticClient.SearchAsync<Place>(search, cancellationToken);

Выше используется перегрузка операторов для запросов, о которой вы можете прочитать в документации по Writing Bool . Выше выдает следующий запрос

{
  "from": 0,
  "size": 10,
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "this is the place name",
            "fuzziness": "AUTO",
            "fields": [
              "placeName"
            ]
          }
        }
      ],
      "filter": [
        {
          "terms": {
            "placeType": [
              "Place 1",
              "Place 2"
            ]
          }
        }
      ]
    }
  }
}

Обратите внимание, что запрос terms будет ИЛИ в терминах, так что только один из них должен соответствовать. Если все входные данные должны совпадать, вы можете выполнить несколько запросов term.

Некоторые вещи выглядят не совсем правильно с вашим отображением и анализом

  1. PlaceName отображается как completion тип данных , но используется в поисковом запросе multi_match, а не suggest. Я бы ожидал, что для этого случая будет отображен тип данных text.
  2. Если PlaceType поля должны быть сопоставлены точно как есть, я ожидаю, что они будут отображены как keyword тип данных
  3. html_strip встроен фильтр символов, поэтому его не нужно переопределять, если только вам не нужно настраивать его работу, например. escaped_tags. Аналогично для lowercase токен-фильтра.
  4. В пользовательском анализаторе "autocompletion_indexation" может быть только один токенизатор, поэтому выигрывает последний назначенный (токенизатор "whitespace"). Ознакомьтесь с документами для написания анализаторов .
...