python elasticsearch 修改默认查询最大条数1万条 及浅分页和利用scroll_id深分页

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,传入的值或查询的内容超过这个限制就会报错,可以通过请求更改这个最大限制
首先关闭索引

image.png

然后在复合查询的界面中请求 test/_settings?preserve_existing=true #test为index的名字
然后请求json 为 {"max_result_window":"200000"} 请求方式 为put,
image.png

发起请求后返回true,就设置成功
然后开启索引,查看信息
image.png

可是,设置的太大,往后也会越来越慢,

这个时候,可以用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分页跳页的时候,这也不失为一个解决方法

你可能感兴趣的:(python elasticsearch 修改默认查询最大条数1万条 及浅分页和利用scroll_id深分页)