Подстановочный запрос Elasticsearch Nest с пробелами

Укороченная версия:

Я хотел бы написать эластичный поисковый запрос, используя Nest, чтобы получить полные проиндексированные элементы (ContentIndexables в моем случае как мой пользовательский тип), которые были проиндексированы. Запрос подчиняется термину запроса [некоторая строка] + * (т.е. String.StartsWith(), где [некоторая строка] может содержать или не содержать пробелы.

Это отличается от CompletionSuggester так как мне нужно получить полный объект, а не строки предложения.

Что я пробовал до сих пор:

Когда я запрашиваю текст без пробелов, желаемый результат возвращается с помощью кода ниже. Однако если мой поисковый термин содержит пробелы, он не возвращает ожидаемых результатов.

Вот как я ищу поля:

var searchResults = _client.Search<ContentIndexable>(
            body =>
            body
                .Index(indexName)
                .Query(
                    query =>
                    query.QueryString(
                        qs => qs.
                                  OnFields(f => f.Title, f => f.TextContent)
                                  .Query(searchTerm + "*"))));

И это модульный тест, который демонстрирует, как воспроизвести проблему:

indexService.IndexUserItemsSync(testGuid, IndexType.submission, new ContentIndexable
        {
            ContentId = Guid.NewGuid(),
            TextContent = "Some description",
            Title = "title"
        });

        indexService.IndexUserItemsSync(testGuid, IndexType.submission, new ContentIndexable
        {
            ContentId = Guid.NewGuid(),
            TextContent = "Some description",
            Title = "title that is long"
        });

        indexService.IndexUserItemsSync(testGuid, IndexType.submission, new ContentIndexable
        {
            ContentId = Guid.NewGuid(),
            TextContent = "Some description",
            Title = "title that likes"
        });

        indexService.IndexUserItemsSync(testGuid, IndexType.submission, new ContentIndexable
        {
            ContentId = Guid.NewGuid(),
            TextContent = "Some description",
            Title = "titlethat"
        });

        var searchResult = indexService.SearchUserItems(testGuid, IndexType.submission, 10, "title");
        Assert.IsNotNull(searchResult);
// this one works
        Assert.AreEqual(4, searchResult.Count());

        var searchResult2 = indexService.SearchUserItems(testGuid, IndexType.submission, 10, "title that");
        Assert.IsNotNull(searchResult2);
// this one does not!!! searchREsult2.Count() evaluates to 0
        Assert.AreEqual(2, searchResult2.Count());

Как видите, затем я ввожу "title that", поиск возвращается пустым вместо двух строк, которые я ожидаю вернуть.

Обновление: дополнительная информация: я создаю индекс для моего типа ContentIndexable:

public class ContentIndexable : IIndexable
{
    public Guid ContentId { get; set; }
    public string Title { get; set; }
    public string TextContent { get; set; }
}

С этим кодом:

_client.CreateIndex(
    indexName,
    descriptor =>
    descriptor.AddMapping<ContentIndexable>(
        m => m.Properties(
            p => p.Completion(s => s
                                       .Name(n => n.Title)
                                       .IndexAnalyzer("standard")
                                       .SearchAnalyzer("standard")
                                       .MaxInputLength(30)
                                       .Payloads()
                                       .PreserveSeparators()
                                       .PreservePositionIncrements())
                     .Completion(s => s.Name(n => n.TextContent)
                                          .IndexAnalyzer("standard")
                                          .SearchAnalyzer("standard")
                                          .MaxInputLength(50)
                                          .Payloads()
                                          .PreserveSeparators()
                                          .PreservePositionIncrements())
                 )));

Я даже пытался избежать пробелов как при индексировании, так и при запросе string.Replace(" ", @"\ ") но это не помогло

Изменение типа поиска на групповой символ также не помогло:

var searchResults = _client.Search<ContentIndexable>(
            body =>
            body
                .Index(indexName)
                .Query(
                    query => query.Wildcard(qd => qd.OnField(f => f.Title).Value(searchTerm + "*"))));

Кто-нибудь знает, что я делаю не так?

Обратите внимание, что мой CompletionSuggester версия работает с пробелами, но, к сожалению, возвращает только строки. Мне нужно достать полный предмет, чтобы получить ContentId, МОЯ реализация CompletionSuggester:

public IEnumerable<string> GetAutoCompleteSuggestions(Guid userId, IndexType indexType, int size, string searchTerm)
    {
        string indexName = getIndexName(indexType, userId);

        var result = _client.Search<ContentIndexable>(
            body => body.Index(indexName)
                        .SuggestCompletion("content-suggest" + Guid.NewGuid(),
                                           descriptor => descriptor
                                                             .OnField(t => t.Title)
                                                             .Text(searchTerm)
                                                             .Size(size)));

        if (result.Suggest == null)
        {
            return new List<string>();
        }

        return (from suggest in result.Suggest
                from value in suggest.Value
                from options in value.Options
                select options.Text).Take(size);
    }

Я знаю, что могу принять предложения, получить полное значение (что приведет к двум ожидаемым элементам), а затем выполнить полное совпадение, используя мой первый метод, но для этого требуется 2 отдельных вызова в ElasticSearch (один для полного предложения) и второй для термина запроса), но в идеале я хотел бы сделать это без обратной связи, если это возможно.

Спасибо заранее,

1 ответ

Решение

Это пример того, как вы можете справиться с вашей проблемой для Title поле.

Измените ваше отображение на что-то вроде (или используйте MultiField, но я не смог найти опцию для сопоставления поля как строки и завершения одновременно):

client.CreateIndex(indexName, i => i
    .AddMapping<ContentIndexable>(m => m
        .Properties(
            ps => ps
                .Completion(c => c.Name("title.completion")
                    .IndexAnalyzer("standard")
                    .SearchAnalyzer("standard")
                    .MaxInputLength(30)
                    .Payloads()
                    .PreserveSeparators()
                    .PreservePositionIncrements())
                .String(s => s.Name(x => x.Title).CopyTo("title.completion")))));

Измени свой SuggestCompletion в

var result = client.Search<ContentIndexable>(body => body
    .Index(indexName)
    .SuggestCompletion("content-suggest" + Guid.NewGuid(),
        descriptor => descriptor
            .OnField(t => t.Title.Suffix("completion"))
            .Text("title")
            .Size(10)));

а также QueryString в

var searchResponse = client.Search<ContentIndexable>(body => body
    .Index(indexName)
    .Query(query => query
        .QueryString(
            qs => qs
                .OnFields(f => f.Title.Suffix("completion"))
                .Query("title tha" + "*")
                .MinimumShouldMatchPercentage(100))));

Проблема с этим решением заключается в том, что мы храним данные дважды для Title поле. Вот почему я упоминал ранее, что было бы здорово использовать MultiField, но я не смог сделать это с NEST,

Надеюсь это поможет.

Другие вопросы по тегам