搜索 DSL
Search
对象
Search
对象代表整个搜索请求
查询
过滤器
聚合
k-最近邻搜索
排序
分页
高亮
建议
折叠
附加参数
关联的客户端
API 设计为可链式调用。除了聚合功能外,这意味着 Search
对象是不可变的 - 对对象的任何更改都会导致创建包含更改的浅层副本。这意味着您可以安全地将 Search
对象传递给外部代码,而不必担心它会修改您的对象,只要它坚持使用 Search
对象 API。
您可以在实例化 Search
对象时传递低级 elasticsearch 客户端 的实例
from elasticsearch import Elasticsearch
from elasticsearch_dsl import Search
client = Elasticsearch()
s = Search(using=client)
您也可以在稍后定义客户端(有关更多选项,请参阅 配置 章节)
s = s.using(client)
注意
所有方法都返回对象的副本,使其可以安全地传递给外部代码。
API 是可链式的,允许您在一个语句中组合多个方法调用
s = Search().using(client).query("match", title="python")
要将请求发送到 Elasticsearch
response = s.execute()
如果您只想遍历搜索返回的命中结果,您可以遍历 Search
对象
for hit in s:
print(hit.title)
搜索结果将被缓存。后续对 execute
的调用或尝试遍历已执行的 Search
对象不会触发发送到 Elasticsearch 的额外请求。要强制请求,请在调用 execute
时指定 ignore_cache=True
。
为了调试目的,您可以将 Search
对象显式序列化为 dict
print(s.to_dict())
根据查询删除
您可以通过在 Search
对象上调用 delete
而不是 execute
来删除匹配搜索的文档,如下所示
s = Search(index='i').query("match", title="python")
response = s.delete()
查询
该库为所有 Elasticsearch 查询类型提供了类。将所有参数作为关键字参数传递。这些类接受任何关键字参数,然后 dsl 将所有传递给构造函数的参数序列化为结果字典中的顶级键(以及发送到 elasticsearch 的结果 json)。这意味着原始查询与其在 DSL 中的等效项之间存在明确的一对一映射
from elasticsearch_dsl.query import MultiMatch, Match
# {"multi_match": {"query": "python django", "fields": ["title", "body"]}}
MultiMatch(query='python django', fields=['title', 'body'])
# {"match": {"title": {"query": "web framework", "type": "phrase"}}}
Match(title={"query": "web framework", "type": "phrase"})
注意
在某些情况下,由于 python 对标识符的限制,这种方法不可行 - 例如,如果您的字段名为 @timestamp
。在这种情况下,您必须回退到解包字典:Range(** {'@timestamp': {'lt': 'now'}})
您可以使用 Q
快捷方式使用带有参数的名称或原始 dict
来构造实例
from elasticsearch_dsl import Q
Q("multi_match", query='python django', fields=['title', 'body'])
Q({"multi_match": {"query": "python django", "fields": ["title", "body"]}})
要将查询添加到 Search
对象,请使用 .query()
方法
q = Q("multi_match", query='python django', fields=['title', 'body'])
s = s.query(q)
该方法还接受所有参数作为 Q
快捷方式
s = s.query("multi_match", query='python django', fields=['title', 'body'])
如果您已经拥有查询对象或表示查询对象的 dict
,则可以覆盖 Search
对象中使用的查询
s.query = Q('bool', must=[Q('match', title='python'), Q('match', body='best')])
点分隔字段
有时您想引用另一个字段中的字段,无论是作为多字段 (title.keyword
) 还是在结构化的 json
文档中,例如 address.city
。为了简化操作,Q
快捷方式(以及 Search
类上的 query
、filter
和 exclude
方法)允许您在关键字参数中使用 __
(双下划线)代替点
s = Search()
s = s.filter('term', category__keyword='Python')
s = s.query('match', address__city='prague')
或者,如果您愿意,您始终可以回退到 python 的 kwarg 解包
s = Search()
s = s.filter('term', **{'category.keyword': 'Python'})
s = s.query('match', **{'address.city': 'prague'})
查询组合
可以使用逻辑运算符组合查询对象
Q("match", title='python') | Q("match", title='django')
# {"bool": {"should": [...]}}
Q("match", title='python') & Q("match", title='django')
# {"bool": {"must": [...]}}
~Q("match", title="python")
# {"bool": {"must_not": [...]}}
当您多次调用 .query()
方法时,内部将使用 &
运算符
s = s.query().query()
print(s.to_dict())
# {"query": {"bool": {...}}}
如果您想精确控制查询形式,请使用 Q
快捷方式直接构造组合查询
q = Q('bool',
must=[Q('match', title='python')],
should=[Q(...), Q(...)],
minimum_should_match=1
)
s = Search().query(q)
过滤器
如果您想在 过滤器上下文 中添加查询,可以使用 filter()
方法来简化操作
s = Search()
s = s.filter('terms', tags=['search', 'python'])
在幕后,这将生成一个 Bool
查询并将指定的 terms
查询放置到其 filter
分支中,使其等效于
s = Search()
s = s.query('bool', filter=[Q('terms', tags=['search', 'python'])])
如果您想使用 post_filter 元素进行面向导航,请使用 .post_filter()
方法。
您还可以像这样从查询中 exclude()
项
s = Search()
s = s.exclude('terms', tags=['search', 'python'])
这是 s = s.query('bool', filter=[~Q('terms', tags=['search', 'python'])])
的简写
聚合
要定义聚合,可以使用 A
快捷方式
from elasticsearch_dsl import A
A('terms', field='tags')
# {"terms": {"field": "tags"}}
要嵌套聚合,可以使用 .bucket()
、.metric()
和 .pipeline()
方法
a = A('terms', field='category')
# {'terms': {'field': 'category'}}
a.metric('clicks_per_category', 'sum', field='clicks')\
.bucket('tags_per_category', 'terms', field='tags')
# {
# 'terms': {'field': 'category'},
# 'aggs': {
# 'clicks_per_category': {'sum': {'field': 'clicks'}},
# 'tags_per_category': {'terms': {'field': 'tags'}}
# }
# }
要将聚合添加到 Search
对象,请使用 .aggs
属性,它充当顶级聚合
s = Search()
a = A('terms', field='category')
s.aggs.bucket('category_terms', a)
# {
# 'aggs': {
# 'category_terms': {
# 'terms': {
# 'field': 'category'
# }
# }
# }
# }
或
s = Search()
s.aggs.bucket('articles_per_day', 'date_histogram', field='publish_date', interval='day')\
.metric('clicks_per_day', 'sum', field='clicks')\
.pipeline('moving_click_average', 'moving_avg', buckets_path='clicks_per_day')\
.bucket('tags_per_day', 'terms', field='tags')
s.to_dict()
# {
# "aggs": {
# "articles_per_day": {
# "date_histogram": { "interval": "day", "field": "publish_date" },
# "aggs": {
# "clicks_per_day": { "sum": { "field": "clicks" } },
# "moving_click_average": { "moving_avg": { "buckets_path": "clicks_per_day" } },
# "tags_per_day": { "terms": { "field": "tags" } }
# }
# }
# }
# }
您可以通过名称访问现有的桶
s = Search()
s.aggs.bucket('per_category', 'terms', field='category')
s.aggs['per_category'].metric('clicks_per_category', 'sum', field='clicks')
s.aggs['per_category'].bucket('tags_per_category', 'terms', field='tags')
注意
在链接多个聚合时,.bucket()
和 .metric()
方法的返回值之间存在差异 - .bucket()
返回新定义的桶,而 .metric()
返回其父桶以允许进一步链接。
与 Search
对象上的其他方法不同,定义聚合是在原地完成的(不返回副本)。
K-最近邻搜索
要发出 kNN 搜索,请使用 .knn()
方法
s = Search()
vector = get_embedding("search text")
s = s.knn(
field="embedding",
k=5,
num_candidates=10,
query_vector=vector
)
field
、k
和 num_candidates
参数可以作为位置参数或关键字参数给出,并且是必需的。除了这些参数外,还必须给出 query_vector
或 query_vector_builder
。
.knn()
方法可以被多次调用以在请求中包含多个 kNN 搜索。
排序
要指定排序顺序,请使用 .sort()
方法
s = Search().sort(
'category',
'-title',
{"lines" : {"order" : "asc", "mode" : "avg"}}
)
它接受位置参数,这些参数可以是字符串或字典。字符串值是字段名称,可以选择以 -
符号为前缀以指定降序。
要重置排序,只需在不带参数的情况下调用该方法即可
s = s.sort()
分页
要指定 from/size 参数,请使用 Python 切片 API
s = s[10:20]
# {"from": 10, "size": 10}
s = s[:20]
# {"size": 20}
s = s[10:]
# {"from": 10}
s = s[10:20][2:]
# {"from": 12, "size": 8}
如果您想访问查询匹配的所有文档,可以使用 scan
方法,该方法使用 scan/scroll elasticsearch API
for hit in s.scan():
print(hit.title)
请注意,在这种情况下,结果将不会被排序。
高亮
要设置高亮的通用属性,请使用 highlight_options
方法
s = s.highlight_options(order='score')
使用 highlight
方法为各个字段启用高亮
s = s.highlight('title')
# or, including parameters:
s = s.highlight('title', fragment_size=50)
然后,响应中的片段将在每个 Result
对象上作为 .meta.highlight.FIELD
提供,其中将包含片段列表
response = s.execute()
for hit in response:
for fragment in hit.meta.highlight.title:
print(fragment)
建议
要在您的 Search
对象上指定一个建议请求,请使用 suggest
方法
# check for correct spelling
s = s.suggest('my_suggestion', 'pyhton', term={'field': 'title'})
第一个参数是建议的名称(将在返回结果中使用的名称),第二个参数是您希望建议器处理的实际文本,关键字参数将按原样添加到建议的 JSON 中,这意味着它应该是 term
、phrase
或 completion
之一,以指示应使用哪种类型的建议器。
折叠
要折叠搜索结果,请在您的 Search
对象上使用 collapse
方法
s = Search().query("match", message="GET /search")
# collapse results by user_id
s = s.collapse("user_id")
热门结果将只包含每个 user_id
的一个结果。您还可以使用 inner_hits
参数扩展每个折叠的热门结果,max_concurrent_group_searches
是允许用于检索每个组的内部命中结果的并发请求数
inner_hits = {"name": "recent_search", "size": 5, "sort": [{"@timestamp": "desc"}]}
s = s.collapse("user_id", inner_hits=inner_hits, max_concurrent_group_searches=4)
类似内容查询
要使用 Elasticsearch 的 more_like_this 功能,您可以使用 MoreLikeThis 查询类型。
下面是一个简单的示例
from elasticsearch_dsl.query import MoreLikeThis
from elasticsearch_dsl import Search
my_text = 'I want to find something similar'
s = Search()
# We're going to match based only on two fields, in this case text and title
s = s.query(MoreLikeThis(like=my_text, fields=['text', 'title']))
# You can also exclude fields from the result to make the response quicker in the normal way
s = s.source(exclude=["text"])
response = s.execute()
for hit in response:
print(hit.title)
额外属性和参数
要设置搜索请求的额外属性,请使用 .extra()
方法。这可以用于定义无法通过特定 API 方法(如 explain
或 search_after
)定义的主体中的键
s = s.extra(explain=True)
要设置查询参数,请使用 .params()
方法
s = s.params(routing="42")
如果您需要限制 Elasticsearch 返回的字段,请使用 source()
方法
# only return the selected fields
s = s.source(['title', 'body'])
# don't return any fields, just the metadata
s = s.source(False)
# explicitly include/exclude fields
s = s.source(includes=["title"], excludes=["user.*"])
# reset the field selection
s = s.source(None)
序列化和反序列化
搜索对象可以使用 .to_dict()
方法序列化为字典。
您还可以使用 from_dict
类方法从 dict
创建一个 Search
对象。这将创建一个新的 Search
对象,并使用字典中的数据填充它
s = Search.from_dict({"query": {"match": {"title": "python"}}})
如果您希望修改现有的 Search
对象,覆盖其属性,请改用 update_from_dict
方法,该方法会**就地**更改实例
s = Search(index='i')
s.update_from_dict({"query": {"match": {"title": "python"}}, "size": 42})
响应
您可以通过调用 .execute()
方法执行搜索,该方法将返回一个 Response
对象。 Response
对象允许您通过属性访问访问响应字典中的任何键。它还提供了一些方便的助手
response = s.execute()
print(response.success())
# True
print(response.took)
# 12
print(response.hits.total.relation)
# eq
print(response.hits.total.value)
# 142
print(response.suggest.my_suggestions)
如果您想检查 response
对象的内容,只需使用其 to_dict
方法即可访问原始数据以进行漂亮打印。
命中结果
要访问搜索返回的命中结果,请访问 hits
属性,或者直接迭代 Response
对象
response = s.execute()
print('Total %d hits found.' % response.hits.total)
for h in response:
print(h.title, h.body)
注意
如果您只看到部分结果(例如 10000 甚至 10 个结果),请考虑使用选项 s.extra(track_total_hits=True)
来获取完整的命中结果计数。
结果
单个命中结果被包装在一个方便的类中,该类允许对返回字典中的键进行属性访问。所有结果的元数据都可以通过 meta
(没有前导的 _
)访问
response = s.execute()
h = response.hits[0]
print('/%s/%s/%s returned with score %f' % (
h.meta.index, h.meta.doc_type, h.meta.id, h.meta.score))
注意
如果您的文档有一个名为 meta
的字段,您必须使用获取项语法访问它:hit['meta']
。
聚合
聚合可以通过 aggregations
属性访问
for tag in response.aggregations.per_tag.buckets:
print(tag.key, tag.max_lines.value)
MultiSearch
如果您需要同时执行多个搜索,可以使用 MultiSearch
类,它将使用 _msearch
API
from elasticsearch_dsl import MultiSearch, Search
ms = MultiSearch(index='blogs')
ms = ms.add(Search().filter('term', tags='python'))
ms = ms.add(Search().filter('term', tags='elasticsearch'))
responses = ms.execute()
for response in responses:
print("Results for query %r." % response._search.query)
for hit in response:
print(hit.title)
EmptySearch
EmptySearch
类可以用作 Search
的完全兼容版本,它将不返回任何结果,无论配置了哪些查询。