私信  •  关注

Russ Cam

Russ Cam 最近创建的主题
Russ Cam 最近回复了
4 年前
回复了 Russ Cam 创建的主题 » 如何在elasticsearch中添加文档时引用多个字段

可以使用update API更新单个字段

var client = new ElasticClient();

var documentId = 1;

var partial = new 
{
    Description = "This is some description"
};

var updateResponse = client.Update<Document, object>(documentId, u => u
    .Index("your_index")
    .Doc(partial)
);

这个 .Index() Document 键入。要更新的文档是用部分文档建模的,因为使用 将导致为值类型发送默认值,如 DocumentDate DocumentType_Id

doc.Description.en=“这是一些描述”;

不可能这样做,因为这不是 multi-fields 工作。对于多个字段,可以通过多种不同的方式分析单个文档字段输入,以满足不同的搜索需求。在你的例子中, Description 财产价值将通过4种不同的方式进行分析:

  1. 用标准分析仪 text 映射
  2. 由英语分析器和 .en 多场映射
  3. 通过Nofa波斯语分析器 .fa 多场映射
  4. .fr 多场映射

"description" 字段,当您检索 _source 对于文档(如果 存储,默认情况下为)。

如果要将这些字段建模为文档上的单独字段,可以引入 具有必需属性的类型

public class Description
{
    public string Standard { get;set; }
    public string English { get;set; }
    public string NoFaPersian{ get;set; }
    public string French{ get;set; }
}

然后将其索引为 object 类型映射,为每个

public class Document
{
    public string BaseUniqueID { get; set; }
    public int? Weight { get; set; }
    public DateTime DocumentDate { get; set; }
    public Description Description { get; set; }
    public int DocumentType_Id { get; set; }
}

var indexResponse = client.CreateIndex("your_index", c => c
    .Mappings(m => m
        .Map<Document>(mm => mm
            .AutoMap()
            .Properties(p => p
                .Object<Description>(o => o
                    .Name(n => n.Description)
                    .AutoMap()
                    .Properties(pp => pp
                        .Text(t => t.Name(n => n.Standard).Analyzer("standard"))
                        .Text(t => t.Name(n => n.English).Analyzer("english"))
                        .Text(t => t.Name(n => n.NoFaPersian).Analyzer("nofapersian"))
                        .Text(t => t.Name(n => n.French).Analyzer("french"))
                    )
                )
            )
        )
    )       
);

它生成以下创建索引请求

PUT http://localhost:9200/your_index?pretty=true 
{
  "mappings": {
    "document": {
      "properties": {
        "baseUniqueID": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "weight": {
          "type": "integer"
        },
        "documentDate": {
          "type": "date"
        },
        "description": {
          "type": "object",
          "properties": {
            "standard": {
              "type": "text",
              "analyzer": "standard"
            },
            "english": {
              "type": "text",
              "analyzer": "english"
            },
            "noFaPersian": {
              "type": "text",
              "analyzer": "nofapersian"
            },
            "french": {
              "type": "text",
              "analyzer": "french"
            }
          }
        },
        "documentType_Id": {
          "type": "integer"
        }
      }
    }
  }
}
5 年前
回复了 Russ Cam 创建的主题 » 嵌套-当项为空时多匹配搜索所有文档-ElasticSearch 6.4

默认情况下,nest通过一个称为 Conditionless queries 是的。你可以

