SearchTemplate с переменными сортировки и подкачки в Elasticsearch 6.6 и NEST 6.6
Все, я пытаюсь вызвать SearchTemplate, определенный в ES 6.6. Шаблон имеет переменные подкачки (от и размер) и электронные письма, которые я передаю в массиве. Это также имеет сортировку с пользовательской логикой сценария. Когда я запускаю это в кибане, я не вижу, что подкачка и сортировка не работают. Я был бы признателен за любую помощь в получении этого на работу. Пожалуйста, смотрите детали ниже. Есть два индекса, которые я ищу, используя псевдоним индекса.
Отображения для персоны и гостевых индексов одинаковы (просто для упрощения примера)
Отображение индекса
PUT _template/person_guest_template
{
"order": 0,
"index_patterns": ["person*","guest*"],
"settings": {
"index": {
"analysis": {
"filter": {
"autoComplete_filter": {
"type": "edge_ngram",
"min_gram": "2",
"max_gram": "20"
}
},
"analyzer": {
"autoComplete": {
"filter": ["lowercase", "asciifolding","autoComplete_filter"],
"type": "custom",
"tokenizer": "whitespace"
},
"default": {
"filter": ["lowercase", "asciifolding"],
"type": "custom",
"tokenizer": "whitespace"
}
}
},
"number_of_shards": "3",
"number_of_replicas": "1"
}
},
"mappings": {
"_doc": {
"dynamic": false,
"properties": {
"firstName": {
"type": "keyword",
"fields": {
"search": {
"type": "text",
"analyzer": "autoComplete",
"search_analyzer": "default"
}
}
},
"lastName": {
"type": "keyword",
"fields": {
"search": {
"type": "text",
"analyzer": "autoComplete",
"search_analyzer": "default"
}
}
},
"email": {
"type": "keyword"
},"email": {
"type": "keyword"
}
}
}
}
}
Определение шаблона поиска
POST _scripts/guest_person_by_email
{
"script": {
"from": "{{from}}{{^from}}0{{/from}}",
"size": "{{size}}{{^size}}5{{/size}}",
"sort": [
{
"_script": {
"order": "asc",
"type": "number",
"script": "return (doc['type'].value == 'person')? 0 : 1;"
}
},
{
"firstName": {
"order": "asc"
}
},
{
"lastName": {
"order": "asc"
}
}
],
"lang": "mustache",
"source": """
{
"query":{
"bool":{
"filter":{
"terms":{
"email":
{{#toJson}}emails{{/toJson}}
}
}
}
}
}
"""
}
}
Поиск с использованием SearchTemplate
GET guest-person/_search/template
{
"id":"guest_person_by_email",
"params": {
"emails":["rennishj@test.com"]
}
}
Пример данных
PUT person/_doc/1
{
"firstName": "Rennish",
"lastName": "Joseph",
"email": [
"rennishj@test.com"
],
"type":"person"
}
Вызов шаблона поиска с использованием NEST 6.6
List<string> emails = new List<string>(){"rennishj@test.com"};
var searchResponse = client.SearchTemplate<object>(st => st
.Index("guest-person")
.Id("guest_person_by_email")
.Params(p => p
.Add("emails", emails.ToArray())
.Add("from", 0)
.Add("size", 50)
)
);
наблюдения
- Когда я удаляю логику from, size и sort из searchtemplate, она работает
- Похоже, я помещаю переменные sort и from/size в неправильное место?
Я нашел подобный пост здесь https://discuss.elastic.co/t/c-nest-5-search-with-template/104074/2 но похоже, что GetSearchTemplate и PutSearchTemplate более не поддерживаются в NEST 6.x
Можно ли это сделать с помощью поисковых шаблонов? Мы используем несколько очень сложных запросов NEST, отходим от NEST и используем шаблоны поиска.
2 ответа
Есть несколько вопросов
- шаблон индекса определяет
"email"
отображение поля дважды - наборы шаблонов индексов
"dynamic"
ложно, но не содержит"type"
отображение полей, поэтому сортировка сценария не удастся - весь поисковый запрос должен быть определен в пределах
"source"
для вызова API Put Script
NEST может быть полезен при построении правильных поисковых запросов и их использовании в качестве основы для шаблона поиска, помимо целого ряда других причин использования клиента, таких как запросы с циклическим перебором, автоматическое переключение при сбое и повторные попытки и т. Д.
Вот полный пример
private static void Main()
{
var defaultIndex = "person";
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(pool)
.DefaultIndex(defaultIndex)
.DefaultTypeName("_doc");
var client = new ElasticClient(settings);
// WARNING: This deletes the index to make this code repeatable.
// You probably want to remove this if copying verbatim
if (client.IndexExists(defaultIndex).Exists)
client.DeleteIndex(defaultIndex);
var indexTemplateResponse = client.LowLevel.IndicesPutTemplateForAll<PutIndexTemplateResponse>(
"person_guest_template",
@"{
""order"": 0,
""index_patterns"": [""person*"",""guest*""],
""settings"": {
""index"": {
""analysis"": {
""filter"": {
""autoComplete_filter"": {
""type"": ""edge_ngram"",
""min_gram"": ""2"",
""max_gram"": ""20""
}
},
""analyzer"": {
""autoComplete"": {
""filter"": [""lowercase"", ""asciifolding"",""autoComplete_filter""],
""type"": ""custom"",
""tokenizer"": ""whitespace""
},
""default"": {
""filter"": [""lowercase"", ""asciifolding""],
""type"": ""custom"",
""tokenizer"": ""whitespace""
}
}
},
""number_of_shards"": ""3"",
""number_of_replicas"": ""1""
}
},
""mappings"": {
""_doc"": {
""dynamic"": false,
""properties"": {
""firstName"": {
""type"": ""keyword"",
""fields"": {
""search"": {
""type"": ""text"",
""analyzer"": ""autoComplete"",
""search_analyzer"": ""default""
}
}
},
""lastName"": {
""type"": ""keyword"",
""fields"": {
""search"": {
""type"": ""text"",
""analyzer"": ""autoComplete"",
""search_analyzer"": ""default""
}
}
},
""email"": {
""type"": ""keyword""
},
""type"": {
""type"": ""keyword""
}
}
}
}
}");
// build a prototype search request
var searchRequest = new SearchRequest
{
From = 0,
Size = 0,
Sort = new List<ISort>
{
new ScriptSort
{
Order = Nest.SortOrder.Ascending,
Type = "number",
Script = new InlineScript("return (doc['type'].value == 'person')? 0 : 1;")
},
new SortField
{
Field = "firstName",
Order = Nest.SortOrder.Ascending
},
new SortField
{
Field = "lastName",
Order = Nest.SortOrder.Ascending
}
},
Query = new BoolQuery
{
Filter = new QueryContainer[]
{
new TermsQuery
{
Field = "email",
Terms = new[] { "emails" }
}
}
}
};
var json = client.RequestResponseSerializer.SerializeToString(searchRequest);
// create template from prototype search request
var jObject = JsonConvert.DeserializeObject<JObject>(json);
jObject["from"] = "{{from}}{{^from}}0{{/from}}";
jObject["size"] = "{{size}}{{^size}}5{{/size}}";
json = jObject.ToString(Newtonsoft.Json.Formatting.None);
// below is invalid JSON, so can only be constructed with replacement
json = json.Replace("[\"emails\"]", "{{#toJson}}emails{{/toJson}}");
// add search template
var putScriptResponse = client.PutScript("guest_person_by_email", s => s
.Script(sc => sc
.Lang(ScriptLang.Mustache)
.Source(json)
)
);
var person = new Person
{
FirstName = "Rennish",
LastName = "Joseph",
Email = new[] { "rennishj@test.com" }
};
// index document
var indexResponse = client.Index(person, i => i.Id(1).Refresh(Refresh.WaitFor));
// search
var searchResponse = client.SearchTemplate<Person>(s => s
.Id("guest_person_by_email")
.Params(p => p
.Add("emails", person.Email)
.Add("from", 0)
.Add("size", 50)
)
);
}
public class Person
{
public string FirstName {get;set;}
public string LastName { get; set; }
public string[] Email {get;set;}
public string Type {get; set;} = "person";
}
Результат запроса шаблона поиска
{
"took" : 47,
"timed_out" : false,
"_shards" : {
"total" : 3,
"successful" : 3,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : null,
"hits" : [
{
"_index" : "person",
"_type" : "_doc",
"_id" : "1",
"_score" : null,
"_source" : {
"firstName" : "Rennish",
"lastName" : "Joseph",
"email" : [
"rennishj@test.com"
],
"type" : "person"
},
"sort" : [
0.0,
"Rennish",
"Joseph"
]
}
]
}
}
Добавление правильного SearchTemplate(перемещение страниц и сортировка по "источнику", как указал Расс Кэм) на тот случай, если кому-то понадобится в будущем.
POST _scripts/guest_person_by_email
{
"script": {
"lang": "mustache",
"source": """
{
"from": "{{from}}{{^from}}0{{/from}}",
"size": "{{size}}{{^size}}5{{/size}}",
"sort": [
{
"_script": {
"order": "asc",
"type": "number",
"script": "return (doc['type'].value == 'person')? 0 : 1;"
}
},
{
"firstName": {
"order": "asc"
}
},
{
"lastName": {
"order": "asc"
}
}
],
"query":{
"bool":{
"filter":{
"terms":{
"email":
{{#toJson}}emails{{/toJson}}
}
}
}
}
}
"""
}
}