Python Elasticsearch的介绍、使用操作详细步骤

ES介绍

官方文档地址:https://elasticsearch-py.readthedocs.io/

什么是 Elasticsearch

想查数据就免不了搜索,搜索就离不开搜索引擎,百度、谷歌都是一个非常庞大复杂的搜索引擎,他们几乎索引了互联网上开放的所有网页和数据。然而对于我们自己的业务数据来说,肯定就没必要用这么复杂的技术了,如果我们想实现自己的搜索引擎,方便存储和检索,Elasticsearch 就是不二选择,它是一个全文搜索引擎,可以快速地储存、搜索和分析海量数据。

Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。

为什么要用 Elasticsearch

Elasticsearch 是一个开源的搜索引擎,建立在一个全文搜索引擎库 Apache Lucene™ 基础之上。

那 Lucene 又是什么?Lucene 可能是目前存在的,不论开源还是私有的,拥有最先进,高性能和全功能搜索引擎功能的库,但也仅仅只是一个库。要用上 Lucene,我们需要编写 Java 并引用 Lucene 包才可以,而且我们需要对信息检索有一定程度的理解才能明白 Lucene 是怎么工作的,反正用起来没那么简单。

那么为了解决这个问题,Elasticsearch 就诞生了。Elasticsearch 也是使用 Java 编写的,它的内部使用 Lucene 做索引与搜索,但是它的目标是使全文检索变得简单,相当于 Lucene 的一层封装,它提供了一套简单一致的 RESTful API 来帮助我们实现存储和检索。

所以 Elasticsearch 仅仅就是一个简易版的 Lucene 封装吗?那就大错特错了,Elasticsearch 不仅仅是 Lucene,并且也不仅仅只是一个全文搜索引擎。 它可以被下面这样准确的形容:

  1. 一个分布式的实时文档存储,每个字段可以被索引与搜索
  2. 一个分布式实时分析搜索引擎
  3. 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据
    总之,是一个相当牛逼的搜索引擎,维基百科、Stack Overflow、GitHub 都纷纷采用它来做搜索

Elasticsearch 相关概念

在 Elasticsearch 中有几个基本的概念,如节点、索引、文档等等,下面来分别说明一下,理解了这些概念对熟悉 Elasticsearch 是非常有帮助的。
Node 和 Cluster
Elasticsearch 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elasticsearch 实例。

单个 Elasticsearch 实例称为一个节点(Node)。一组节点构成一个集群(Cluster)。

Index
Elasticsearch 会索引所有字段,经过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。

所以,Elasticsearch 数据管理的顶层单位就叫做 Index(索引),其实就相当于 MySQL、MongoDB 等里面的数据库的概念。另外值得注意的是,每个 Index (即数据库)的名字必须是小写。

Document
Index 里面单条的记录称为 Document(文档)。许多条 Document 构成了一个 Index。

Document 使用 JSON 格式表示,下面是一个例子。

同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。

Type
Document 可以分组,比如 weather 这个 Index 里面,可以按城市分组(北京和上海),也可以按气候分组(晴天和雨天)。这种分组就叫做 Type,它是虚拟的逻辑分组,用来过滤 Document,类似 MySQL 中的数据表,MongoDB 中的 Collection。

不同的 Type 应该有相似的结构(Schema),举例来说,id 字段不能在这个组是字符串,在另一个组是数值。这是与关系型数据库的表的一个区别。性质完全不同的数据(比如 products 和 logs)应该存成两个 Index,而不是一个 Index 里面的两个 Type(虽然可以做到)。

根据规划,Elastic 6.x 版只允许每个 Index 包含一个 Type,7.x 版将会彻底移除 Type。

Fields
即字段,每个 Document 都类似一个 JSON 结构,它包含了许多字段,每个字段都有其对应的值,多个字段组成了一个 Document,其实就可以类比 MySQL 数据表中的字段。

在 Elasticsearch 中,文档归属于一种类型(Type),而这些类型存在于索引(Index)中,我们可以画一些简单的对比图来类比传统关系型数据库:

Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices   -> Types  -> Documents -> Fields


Python操作Elasticsearch