private static void Main()
{
    var defaultIndex = "documents";
    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));

    var settings = new ConnectionSettings(pool, new InMemoryConnection())
        .DefaultIndex(defaultIndex)
        .DisableDirectStreaming()
        .PrettyJson()
        .OnRequestCompleted(callDetails =>
        {
            if (callDetails.RequestBodyInBytes != null)
            {
                Console.WriteLine(
                    $"{callDetails.HttpMethod} {callDetails.Uri} \n" +
                    $"{Encoding.UTF8.GetString(callDetails.RequestBodyInBytes)}");
            }
            else
            {
                Console.WriteLine($"{callDetails.HttpMethod} {callDetails.Uri}");
            }

            Console.WriteLine();

            if (callDetails.ResponseBodyInBytes != null)
            {
                Console.WriteLine($"Status: {callDetails.HttpStatusCode}\n" +
                         $"{Encoding.UTF8.GetString(callDetails.ResponseBodyInBytes)}\n" +
                         $"{new string('-', 30)}\n");
            }
            else
            {
                Console.WriteLine($"Status: {callDetails.HttpStatusCode}\n" +
                         $"{new string('-', 30)}\n");
            }
        });

    var client = new ElasticClient(settings);

    var pageSize = 20;
    var currentPageIndex = 0;  
    string search = "foo";


    var searchResponse = client.Search<DocumentElasticModel>(s => s
        .Size(pageSize)
        .Skip(currentPageIndex * pageSize)
        .Sort(ss => ss
            .Descending(SortSpecialField.Score)
        )
        .Source(sf => sf
            .Includes(i => i
                .Fields(f => f.TopLevelMessage)
            )
        )
        .Query(q => q
            .Nested(c => c
                .Name("named_query")
                .Boost(1.1)
                .InnerHits(i => i.Explain())
                .Path(p => p.PerguntasRespostas)
                .Query(nq => nq
                    .MultiMatch(m => m
                        .Fields(f => f
                            .Field(ff => ff.PerguntasRespostas.First().Message)
                        ) 
                        .Query(search)
                    )
                )
                .IgnoreUnmapped()
            )
        )
    );
}


public class DocumentElasticModel 
{
    public string TopLevelMessage { get; set; }

    public IEnumerable<PerguntasRespostas> PerguntasRespostas {get;set;}
}

public class PerguntasRespostas
{
    public string Message { get; set; }  
}

这将发送以下查询

POST http://localhost:9200/documents/documentelasticmodel/_search
{
  "from": 0,
  "size": 20,
  "sort": [
    {
      "_score": {
        "order": "desc"
      }
    }
  ],
  "_source": {
    "includes": [
      "topLevelMessage"
    ]
  },
  "query": {
    "nested": {
      "_name": "named_query",
      "boost": 1.1,
      "query": {
        "multi_match": {
          "query": "foo",
          "fields": [
            "perguntasRespostas.message"
          ]
        }
      },
      "path": "perguntasRespostas",
      "inner_hits": {
        "explain": true
      },
      "ignore_unmapped": true
    }
  }
}

现在,如果你改变 search string.Empty null ,你得到

POST http://localhost:9200/documents/documentelasticmodel/_search
{
  "from": 0,
  "size": 20,
  "sort": [
    {
      "_score": {
        "order": "desc"
      }
    }
  ],
  "_source": {
    "includes": [
      "topLevelMessage"
    ]
  }
}

没有明确的 "query" 在请求中,这与 match_all 查询。

如果要重写嵌套中的无条件查询功能,可以将查询标记为 .Verbatim() Nest会按原样发送

var searchResponse = client.Search<DocumentElasticModel>(s => s
    .Size(pageSize)
    .Skip(currentPageIndex * pageSize)
    .Sort(ss => ss
        .Descending(SortSpecialField.Score)
    )
    .Source(sf => sf
        .Includes(i => i
            .Fields(f => f.TopLevelMessage)
        )
    )
    .Query(q => q
        .Nested(c => c
            .Verbatim() // <-- mark the nested query
            .Name("named_query")          
            .Boost(1.1)
            .InnerHits(i => i.Explain())
            .Path(p => p.PerguntasRespostas)
            .Query(nq => nq
                .MultiMatch(m => m
                    .Verbatim() // <-- mark the inner query
                    .Fields(f => f
                        .Field(ff => ff.PerguntasRespostas.First().Message)
                    ) 
                    .Query(search)
                )
            )
            .IgnoreUnmapped()
        )
    )
);

它发出

POST http://localhost:9200/documents/documentelasticmodel/_search
{
  "from": 0,
  "size": 20,
  "sort": [
    {
      "_score": {
        "order": "desc"
      }
    }
  ],
  "_source": {
    "includes": [
      "topLevelMessage"
    ]
  },
  "query": {
    "nested": {
      "_name": "named_query",
      "boost": 1.1,
      "query": {
        "multi_match": {
          "fields": [
            "perguntasRespostas.message"
          ]
        }
      },
      "path": "perguntasRespostas",
      "inner_hits": {
        "explain": true
      },
      "ignore_unmapped": true
    }
  }
}

