Подстановочный запрос 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
,
Надеюсь это поможет.