Elasticsearch 实际上提供了一系列 Restful API 来进行存取和查询操作,我们可以使用 curl 等命令来进行操作,但毕竟命令行模式没那么方便,所以这里我们就直接介绍利用 Python 来对接 Elasticsearch 的相关方法。

参考整理了一下,当做学习笔记,记录一下。
安装模块

pip install elasticsearch

Python操作Elasticsearch
建立连接

from elasticsearch import Elasticsearch
es = Elasticsearch(["http://{0}:{1}".format('127.0.0.1', '9200')],
                             connection_class=MyConnection,
                             proxies={'http': None},
                             timeout=600,
                             max_retries=10,
                             retry_on_timeout=True)
 # 具体参数,大家可以百度,我就不一一解释了。很简单

创建索引

result = es.indices.create(index='news', ignore=400)
print(result)  # {'acknowledged': True, 'index': 'news', 'shards_acknowledged': True} acknowledged 为True表示创建成功

删除索引

result = es.indices.delete("news", ignore=[400, 404])
print(result) # {'acknowledged': True}

插入一条document

# 法一
es.create("news", "politics", body=content, id=1)

# 法二
 es.index("news", doc_type="politics", body=data)  # ok 可以不用指定id, 参数id默认为随机创建

更新数据

# 法一
data = {'date': '2018-01-05 12:30:00',
 'title': 'asd123',
 'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm'}
result = es.update(index='news', doc_type='politics', body=data, id=1)  # error 
print(result)

data_doc = {'doc': {'date': '2018-01-05 12:30:00',
  'title': 'asd123',
  'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm'}}
result = es.update(index='news', doc_type='politics', body=data, id=1)  # ok 
print(result)

# 法二
data = {'date': '2018-01-05 12:30:00',
 'title': 'asd123',
 'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm'}  # ok 使用data_doc也ok
es.index(index='news', doc_type='politics', body=data, id=1)

删除数据

result = es.delete("news", "politics", id='vNaqc2cBE_LRbsBxQ94C')  # ok

查询数据

# 指定分词器 安装一个分词插件,这里使用的是 elasticsearch-analysis-ik
# mapping 信息中指定了分词的字段,指定了字段的类型 type 为 text,分词器 analyzer 和 搜索分词器 search_analyzer 为 ik_max_word
mapping = {
    'properties': {
        'title': {
            'type': 'text',
            'analyzer': 'ik_max_word',
            'search_analyzer': 'ik_max_word'
        }
    }
}

query = {
    'query': {
        'match': {
            'title': '中国 领事馆'
        }
    }
}
es = Elasticsearch()
result = es.search(index='news', doc_type='politics', body=query)
print(result)

python 查询 elasticsearch 常用方法(Query DSL)
查询所有数据

# 方式1:
es.search(index="index_name", doc_type="type_name")
 
# 方式2:
body = {
    "query":{
        "match_all":{}
    }
}
es.search(index="index_name", doc_type="type_name", body=body)

等于查询,term与terms

# term: 查询 xx = “xx”
body = {
    "query":{
        "term":{
            "name":"python"
        }
    }
}
# 查询name="python"的所有数据
es.search(index="index_name",doc_type="type_name",body=body)

# terms: 查询 xx = “xx” 或 xx = “yy”
body = {
    "query":{
        "terms":{
            "name":[
                "ios","android"
            ]
        }
    }
}

# 查询出name="ios"或name="android"的所有数据
es.search(index="index_name",doc_type="type_name",body=body)

包含查询,match与multi_match

# match: 匹配name包含"python"关键字的数据
body = {
    "query":{
        "match":{
            "name":"python"
        }
    }
}
# 查询name包含python关键字的数据
es.search(index="index_name",doc_type="type_name",body=body)
 
# multi_match: 在name和addr里匹配包含深圳关键字的数据
body = {
    "query":{
        "multi_match":{
            "query":"深圳",
            "fields":["name", "addr"]
        }
    }
}
# 查询name和addr包含"深圳"关键字的数据
es.search(index="index_name",doc_type="type_name",body=body)

ids

body = {
    "query":{
        "ids":{
            "type":"type_name",
            "values":[
                "1","2"
            ]
        }
    }
}
# 搜索出id为1或2的所有数据
es.search(index="index_name",doc_type="type_name",body=body)

复合查询bool

bool有3类查询关系,must(都满足),should(其中一个满足),must_not(都不满足)

