使用 NEST C# 在弹性搜索中使用多个索引进行全文搜索
Posted
技术标签:
【中文标题】使用 NEST C# 在弹性搜索中使用多个索引进行全文搜索【英文标题】:Full text Search with Multiple index in Elastic Search using NEST C# 【发布时间】:2019-10-22 00:04:42 【问题描述】:我正在尝试使用 NEST 客户端搜索多个索引 Elasticsearch,我只需点击以下链接 [堆放帖]How to search inside multiple indices using Nest ElasticSearch? 唯一的区别是我的索引已经存在但没有返回
示例代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Elasticsearch.Net;
using Nest;
namespace ElasticSearchDemo
public class ExceptionData
public bool HasException get; set;
public string ExceptionMessage get; set;
public class ElasticSearchResponse : ExceptionData
public ISearchResponse<dynamic> elasticSearchResponse get; set;
public class ComponentTypES
public string ComponentID get; set;
public string Componentname get; set;
public string Summary get; set;
public class ProjectTypES
public string ProjectID get; set;
public string Projectname get; set;
public string Summary get; set;
public string Description get; set;
class Program
static void Main(string[] args)
// calling the function
var response = GetAllSearchResults("test", 0, 10);
public static ElasticClient GetElasticSearchCommonSearch()
ElasticClient elasticClient = null;
try
const string strElasticSearchURL = "http://localhost:9200/";
const string componentIndex = "componenttypeindex";
const string projectIndex = "projecttypeindex";
if (!string.IsNullOrEmpty(strElasticSearchURL))
ConnectionSettings connectionSettings = new ConnectionSettings(new Uri(strElasticSearchURL))
.DefaultIndex(componentIndex)
.DefaultMappingFor<ComponentTypES>(i => i.IndexName(componentIndex).TypeName("Componenttype"))
.DefaultMappingFor<ProjectTypES>(j => j.IndexName(projectIndex).TypeName("Projecttype"))
.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");
);
elasticClient = new ElasticClient(connectionSettings);
catch (Exception ex)
throw new Exception(ex.Message + " ConnectionObject for : Common Search");
return elasticClient;
public static ElasticSearchResponse GetAllSearchResults(string query = "test", int
page = 1, int pagesize = 10)
ElasticSearchResponse combinedResponse = new ElasticSearchResponse();
try
ElasticClient elasticClient = GetElasticSearchCommonSearch();
var clusterHealth = elasticClient.ClusterHealth();
if (clusterHealth.IsValid && string.Compare(clusterHealth.Status.ToString(), "red", true) != 0 && clusterHealth.ServerError == null)
string Componentindex = "componenttypeindex";
string Projectindex = "projecttypeindex";
var indices = Indices.Index(typeof(ComponentTypES)).And(typeof(ProjectTypES));
//elasticClient.Refresh(indices);
//TODO : Development time coding
if (null != (indices))
var indexExists = elasticClient.IndexExists(Indices.Index(Componentindex));
var projectExists = elasticClient.IndexExists(Indices.Index(Projectindex));
if (indexExists.Exists && indexExists.IsValid && projectExists.Exists && projectExists.IsValid)
//full text example 1
combinedResponse.elasticSearchResponse = elasticClient.Search<object>(s => s
.Index(indices)
.Type(Types.Type(typeof(ComponentTypES), typeof(ProjectTypES)))
.Query(q => (q
.MultiMatch(m => m
.Fields(f => f
.Field(Infer.Field<ComponentTypES>(ff => ff.Componentname))
.Field(Infer.Field<ComponentTypES>(ff => ff.Summary, 1.1))
)
.Operator(Operator.Or)
.Query(query)
) && +q
.Term("_index", Componentindex)) || (q
.MultiMatch(m => m
.Fields(f => f
.Field(Infer.Field<ProjectTypES>(ff => ff.Projectname))
.Field(Infer.Field<ProjectTypES>(ff => ff.Summary, 0.3))
)
.Operator(Operator.Or)
.Query(query)
) && +q
.Term("_index", Projectindex))
).From(page - 1)
.Size(pagesize)
);
//free text example 2
combinedResponse.elasticSearchResponse = elasticClient.Search<object>(s => s
.Index(indices)
.Type(Types.Type(typeof(ComponentTypES), typeof(ProjectTypES)))
.Query(q => (q
.MatchPhrase(m => m
.Field(Infer.Field<ComponentTypES>(ff => ff.Componentname))
.Query(query)
) && +q
.Term("_index", Componentindex)) || (q
.MatchPhrase(m => m
.Field(Infer.Field<ProjectTypES>(ff => ff.Projectname))
.Query(query)
)
) && +q
.Term("_index", Projectindex)
).From(page - 1)
.Size(pagesize)
);
else
combinedResponse.HasException = true;
combinedResponse.ExceptionMessage = "Index Not Found";
else
combinedResponse.HasException = true;
combinedResponse.ExceptionMessage = "Index Not Found In Config File";
else
combinedResponse.HasException = true;
combinedResponse.ExceptionMessage = "Error on connecting with ElasticSearch";
catch (Exception ex)
combinedResponse.HasException = true;
combinedResponse.ExceptionMessage = ex.Message;
return combinedResponse;
return combinedResponse;
弹性表架构:
PUT componenttypeindex
"mappings":
"Componenttype":
"properties":
"ComponentID":"type":"text",
"Componentname":"type":"text",
"Summary":"type":"text"
PUT projecttypeindex
"mappings":
"Projecttype":
"properties":
"ProjectID":"type":"text",
"Projectname":"type":"text",
"Summary":"type":"text",
"Description":"type":"text"
它应该返回查询匹配的项目,但没有返回 抱歉我尝试了丑陋的代码格式,但新编辑器不会改变任何东西
更新: 我已经按照@RussCam 的建议更新了查询中的索引值,但仍然没有预期的结果,而且当扩展响应对象并直接在浏览器中运行 URI 参数时,它的所有结果都很奇怪,不知道为什么没有显示响应计数
通过对 POST 的成功低级别调用构建的有效 NEST 响应:/componenttypeindex%2Cprojecttypeindex/Componenttype%2CProjecttype/_search?typed_keys=true
此 API 调用的审计跟踪:
[1] 健康响应:节点:http://localhost:9200/ 接受:00:00:00.0620000请求:
URI = "http://localhost:9200/componenttypeindex%2Cprojecttypeindex/Componenttype%2CProjecttype/_search?typed_keys=true"
我的 POCO 课程:
public class ComponentTypES
public string ComponentID get; set;
public string Componentname get; set;
public string Summary get; set;
public class ProjectTypES
public string ProjectID get; set;
public string Projectname get; set;
public string Summary get; set;
public string Description get; set;
样本数据:
PUT componenttypeindex/Componenttype/5342e739-1635-4021-baf2-55e25b95b8ec
"ComponentID":"5342e739-1635-4021-baf2-55e25b95b8ec",
"Componentname":"TestComponent1",
"Summary":"this is summary of test component1"
PUT componenttypeindex/Componenttype/90781386-8065-11e9-bc42-526af7764f64
"ComponentID":"90781386-8065-11e9-bc42-526af7764f64",
"Componentname":"TestComponent2",
"Summary":"this is summary of test component3"
PUT componenttypeindex/Componenttype/19871386-8065-11e9-bc42-526af7764f64
"ComponentID":"19871386-8065-11e9-bc42-526af7764f64",
"Componentname":"some xyz component test",
"Summary":"this is summary test of test xyz"
PUT projecttypeindex/Projecttype/5342e739-2019-4021-baf2-55e25b95b8ec
"ProjectID":"5342e739-2019-4021-baf2-55e25b95b8ec",
"Projectname":"Test Project1",
"Summary":"summary of Test Project1",
"Description":"Description of TestProject1"
PUT projecttypeindex/Projecttype/5342f739-2019-4021-baf2-55e25b95b8ba
"ProjectID":"5342f739-2019-4021-baf2-55e25b95b8ba",
"Projectname":"Test Project2",
"Summary":"summary of Test Project2",
"Description":"Description of TestProject1"
PUT projecttypeindex/Projecttype/6342f739-2020-4021-baf2-55e25b95b8ac
"ProjectID":"6342f739-2020-4021-baf2-55e25b95b8ac",
"Projectname":"some PQRS project",
"Summary":"summary of PQRS Project",
"Description":"Description of PQORS Project1"
【问题讨论】:
您使用的是什么版本的 Elasticsearch?您使用的是哪个版本的 NEST? @RussCam ElasticSearch.NET 6.4.0 版,NEST 6.4.0 版,如果您需要任何其他信息,请告诉我 一个完整的例子真的很有帮助,因为问题中只有部分信息。在您使用.Term("_index", "Componenttype")
的地方,看起来您可以使用componentIndex
,同样,在您使用.Term("_index", "Projecttype")
的地方,您似乎可以使用projectIndex
@RussCam 感谢您的 cmets 将尝试根据您的建议进行更改,我还将添加诸如 POCO 对象之类的附加信息
@RussCam 我已经添加了请求的附加信息,如果您需要任何其他详细信息,请告诉我,我已经运行了返回结果但点击次数始终为零的响应对象 URI 参数,请建议
【参考方案1】:
您的示例中有很多多余的信息,这使得使用起来很棘手,这增加了希望提供帮助的人所需的努力障碍。我是否可以建议您将示例缩减为最小、简洁但完整的示例,以展示您将来面临的问题?这真的有助于更快地解决问题的症结!
我认为根本问题是索引映射中字段内的属性大小写和 NEST 默认发送的属性大小写不同,因此 should
子句中的嵌套 must
子句由于字段大小写的不同,NEST 生成的bool
查询永远不会被任何文档匹配。
NEST by default camel cases property names,但是您的示例中的索引映射和文档中的字段都是帕斯卡大小写的,因此 NEST 生成的字段名称将与映射中的字段名称不匹配。您可以使用ConnectionSettings
上的DefaultFieldNameInferrer(Func<string, string>)
方法轻松更改NEST 的现场套管行为。仅返回传递的字符串值的委托将保留 POCO 上的字段名称。
这是一个完整但简洁的工作示例
private static void Main()
const string componentIndex = "componenttypeindex";
const string projectIndex = "projecttypeindex";
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(pool)
.DefaultIndex(componentIndex)
.DefaultMappingFor<ComponentTypES>(i => i.IndexName(componentIndex).TypeName("Componenttype").IdProperty(f => f.ComponentID))
.DefaultMappingFor<ProjectTypES>(j => j.IndexName(projectIndex).TypeName("Projecttype").IdProperty(f => f.ProjectID))
.DefaultFieldNameInferrer(f => f)
.DefaultTypeName("_doc")
.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);
foreach (var index in new[] componentIndex, projectIndex )
if (client.IndexExists(index).Exists)
client.DeleteIndex(index);
client.CreateIndex(index, c => c
.Mappings(m =>
if (index == projectIndex)
return m.Map<ProjectTypES>(mm => mm.AutoMap());
else
return m.Map<ComponentTypES>(mm => mm.AutoMap());
)
);
client.Bulk(b => b
.IndexMany(new []
new ComponentTypES
ComponentID = "5342e739-1635-4021-baf2-55e25b95b8ec",
Componentname = "TestComponent1",
Summary = "this is summary of test component1"
,
new ComponentTypES
ComponentID = "90781386-8065-11e9-bc42-526af7764f64",
Componentname = "TestComponent2",
Summary = "this is summary of test component3"
,
new ComponentTypES
ComponentID = "19871386-8065-11e9-bc42-526af7764f64",
Componentname = "some xyz component test",
Summary = "this is summary test of test xyz"
,
)
.IndexMany(new []
new ProjectTypES
ProjectID = "5342e739-2019-4021-baf2-55e25b95b8ec",
Projectname = "Test Project1",
Summary = "summary of Test Project1",
Description = "Description of TestProject1"
,
new ProjectTypES
ProjectID = "5342f739-2019-4021-baf2-55e25b95b8ba",
Projectname = "Test Project2",
Summary = "summary of Test Project2",
Description = "Description of TestProject1"
,
new ProjectTypES
ProjectID = "6342f739-2020-4021-baf2-55e25b95b8ac",
Projectname = "some PQRS project",
Summary = "summary of PQRS Project",
Description = "Description of PQORS Project1"
,
)
.Refresh(Refresh.WaitFor)
);
var query = "test";
var response = client.Search<object>(s => s
.Index(Indices.Index(typeof(ComponentTypES)).And(typeof(ProjectTypES)))
.Type(Types.Type(typeof(ComponentTypES), typeof(ProjectTypES)))
.Query(q =>
(q
.MultiMatch(m => m
.Fields(f => f
.Field(Infer.Field<ComponentTypES>(ff => ff.Componentname))
.Field(Infer.Field<ComponentTypES>(ff => ff.Summary, 1.1))
)
.Operator(Operator.Or)
.Query(query)
) && +q
.Term("_index", componentIndex)
) ||
(q
.MultiMatch(m => m
.Fields(f => f
.Field(Infer.Field<ProjectTypES>(ff => ff.Projectname))
.Field(Infer.Field<ProjectTypES>(ff => ff.Summary, 0.3))
)
.Operator(Operator.Or)
.Query(query)
) && +q
.Term("_index", projectIndex)
)
)
);
public class ComponentTypES
public string ComponentID get; set;
public string Componentname get; set;
public string Summary get; set;
public class ProjectTypES
public string ProjectID get; set;
public string Projectname get; set;
public string Summary get; set;
public string Description get; set;
搜索结果的 JSON 查询是
POST http://localhost:9200/componenttypeindex%2Cprojecttypeindex/Componenttype%2CProjecttype/_search?pretty=true&typed_keys=true
"query":
"bool":
"should": [
"bool":
"filter": [
"term":
"_index":
"value": "componenttypeindex"
],
"must": [
"multi_match":
"fields": [
"Componentname",
"Summary^1.1"
],
"operator": "or",
"query": "test"
]
,
"bool":
"filter": [
"term":
"_index":
"value": "projecttypeindex"
],
"must": [
"multi_match":
"fields": [
"Projectname",
"Summary^0.3"
],
"operator": "or",
"query": "test"
]
]
返回 5 个结果
"took" : 53,
"timed_out" : false,
"_shards" :
"total" : 10,
"successful" : 10,
"skipped" : 0,
"failed" : 0
,
"hits" :
"total" : 5,
"max_score" : 0.7549128,
"hits" : [
"_index" : "projecttypeindex",
"_type" : "Projecttype",
"_id" : "5342e739-2019-4021-baf2-55e25b95b8ec",
"_score" : 0.7549128,
"_source" :
"ProjectID" : "5342e739-2019-4021-baf2-55e25b95b8ec",
"Projectname" : "Test Project1",
"Summary" : "summary of Test Project1",
"Description" : "Description of TestProject1"
,
"_index" : "componenttypeindex",
"_type" : "Componenttype",
"_id" : "19871386-8065-11e9-bc42-526af7764f64",
"_score" : 0.5565415,
"_source" :
"ComponentID" : "19871386-8065-11e9-bc42-526af7764f64",
"Componentname" : "some xyz component test",
"Summary" : "this is summary test of test xyz"
,
"_index" : "componenttypeindex",
"_type" : "Componenttype",
"_id" : "5342e739-1635-4021-baf2-55e25b95b8ec",
"_score" : 0.3164503,
"_source" :
"ComponentID" : "5342e739-1635-4021-baf2-55e25b95b8ec",
"Componentname" : "TestComponent1",
"Summary" : "this is summary of test component1"
,
"_index" : "projecttypeindex",
"_type" : "Projecttype",
"_id" : "5342f739-2019-4021-baf2-55e25b95b8ba",
"_score" : 0.2876821,
"_source" :
"ProjectID" : "5342f739-2019-4021-baf2-55e25b95b8ba",
"Projectname" : "Test Project2",
"Summary" : "summary of Test Project2",
"Description" : "Description of TestProject1"
,
"_index" : "componenttypeindex",
"_type" : "Componenttype",
"_id" : "90781386-8065-11e9-bc42-526af7764f64",
"_score" : 0.20706992,
"_source" :
"ComponentID" : "90781386-8065-11e9-bc42-526af7764f64",
"Componentname" : "TestComponent2",
"Summary" : "this is summary of test component3"
]
【讨论】:
非常感谢你拯救了这一天,我在开始工作前一周才刚接触弹性搜索,过去五天我一直在寻找这个解决方案,最后你让它工作了,我确保在以后的问题中添加工作示例,需要一点帮助如何为其他字段添加 where 子句条件,例如 description ="somestring" 和 description != "somestring"以上是关于使用 NEST C# 在弹性搜索中使用多个索引进行全文搜索的主要内容,如果未能解决你的问题,请参考以下文章
为弹性搜索指定和使用带有 C# NEST 客户端的 NGramTokenizer
将对象序列化为 JSON,然后使用 NEST 在弹性搜索中发送查询