您需要检查这是否是ElasticSearch接受的有效查询。

5 年前
回复了 Russ Cam 创建的主题 » 在ElasticSearch中对嵌套集合执行查询,嵌套在.NET Core中

你不需要表演 nested 查询以查询 Tags 字段,因为每个标记只是一个原始json值,即 string . 只是 terms 查询就足够了。

在哪里 嵌套的 需要查询的是 标签 是具有多个属性的poco,映射为 嵌套的 数据类型。

5 年前
回复了 Russ Cam 创建的主题 » 搜索查询elasticsearch with ngram始终返回0个结果

这个 ngram_analyzer 用于分析搜索请求的查询输入,但此分析器不用于分析 OriginalTitle 索引请求的输入。

您只需要将分析器配置为用于 原岩 索引文档时的字段,可以用 attribute mapping fluent mapping . 例如,fluent映射

var client = new ElasticClient();

if (client.IndexExists(defaultIndex).Exists)
    client.DeleteIndex(defaultIndex);

var nGramFilters = new List<string> { "lowercase", "asciifolding", "nGram_filter" };

var resp = client.CreateIndex(defaultIndex, c => c
     .Mappings(m => m
        .Map<ElasticVideoMaterial>(mm => mm
            .AutoMap()
            .Properties(p => p
                .Text(t => t
                    .Name(n => n.OriginalTitle)
                    .Fields(f => f
                        .Keyword(k => k
                            .Name("keyword")
                            .IgnoreAbove(256)
                        )
                        .Text(tt => tt
                            .Name("ngram")
                            .Analyzer("ngram_analyzer")
                        )
                    )
                )
            )
        )
    )
    .Settings(s => s
        .Analysis(a => a
            .Analyzers(anz => anz
                .Custom("ngram_analyzer", cc => cc
                    .Filters(nGramFilters)
                    .Tokenizer("ngram_tokenizer")))
            .Tokenizers(tz => tz
                .NGram("ngram_tokenizer", td => td
                    .MinGram(3)
                    .MaxGram(3)
                    .TokenChars(TokenChar.Letter, TokenChar.Digit)
                )
            )
        )
    )
);

var searchResponse = client.Search<ElasticVideoMaterial>(i => i
    .Query(q => q
        .Match(m => m
            .Field(f => f.OriginalTitle.Suffix("ngram"))
            .Query("Hob")
        )
    )
);

这成立了 原岩 作为一个 multi-field 并创建一个称为 ngram 在下面 原岩 它将使用 ngram_分析仪 在此字段的索引时间和搜索时间。

14 年前
回复了 Russ Cam 创建的主题 » 组合框交互中的jquery live方法

试用使用 change onchange live() 方法。另外,如果我的内存正确地为我服务,您将需要jquery 1.4+来使用 live 对于change事件,因为代码仅在1.4+中实现,以处理 改变 事件。

为了将新选择的值与上一个选择的值进行比较,您可以考虑存储选定的值以供使用 $.data() -然后,可以将更改事件后的值与存储在$.cache(其中$.data()存储值)中的值进行比较,并执行必要的操作。您也可以使用闭包来实现它。

5 年前
回复了 Russ Cam 创建的主题 » ElasticSearch中是否有批量部分更新?

您可以使用批量API发送部分更新。下面是一个例子