body = {
    "query":{
        "bool":{
            "must":[
                {
                    "term":{
                        "name":"python"
                    }
                },
                {
                    "term":{
                        "age":18
                    }
                }
            ]
        }
    }
}
# 获取name="python"并且age=18的所有数据
es.search(index="index_name",doc_type="type_name",body=body)

切片式查询

body = {
    "query":{
        "match_all":{}
    }
    "from":2    # 从第二条数据开始
    "size":4    # 获取4条数据
}
# 从第2条数据开始,获取4条数据
es.search(index="index_name",doc_type="type_name",body=body)

范围查询

body = {
    "query":{
        "range":{
            "age":{
                "gte":18,       # >=18
                "lte":30        # <=30
            }
        }
    }
}
# 查询18<=age<=30的所有数据
es.search(index="index_name",doc_type="type_name",body=body)

前缀查询

body = {
    "query":{
        "prefix":{
            "name":"p"
        }
    }
}
# 查询前缀为"赵"的所有数据
es.search(index="index_name",doc_type="type_name",body=body)

通配符查询

body = {
    "query":{
        "wildcard":{
            "name":"*id"
        }
    }
}
# 查询name以id为后缀的所有数据
es.search(index="index_name",doc_type="type_name",body=body)

排序

body = {
    "query":{
        "match_all":{}
    }
    "sort":{
        "age":{                 # 根据age字段升序排序
            "order":"asc"       # asc升序,desc降序
        }
    }
}

# 多字段排序,注意顺序!写在前面的优先排序
body = {
    "query":{
        "match_all":{}
    }
    "sort":[{
        "age":{                # 先根据age字段升序排序
            "order":"asc"      # asc升序,desc降序
        }
    },{
        "name":{               # 后根据name字段升序排序
            "order":"asc"      # asc升序,desc降序
        }
    }],
}

filter_path, 响应过滤

# 只需要获取_id数据,多个条件用逗号隔开
es.search(index="index_name",doc_type="type_name",filter_path=["hits.hits._id"])
 
# 获取所有数据
es.search(index="index_name",doc_type="type_name",filter_path=["hits.hits._*"])

count, 执行查询并获取该查询的匹配数

# 获取数据量
es.count(index="index_name",doc_type="type_name")

度量类聚合
获取最小值

body = {
    "query":{
        "match_all":{}
    },
    "aggs":{                        # 聚合查询
        "min_age":{                 # 最小值的key
            "min":{                 # 最小
                "field":"age"       # 查询"age"的最小值
            }
        }
    }
}
# 搜索所有数据,并获取age最小的值
es.search(index="index_name",doc_type="type_name",body=body)

获取最大值

body = {
    "query":{
        "match_all":{}
    },
    "aggs":{                        # 聚合查询
        "max_age":{                 # 最大值的key
            "max":{                 # 最大
                "field":"age"       # 查询"age"的最大值
            }
        }
    }
}
# 搜索所有数据,并获取age最大的值
es.search(index="index_name",doc_type="type_name",body=body)

获取和

body = {
    "query":{
        "match_all":{}
    },
    "aggs":{                        # 聚合查询
        "sum_age":{                 # 和的key
            "sum":{                 # 和
                "field":"age"       # 获取所有age的和
            }
        }
    }
}
# 搜索所有数据,并获取所有age的和
es.search(index="index_name",doc_type="type_name",body=body)

获取平均值

body = {
    "query":{
        "match_all":{}
    },
    "aggs":{                        # 聚合查询
        "avg_age":{                 # 平均值的key
            "sum":{                 # 平均值
                "field":"age"       # 获取所有age的平均值
            }
        }
    }
}
# 搜索所有数据,获取所有age的平均值
es.search(index="index_name",doc_type="type_name",body=body)

from、size

from:从“第几条”开始查询
size:查询多少条
body = {
    "query":{
        "match_all":{}
    },
    "size":"50",
    "from":"0"
}

参考原文链接:
https://blog.csdn.net/cui_yonghua/article/details/107510609
https://blog.csdn.net/u011361138/article/details/84788175
https://www.cnblogs.com/remainsu/p/python-cha-xun-elasticsearch-chang-yong-fang-fa-qu.html

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值