概述
我们在Elasticsearch中存储了数据索引,当然希望通过Elasticseach提供的强大的全文索引能力来搜索文档。Elasticsearch提供了一个强大的查询DSL来定义针对Elasticsearch执行的查询。该DSL基于JSON,在NEST中是以Fluent API和对象初始化器语法的形式供C#/.NET开发者调用。
MatchAll查询
在Elasticsearch中,最简单的查询语法是match_all
,它将返回目标索引中的所有文档,并且所有文档的_score
均为1.0。
使用
match_all
查询时并不是所有匹配的文档都在一个响应中返回,默认情况下,只返回前10个文档,你可以使用from
和size
对结果进行分页。
在NEST中,要实现与Elastisearch API中的match_all
相同的查询语法,则可以使用如下的方式:
var searchResponse = _client.Search<Project>(s => s
.Query(q => q
.MatchAll()
)
);
NEST序列化后得到的JSON结果为:
{
"query": {
"match_all": {}
}
}
在NEST中,MatchAll
方法还提供了一种简写方式:
searchResponse = _client.Search<Project>(s => s
.MatchAll()
);
此写法与上例写法在序列化后得到的JSON是相同的。
前面的两个示例都使用Fluent API来构建查询。NEST还公开了一个对象初始化器语法来构建查询语句,如下:
var searchRequest = new SearchRequest<Project>
{
Query = new MatchAllQuery()
};
searchResponse = _client.Search<Project>(searchRequest);
Search()
方法提供了几个可用参数,你可以点击这里查看更多关于查询参数的细节。
通用查询
默认情况下,文档将按_score
降序返回,其中每次命中的_score
是计算文档与查询条件匹配程度的相关度评分。
NEST提供了许多查询功能,在这里,我们将重点分析三种常用的查询操作:
- 结构化搜索(Structured search)
- 非结构化搜索(Unstructured search)
- 组合查询(Combining queries)
结构化查询
结构化搜索是关于查询具有固有结构的数据。日期、时间和数字都是结构化的,通常需要对这些类型的字段进行查询,以查找精确匹配、范围内的值等。文本也可以结构化,例如,应用于博客文章的关键字标签。
在结构化搜索中,查询的答案总是是或否,即:文档要么与查询匹配,要么不匹配。
术语级查询通常用于结构化搜索。下面是一个示例,它查找以日期开始的文档位于指定范围内:
var searchResponse = _client.Search<Project>(s => s
.Query(q => q
.DateRange(r => r
.Field(f => f.StartedOn)
.GreaterThanOrEquals(new DateTime(2017, 01, 01))
.LessThan(new DateTime(2018, 01, 01))
)
)
);
以上查询语句将生成以下查询JSON:
{
"query": {
"range": {
"startedOn": {
"lt": "2018-01-01T00:00:00",
"gte": "2017-01-01T00:00:00"
}
}
}
}
由于该查询的答案总是是或否,所以我们不想对该查询进行评分。为此,我们可以通过将查询包装在bool
查询过滤中来获得在过滤器上下文中执行的查询:
searchResponse = _client.Search<Project>(s => s
.Query(q => q
.Bool(b => b
.Filter(bf => bf
.DateRange(r => r
.Field(f => f.StartedOn)
.GreaterThanOrEquals(new DateTime(2017, 01, 01))
.LessThan(new DateTime(2018, 01, 01))
)
)
)
)
);
以上查询语句将生成以下查询JSON:
{
"query": {
"bool": {
"filter": [
{
"range": {
"startedOn": {
"lt": "2018-01-01T00:00:00",
"gte": "2017-01-01T00:00:00"
}
}
}
]
}
}
}
在过滤器上下文中执行查询的好处是Elasticsearch可以放弃计算相关度评分,以及缓存过滤器以获得更高的执行效率和查询性能。
注:术语级查询没有分词阶段,也就是说,不会对查询输入进行分词,而是在倒排索引中寻找与输入的精确匹配。作业Elasticsearch的新手,当对索引的字段使用术语级查询时,需要特别注意。
当字段仅用于精确匹配时,应该考虑将其作为关键字数据类型进行索引。如果一个字段同时用于精确匹配和全文搜索,应该考虑使用多个字段(
multi fields
)为其建立索引。
非结构化搜索
Elasticsearch的另一类常用的场景是在全文字段中进行搜索,以找到最相关的文档。
全文搜索用于非结构化搜索。以下示例中,我们使用匹配(match
)查询在Project
索引的LeadDeveloper
的firstName
字段中查找所有包含”Russ”关键字的文档:
var searchResponse = _client.Search<Project>(s => s
.Query(q => q
.Match(m => m
.Field(f => f.LeadDeveloper.FirstName)
.Query("Russ")
)
)
);
NEST生成以下查询JSON:
{
"query": {
"match": {
"leadDeveloper.firstName": {
"query": "Russ"
}
}
}
}
全文查询有分词阶段,即会对查询输入进行分词,并将查询分词器产生的术语与倒排索引中的术语进行比较。
通过将分词器通过映射应用到文本数据类型字段,你可以完全控制搜索分词器和索引分词器。
组合查询
在Elasticsearch中,一个非常常见的场景是将单独的查询组合在一起形成复合查询,其中最常见的是bool
查询L,NEST中同样实现相应的方法:
var searchResponse = _client.Search<Project>(s => s
.Query(q => q
.Bool(b => b
.Must(mu => mu
.Match(m => m
.Field(f => f.LeadDeveloper.FirstName)
.Query("Russ")
), mu => mu
.Match(m => m
.Field(f => f.LeadDeveloper.LastName)
.Query("Cam")
)
)
.Filter(fi => fi
.DateRange(r => r
.Field(f => f.StartedOn)
.GreaterThanOrEquals(new DateTime(2017, 01, 01))
.LessThan(new DateTime(2018, 01, 01))
)
)
)
)
);
NEST将生成以下查询JSON:
{
"query": {
"bool": {
"must": [
{
"match": {
"leadDeveloper.firstName": {
"query": "Russ"
}
}
},
{
"match": {
"leadDeveloper.lastName": {
"query": "Cam"
}
}
}
],
"filter": [
{
"range": {
"startedOn": {
"lt": "2018-01-01T00:00:00",
"gte": "2017-01-01T00:00:00"
}
}
}
]
}
}
}
文档必须满足本例中的所有三个查询才能算成功匹配:
- 姓氏和名字的匹配查询将有助于计算相关性得分,因为这两个查询都在查询上下文中运行
- 针对开始日期(
staredOn
)的范围查询在过滤器(filter
)上下文中运行,因此不计算匹配文档的分数
因为bool
是一个非常常用的查询,NEST重载了Query
运算符,使bool
查询的构建更加简洁,前面的bool
查询可以更简洁地表示为:
searchResponse = _client.Search<Project>(s => s
.Query(q => q
.Match(m => m
.Field(f => f.LeadDeveloper.FirstName)
.Query("Russ")
) && q
.Match(m => m
.Field(f => f.LeadDeveloper.LastName)
.Query("Cam")
) && +q
.DateRange(r => r
.Field(f => f.StartedOn)
.GreaterThanOrEquals(new DateTime(2017, 01, 01))
.LessThan(new DateTime(2018, 01, 01))
)
)
);
查询的响应结果
NEST中,搜索返回的响应是SearchResponse<T>
,其中T
是搜索方法调用中定义的泛型参数类型,响应结果上有相当多的属性,但最常见的可能是. documents
,将在下面演示:
var searchResponse = client.Search<Project>(s => s
.Query(q => q
.MatchAll()
)
);
var projects = searchResponse.Documents;
.Documents
是检索每个命中的_source
的方便快捷方式:
var sources = searchResponse.HitsMetadata.Hits.Select(h => h.Source);
此外,也可以从命中集合中检索与每个命中相关的其他元数据。以下是一个在使用高亮显示时检索命中结果中高亮文档的示例:
var highlights = searchResponse.HitsMetadata.Hits.Select(h => h
.Highlight // 获取高亮搜索结果
);
发表评论
登录用户才能发表评论, 请 登 录 或者 注册