private static void Main()
{
    var defaultIndex = "documents";
    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));

    var settings = new ConnectionSettings(pool)
        .DefaultIndex(defaultIndex);

    var client = new ElasticClient(settings);

    if (client.IndexExists(defaultIndex).Exists)
        client.DeleteIndex(defaultIndex);

    var docs = Enumerable.Range(1, 10).Select(i => new MyDocument(i) 
        {
            Message = $"message {i}"
        });

    // bulk index the documents   
    var bulkResponse = client.Bulk(b => b
        .IndexMany(docs)
        .Refresh(Refresh.WaitFor)
    );

    var searchResponse = client.Search<MyDocument>(s => s
        .Sort(so => so.Ascending("_id"))
    );

    // update the documents
    bulkResponse = client.Bulk(b => b
        .UpdateMany<MyDocument, object>(docs, (bu, doc) => 
        {
            if (doc.Id % 3 == 0)
            {
                // use script to update
                bu.Id(doc.Id).Script(s => s
                    .Source("ctx._source.message = 'message ' + (Integer.parseInt(ctx._id) * 2);")
                );
            }
            else if (doc.Id % 2 == 0)
            {
                // use partial document to update
                bu.Id(doc.Id).Doc(new { message = "updated message" });
            }
            else
            {
                // send the original document to update
                bu.Doc(doc);
            }

            return bu;
        })
        .Refresh(Refresh.WaitFor)
    );

    searchResponse = client.Search<MyDocument>(s => s
        .Sort(so => so.Ascending("_id"))
    );    
}


public class MyDocument 
{
    public MyDocument(int id) => Id = id;

    public int Id { get; set; }  

    public string Message { get; set; }
}

最终搜索响应返回

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 10,
    "max_score" : null,
    "hits" : [
      {
        "_index" : "documents",
        "_type" : "mydocument",
        "_id" : "1",
        "_score" : null,
        "_source" : {
          "id" : 1,
          "message" : "message 1"
        },
        "sort" : [
          "1"
        ]
      },
      {
        "_index" : "documents",
        "_type" : "mydocument",
        "_id" : "10",
        "_score" : null,
        "_source" : {
          "id" : 10,
          "message" : "updated message"
        },
        "sort" : [
          "10"
        ]
      },
      {
        "_index" : "documents",
        "_type" : "mydocument",
        "_id" : "2",
        "_score" : null,
        "_source" : {
          "id" : 2,
          "message" : "updated message"
        },
        "sort" : [
          "2"
        ]
      },
      {
        "_index" : "documents",
        "_type" : "mydocument",
        "_id" : "3",
        "_score" : null,
        "_source" : {
          "id" : 3,
          "message" : "message 6"
        },
        "sort" : [
          "3"
        ]
      },
      {
        "_index" : "documents",
        "_type" : "mydocument",
        "_id" : "4",
        "_score" : null,
        "_source" : {
          "id" : 4,
          "message" : "updated message"
        },
        "sort" : [
          "4"
        ]
      },
      {
        "_index" : "documents",
        "_type" : "mydocument",
        "_id" : "5",
        "_score" : null,
        "_source" : {
          "id" : 5,
          "message" : "message 5"
        },
        "sort" : [
          "5"
        ]
      },
      {
        "_index" : "documents",
        "_type" : "mydocument",
        "_id" : "6",
        "_score" : null,
        "_source" : {
          "id" : 6,
          "message" : "message 12"
        },
        "sort" : [
          "6"
        ]
      },
      {
        "_index" : "documents",
        "_type" : "mydocument",
        "_id" : "7",
        "_score" : null,
        "_source" : {
          "id" : 7,
          "message" : "message 7"
        },
        "sort" : [
          "7"
        ]
      },
      {
        "_index" : "documents",
        "_type" : "mydocument",
        "_id" : "8",
        "_score" : null,
        "_source" : {
          "id" : 8,
          "message" : "updated message"
        },
        "sort" : [
          "8"
        ]
      },
      {
        "_index" : "documents",
        "_type" : "mydocument",
        "_id" : "9",
        "_score" : null,
        "_source" : {
          "id" : 9,
          "message" : "message 18"
        },
        "sort" : [
          "9"
        ]
      }
    ]
  }
}

注意源文档已更新

  1. 带有 _id 可被3整除已使用脚本更新更新文档
  2. 文档 圣婴 可被2除尽的部分更新了文档。
  3. 其余文档已通过传递原始文档进行了更新;这将导致 noop 在批量响应中。
14 年前
回复了 Russ Cam 创建的主题 » 将javascript转换为jquery

转换为更简洁的javascript?

