elasticsearch - 在 Elasticsearch 6.6 和 NEST 6.6 中具有排序和分页变量的 SearchTemplate
问题描述
所有,我正在尝试调用 ES 6.6 中定义的 SearchTemplate。该模板具有分页变量(来自和大小)和我在数组中传递的电子邮件。这也具有自定义脚本逻辑的排序。当我在 kibana 中运行它时,我看不到分页和排序不起作用。我将不胜感激任何帮助使其发挥作用。请参阅下面的详细信息。我使用索引别名搜索了两个索引。
人和访客索引的映射是相同的(只是为了简化示例)
索引映射
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)
)
);
观察
- 当我从搜索模板中删除来自、大小和排序逻辑时,它可以工作
- 好像我将 sort 和 from/size 变量放在错误的位置?
我在这里找到了一个类似的帖子https://discuss.elastic.co/t/c-nest-5-search-with-template/104074/2但似乎 GetSearchTemplate 和 PutSearchTemplate 在 NEST 6.x 上已停产
这可以使用搜索模板来完成吗?我们使用一些非常复杂的 NEST 查询,并且正在远离 NEST 并使用搜索模板。
解决方案
有几个问题
- 索引模板定义了
"email"
两次字段映射 - index模板设置
"dynamic"
为false但不包含"type"
字段映射,所以脚本排序会失败 - 需要
"source"
为 Put Script API 调用定义整个搜索请求
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"
]
}
]
}
}
推荐阅读
- mongodb - 多查询 Express/Mongodb
- vue.js - 如何在合成 api 中设置步骤形式?
- rfid - RFID 标签会话 2 持续多长时间?
- java - 是否可以根据子接口自动生成构造函数?(有或没有龙目岛)
- angular - JavaScript - 合并两个 API 调用的结果
- python - 如何使用插入多条记录
- java - 如何在 ubuntu 的命令行中使用 maven 运行 java 应用程序?
- html - 定位来自外部组件的嵌套 div
- python - 检查项目是否在列表中 discord python
- woocommerce - 如何跟踪在存档产品页面上单击了添加到购物车按钮?