Elasticsearch的两种分页方式
使用常规的分页方式通用函数
from elasticsearch import Elasticsearch
es = Elasticsearch('127.0.0.1', port=9200)
def search(self, index, doc_type, q_dict={}, search_str='', search_fields=[], fields=[],
page_index=0,page_size=10, all_data=False, body={}, size=1000, sort=''):
'''
查询es的基础函数
:param index: 索引 必填
:param doc_type: 类型 必填
:param q_dict: 精确条件 例:{'name': 'tom', 'age': [12,13,14]}
:param search_str: 模糊查询 例: 'om'
:param search_fields: 模糊查询字段 例 ['name', 'age'] 注:数字等特殊类型不能使用
:param fields: 返回字段 例:['name']
:param page_index: 查询开始的条页,查询从第多少条数据开始查 int
:param page_size: 从查询开始往后查多少条,颗粒理解为每页的大小 int
:param all_data: 是否获取全部的数据 False/Ture
:param body: 可自定义的查询条件
:param size: 每次索引的大小
:param sort: 排序
'''
result = {}
must = []
if q_dict:
for key in q_dict:
if isinstance(q_dict[key], list):
must.append({
'terms': {key: q_dict[key]}
})
else:
must.append({
'term': {key: q_dict[key]}
})
if search_str:
if not search_fields:
search_fields = fields
should = []
for field in search_fields:
should.append({
"wildcard": {
field: "*{}*".format(search_str)
}
})
must.append({"bool": {"should": should}})
dsl = {
'query': {
'bool': {
'must': must,
'must_not': [],
'should': []
}
},"from": self.start_index,
"size": self.paginate_by
}
es_res = es.search(index='backup', doc_type='backup_log', body=dsl, sort=self.ordering)
es_hits = es_res["hits"]["hits"]
result["count"] = es_res["hits"]["total"]
result["data"] = es_hits
return result
修改最大查询1万条的限制
默认情况下,最大查询返回条数size被限制在10000,传入的值或查询的内容超过这个限制就会报错,可以通过请求更改这个最大限制
首先关闭索引
然后在复合查询的界面中请求
test/_settings?preserve_existing=true
#test为index的名字
然后请求json 为
{"max_result_window":"200000"}
请求方式 为put,
发起请求后返回true,就设置成功
然后开启索引,查看信息
可是,设置的太大,往后也会越来越慢,
这个时候,可以用scroll的方式进行分页,起原理类似于关系型数据库的游标,加上scroll参数之后,就会以size的大小作为游标移动的大小进行移动
def search(self, index, doc_type, q_dict={}, search_str='', search_fields=[], fields=[], page_index=1,
page_size=10, all_data=False, body={}, size=1000, sort=''):
result = {}
must = []
if q_dict:
for key in q_dict:
if isinstance(q_dict[key], list):
must.append({
'terms': {key: q_dict[key]}
})
else:
must.append({
'term': {key: q_dict[key]}
})
if search_str:
if not search_fields:
search_fields = fields
should = []
for field in search_fields:
should.append({
"wildcard": {
field: "*{}*".format(search_str)
}
})
must.append({"bool": {"should": should}})
dsl = {
'query': {
'bool': {
'must': must,
'must_not': [],
'should': []
}
},
}
if body:
dsl = body
if fields:
dsl['fields'] = fields
exact_res = self.db.search(index=index, doc_type=doc_type, scroll='1m', body=dsl,
sort=sort.lower(), size=size)
# scroll = '1m'表示这个游标要维护多久,如果过期,将不能在用
num = exact_res.get('hits').get('total')
print 'Total: {}'.format(num)
scroll_id = exact_res.get('_scroll_id') # 获取scroll_id
while True:
p_data = self.db.scroll(scroll_id=scroll_id, scroll='1m')
# 通过scroll_id 拿到本次的值,当值为空的时候表示游标走到了底,其中,每次循环游标会移动size个数据,例如size=1000,总数据为10000,那么每次循环会取100条数据,循环100次会取到全部数据
data = p_data['hits']['hits']
self.db.clear_scroll(scroll_id=scroll_id) # 如果不在使用,调用关闭,不然多次查询会维护很多个scroll,损耗性能
return result
用scroll查询,在每一次的查询中,性能的确是有了,不过,很头疼的是,这样只能一页一页的往下翻,不能到指定页
如果想要跳至指定页,那么,有一个效率一般的方法,可以考虑慎重使用,就是直接通过循环迭代至指定页数,然后把需要的数据通过切片取出来
def search(self, index, doc_type, q_dict={}, search_str='', search_fields=[], fields=[], page_index=1,
page_size=10, all_data=False, body={}, size=1000, sort=''):
page_befor_l = int(int(page_index) / size)
page_over = int(page_index) + int(page_size)
page_over_l = int(page_over / size)
if (page_over_l - page_befor_l) > 1:
raise Exception('page error: The number of query bars cannot exceed the maximum query')
result = {}
must = []
if q_dict:
for key in q_dict:
if isinstance(q_dict[key], list):
must.append({
'terms': {key: q_dict[key]}
})
else:
must.append({
'term': {key: q_dict[key]}
})
if search_str:
if not search_fields:
search_fields = fields
should = []
for field in search_fields:
should.append({
"wildcard": {
field: "*{}*".format(search_str)
}
})
must.append({"bool": {"should": should}})
dsl = {
'query': {
'bool': {
'must': must,
'must_not': [],
'should': []
}
},
}
if body:
dsl = body
if fields:
dsl['fields'] = fields
exact_res = self.db.search(index=index, doc_type=doc_type, scroll='1m', body=dsl,
sort=sort.lower(), size=size)
num = exact_res.get('hits').get('total')
print 'Total: {}'.format(num)
res, res_list, page, ch = None, [], 1, 0
p_o = page_befor_l
if page_befor_l < page_over_l:
ch = page_over - (page_befor_l + 1) * size
scroll_id = exact_res.get('_scroll_id')
while True:
p_data = self.db.scroll(scroll_id=scroll_id, scroll='1m')
if p_o > page:
page += 1
continue
if p_o == 0:
es_data = exact_res['hits']['hits']
else:
es_data = p_data['hits']['hits']
if all_data:
res_list.extend(es_data) # 全量数据
elif ch:
if p_o == 0:
qian = int(page_index)
res_list.extend(es_data[qian:])
res_list.extend(p_data['hits']['hits'][:ch])
break
elif page_over_l == page:
res_list.extend(es_data[:ch])
break
else:
qian = int(page_index) - page_befor_l * size
res_list.extend(es_data[qian:])
page += 1
continue
else:
qian = int(page_index) - int(int(int(page_index) / size) * size)
hou = qian + page_size
res = es_data[qian:hou]
break
if res:
result['data'] = res
else:
result['data'] = res_list
self.db.clear_scroll(scroll_id=scroll_id)
return result
这样就可以返回指定数据,不过,越是往后,效率越低,
根据数据量的大小和翻到指定页的大小和频率,灵活的修改size的大小,可明显看出效率是不一样的,但是在数据量小的时候,也是毫秒间的,在一定量数据的时候,查询效率也是高于from,size的,在数据量超大,而且用必须用es分页跳页的时候,这也不失为一个解决方法