var results = xvotesString[htmlid].split('~'),
    elem = function elem(prefix) {
       return document.getElementById(prefix + htmlid);
    }
if(var t3 = elem('xvote-')) 
    t3.style.width = results[0] + 'px';
if(var t4 = elem('mnma-'))
    t4.innerHTML = results[1];
if(var t5 = elem('mnma-'))
    t5.innerHTML = results[2];
if(var t6 = elem('mnmc-'))
    t6.style.display='none';
if(t6 = elem('mnmd-'))
    t6.style.display='block';
if(var t7 = elem('xvotes-'))
    t7.className += ' star-rating-noh';  
13 年前
回复了 Russ Cam 创建的主题 » jquery数组和jquery对象在技术上有什么区别?

第一个是正确的,第二个将失败,因为没有定义回调函数。如果定义了回调函数,并且click事件处理程序应用于中回调内部的每个迭代元素 .each() 然后两者在功能上是相似的。然而, each 调用实际上是多余的,因为jquery命令/方法是 通常地 应用于jquery对象中所有匹配的元素(有某些方法,例如 . val() 这不是,但他们是例外。这是通过内部应用 () :)

jquery对象是具有类似数组的属性的对象;所有匹配的元素都是对象的索引属性,即。

<p>Hello</p><World</p>

$('p'); // is an object { selector: 'p', O: [DOM Element], 1: [DOM Element], ... }

在哪里? [DOM Element] 表示对DOM中与选择器匹配的htmlelement的引用。 类似于数组的优点是它使得对对象执行数组操作非常简单。

5 年前
回复了 Russ Cam 创建的主题 » 如何使用ElasticSearch筛选查询结果

您要寻找的是将多个查询组合在一起的方法:

  1. 查询搜索词
  2. 查询 isGroup
  3. 查询 isUser
  4. 查询 organizationId

并使用这些组合执行搜索。这是类似 bool query 进来。给出以下POCO

public class UserGroupDocument 
{
    public string Name { get; set; }
    public bool IsGroup { get; set; }
    public bool IsUser { get; set; }
    public string OrganizationId { get; set; }
}

我们可以从

private static void Main()
{
    var defaultIndex = "default-index";
    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200")); 
    var settings = new ConnectionSettings(pool)
        .DefaultIndex(defaultIndex);

    var client = new ElasticClient(settings);

    var isUser = true;
    var isGroup = true;
    var organizationId = "organizationId";

    var searchResponse = client.Search<UserGroupDocument>(x => x
        .Index(defaultIndex)
        .Query(q => q
            .Bool(b => b
                .Must(mu => mu
                    .QueryString(mmp => mmp
                        .Query("some admin")
                        .Fields(f => f
                            .Field(ff => ff.Name)
                        )
                    )
                )
                .Filter(fi => 
                    {
                        if (isUser)
                        {
                            return fi
                                .Term(f => f.IsUser, true);
                        }

                        return null;
                    }, fi =>
                    {
                        if (isGroup)
                        {
                            return fi
                                .Term(f => f.IsGroup, true);
                        }

                        return null;
                    }, fi => fi
                    .Term(f => f.OrganizationId, organizationId)
                )
            )
        )
    );
}

这将生成以下查询

POST http://localhost:9200/default-index/usergroupdocument/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "isUser": {
              "value": true
            }
          }
        },
        {
          "term": {
            "isGroup": {
              "value": true
            }
          }
        },
        {
          "term": {
            "organizationId": {
              "value": "organizationId"
            }
          }
        }
      ],
      "must": [
        {
          "query_string": {
            "fields": [
              "name"
            ],
            "query": "some admin"
          }
        }
      ]
    }
  }
}

如果

  • 伊塞尔 false , the term 查询筛选器 伊塞尔 字段将不包括在搜索查询中
  • IS-群 , the 学期 查询筛选器 IS-群 字段将不包括在搜索查询中
  • 组织ID null 或空字符串, 学期 查询筛选器 组织ID 不会包含在搜索查询中。

现在,我们可以走得更远 IS-群 伊塞尔 可空布尔值( bool? )然后,当其中一个值为 无效的 ,各自的 学期 发送到ElasticSearch的搜索查询中将不包含查询筛选器。这利用了一个称为 无条件的 Nest中的查询,旨在使编写更复杂的查询更容易。此外,我们可以使用 operator overloading on queries 使书写更容易 布尔 查询。这意味着我们可以将查询细化到

bool? isUser = true;
bool? isGroup = true;
var organizationId = "organizationId";

var searchResponse = client.Search<UserGroupDocument>(x => x
    .Index(defaultIndex)
    .Query(q => q
        .QueryString(mmp => mmp
            .Query("some admin")
            .Fields(f => f
                .Field(ff => ff.Name)
            )
        ) && +q
        .Term(f => f.IsUser, isUser) && +q
        .Term(f => f.IsGroup, isGroup) && +q
        .Term(f => f.OrganizationId, organizationId)
    )
);
5 年前
回复了 Russ Cam 创建的主题 » ElasticSearch嵌套-GroupBy,然后OrderBy

假设一个模型

public class Student 
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Grade { get; set; }
}

下面将按学生姓名分组,然后按年级降序排列每组中的前x个点击数。

private static void Main()
{
    var defaultIndex = "students";
    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));

    var settings = new ConnectionSettings(pool)
        .DefaultIndex(defaultIndex);

    var client = new ElasticClient(settings);

    if (client.IndexExists(defaultIndex).Exists)
        client.DeleteIndex(defaultIndex);

    var createIndexResponse = client.CreateIndex(defaultIndex, c => c
        .Settings(s => s
            .NumberOfShards(1)
            .NumberOfReplicas(0)
        )
        .Mappings(m => m
            .Map<Student>(mm => mm
                .AutoMap()
            )
        )
    );

    var students = Enumerable.Range(1, 20).Select(i =>
        new Student 
        {
            Id = i,
            Name = i % 2 == 0 ? "Foo" : "Bar",
            Grade = i
        }
    );

    var bulkResponse = client.Bulk(b => b
        .IndexMany(students)
        .Refresh(Refresh.WaitFor) // refresh, so that documents indexed are available to search immediately
    );

    var topX = 10;

    var searchResponse = client.Search<Student>(s => s
        .Aggregations(a => a
            .Terms("student_name", t => t
                .Field(f => f.Name.Suffix("keyword"))
                .Aggregations(aa => aa
                    .TopHits("top_grades", th => th
                        .Sort(so => so
                            .Descending(f => f.Grade)
                        )
                        .Size(topX)
                    )
                )
            )
        )
    );

    var studentNames = searchResponse.Aggregations.Terms("student_name");

    foreach(var bucket in studentNames.Buckets)
    {
        var header = $"Student Name: {bucket.Key}";
        Console.WriteLine(header);
        Console.WriteLine(new string('-', header.Length));
        foreach(var hit in bucket.TopHits("top_grades").Documents<Student>())
        {
            Console.WriteLine($"Id: {hit.Id}, Grade: {hit.Grade}");
        }
        Console.WriteLine();
    }
}

哪个打印出来的

Student Name: Bar
-----------------
Id: 19, Grade: 19
Id: 17, Grade: 17
Id: 15, Grade: 15
Id: 13, Grade: 13
Id: 11, Grade: 11
Id: 9, Grade: 9
Id: 7, Grade: 7
Id: 5, Grade: 5
Id: 3, Grade: 3
Id: 1, Grade: 1

Student Name: Foo
-----------------
Id: 20, Grade: 20
Id: 18, Grade: 18
Id: 16, Grade: 16
Id: 14, Grade: 14
Id: 12, Grade: 12
Id: 10, Grade: 10
Id: 8, Grade: 8
Id: 6, Grade: 6
Id: 4, Grade: 4
Id: 2, Grade: 2

有几个问题

  1. 索引模板定义 "email" 字段映射两次
  2. 索引模板集 "dynamic" 错误,但不包含 "type" 字段映射,因此脚本排序将失败
  3. 整个搜索请求需要在 "source" 对于Put脚本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"
        ]
      }
    ]
  }
}