Elasticsearch操作
指南 https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
Elasticsearch概念
Elasticsearch 是一个开源的搜索引擎,建立在一个全文搜索引擎库 Apache Lucene™ 基础之上。 Lucene 可能是目前存在的,不论开源还是私有的,拥有最先进,高性能和全功能搜索引擎功能的库。但是 Lucene 仅仅只是一个库。Lucene 是 很复杂的。
Elasticsearch 也是使用 Java 编写的,它的内部使用 Lucene 做索引与搜索,但是它的目标是使全文检索变得简单, 通过隐藏 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API。然而,Elasticsearch 不仅仅是 Lucene,并且也不仅仅只是一个全文搜索引擎。 它可以被下面这样准确的形容:
- 一个分布式的实时文档存储,每个字段 可以被索引与搜索。
- 一个分布式实时分析搜索引擎。
- 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据。
Elasticsearch下载启动
Elasticsearch下载地址:https://www.elastic.co/downloads/elasticsearch
# 进入到elasticsearch的bin目录
cd /.../.../elasticsearch-7.7.1/bin
# 启动elasticsearch
./elasticsearch
可以再开启另外一个终端,输入一下命令,测试是否启动成功
curl http://localhost:9200/
说明:如果是在windows下,需要JAVA环境 JDK,并且配置环境变量。
全文搜索
全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。
在全文搜索的世界中,存在着几个主流工具,主要有:
- Apache Lucene
- Elasticsearch
- Solr
- Ferret
倒排索引
索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。Elasticsearch能够实现快速、高效的搜索功能,正是基于倒排索引原理。
Python操作Elasticsearch
官方文档 https://elasticsearch-py.readthedocs.io/en/master/api.html
python下载
pip3 install elasticsearch
添加数据
index(索引),doc_type(文档类型),body(存放数据,JSON类型的)
_index表示文档在哪存放,_type表示文档的对象类别,_id为文档的唯一标识。
from elasticsearch import Elasticsearch
es = Elasticsearch()
# 默认host为localhost,port为9200.但也可以指定host与port
es.index(index="my_index", doc_type="test_type", id=1,
body={"name": "python", "addr": "北京", "age": 10})
es.index(index="my_index", doc_type="test_type", id=2,
body={"name": "java", "addr": "上海", "age": 20})
es.index(index="my_index", doc_type="test_type", id=3,
body={"name": "python", "addr": "上海", "age": 30})
es.index(index="my_index", doc_type="test_type", id=4,
body={"name": "java", "addr": "北京", "age": 40})
# 添加或更新数据,index,doc_type名称可以自定义,id可以根据需求赋值,body为内容
# 更新,ignore=409忽略文档已存在的异常
# es.create(index="my_index", doc_type="test_type",id=1,ignore=409,
# body={"name": "python", "addr": "北京"})
删除数据
form elasticsearch import Elasticsearch
es = Elasticsearch()
# 删除id=1的数据
result = es.delete(index="my_index",doc_type="test_type",id=1)
查询数据
from elasticsearch import Elasticsearch
es = Elasticsearch()
# search查询所有数据
result = es.search(index="my_index", doc_type="test_type")
print(result)
"""
{'took': 1, 'timed_out': False, '_shards': {'total': 1, 'successful': 1, 'skipped': 0, 'failed': 0}, 'hits': {'total': {'value': 1, 'relation': 'eq'}, 'max_score': 1.0, 'hits': [{'_index': 'my_index', '_type': 'test_type', '_id': '1', '_score': 1.0, '_source': {'name': 'python', 'addr': '北京'}}]}}
"""
for i in result["hits"]["hits"]:
print(i["_source"]) # {'name': 'python', 'addr': '北京'}
# 搜索id=1的文档
result = es.get(index="my_index",doc_type="test_type",id=1)
print(result["_source"])
# {'name': 'python', 'addr': '北京'}
# 或者:查询所有的数据
body = {
"query":{
"match_all":{}
}
}
result = es.search(index="my_index",doc_type="test_type",body=body)
term与terms
elasticsearch 里默认的IK分词器是会将每一个中文都进行了分词的切割,所以你直接用term查一整个词,或者一整句话是无返回结果的。
term query会去倒排索引中寻找确切的term,它并不知道分词器的存在,这种查询适合keyword、numeric、date等明确值的,不太适合中文词
# term
body = {
"query": {
"term": {
"name": "python"
}
}
}
# 查询name="python"的所有数据
result = es.search(index="my_index",doc_type="test_type",body=body)
for i in result["hits"]["hits"]:
print(i["_source"]) # {'name': 'python', 'addr': '北京'}
# terms
body = {
"query": {
"terms": {
"name": ["python", "java"]
}
}
}
# 搜索出name="python"或name="java"的所有数据
result = es.search(index="my_index",doc_type="test_type",body=body)
match与multi_match
match query 知道分词器的存在,会对field进行分词操作,然后再查询,可以用于中文。
# match查询name包含python关键字的数据
body = {
"query": {
"match": {
"name": "python"
}
}
}
result = es.search(index="my_index",doc_type="test_type",body=body)
# multi_match:在name和addr里匹配包含北京关键字的数据
body = {
"query": {
"multi_match": {
"query": "北京",
"fields": ["name", "addr"]
}
}
}
result = es.search(index="my_index",doc_type="test_type",body=body)
ids
# 搜索出id为1和2的所有数据
body = {
"query": {
"ids": {
"type": "test_type",
"values": ["1", "2"]
}
}
}
result = es.search(index="my_index", body=body)
复合查询bool
bool有3类查询关系,must
(都满足and), should
(其中一个满足or), must_not
(都不满足not)
{
"bool" : {
"must" : [],
"should" : [],
"must_not" : [],
}
}
# 获取name="python"并且addr是上海的所有数据
body = {
"query": {
"bool": {
"must": [{"match": {"addr": "上海"}},
{"term": {"name": "python"}},]
#注意这must 里边的条件是不能用term查中文词,查不出结果
}
}
}
result = es.search(index="my_index", body=body)
用elasticsearch表达下面的mysql
select product from products where (price=20 or productID = "XHDK-A-123") and (price !=25)
body = {
"query": {
"bool":{
"should": [{"term": {"price": 20}},
{"term": {"productID": "XHDK-A-123"}}],
"must_not": [{"term": {"price": 25}}]
}
}
}
result = es.search(index='my_store',doc_type='products',body=body)
切片查询
body = {
"query": {
"match_all": {}
},
"from": 2, # 从第二条开始(不包含第二条)
"size": 2 # 取2条
}
result = es.search(index='my_store',doc_type='test_type',body=body)
范围查询 - range
gt 大于, lt 小于; 加e 等于
body = {
"query":{
"range":{
"age":{
"gte":18, # >=18
"lte":35 # <=35
}
}
}
}
# 查询18<=age<=35的所有数据
result = es.search(index="my_index",doc_type="test_type",body=body)
前缀查询 - prefix
# 查询前缀为"p"的所有数据
body = {
"query": {
"prefix": {
"name": "p",
}
}
}
result = es.search(index="my_index",doc_type="test_type",body=body)
通配符查询 - wildcard
body = {
"query":{
"wildcard":{
"name":"*n"
}
}
}
# 查询name以n为后缀的所有数据
result = es.search(index="my_index",doc_type="test_type",body=body)
排序 - sort
body = {
"query":{
"match_all":{}
}
"sort":{
"age":{ # 根据age字段升序排序
"order":"asc" # asc升序,desc降序
}
}
}
result = es.search(index="my_index",doc_type="test_type",body=body)
响应过滤 - filter_path
# 只需要获取_id数据, 多个条件用逗号隔开filter_path=["hits.hits._id", "hits.hits._source"]
result = es.search(index="my_index", doc_type="test_type", filter_path=["hits.hits._id"])
# 获取全部
result = es.search(index="my_index", doc_type="test_type", filter_path=["hits.hits._*"])
统计获取结果的数量 - count
# 获取数据量
result=es.count(index="my_index",doc_type="test_type")
print(result) # {'count': 2, '_shards': {'total': 1, 'successful': 1, 'skipped': 0, 'failed': 0}}
度量类聚合
最小值 - min
body = {
"query":{
"match_all":{}
},
"aggs":{ # 聚合查询
"min_age":{ # 最小值的key(方便查询)
"min":{ # 最小
"field":"age" # 查询"age"的最小值
}
}
}
}
# 搜索所有数据,并获取age最小的值
result = es.search(index="my_index",doc_type="test_type",body=body)
print(result["aggregations"]["min_age"]) # {'value': 10.0}
最大值 - max
body = {
"query":{
"match_all":{}
},
"aggs":{ # 聚合查询
"max_age":{ # 最值大的key(方便查询)
"max":{ # 最大
"field":"age" # 查询"age"的最大值
}
}
}
}
# 搜索所有数据,并获取age最大的值
result = es.search(index="my_index",doc_type="test_type",body=body)
print(result["aggregations"]["max_age"]) # {'value': 40.0}
平均值 - avg
body = {
"query":{
"match_all":{}
},
"aggs":{ # 聚合查询
"avg_age":{ # key(方便查询)
"avg":{ # 平均
"field":"age"
}
}
}
}
# 搜索所有数据,并获取age平均值
result = es.search(index="my_index",doc_type="test_type",body=body)
print(result["aggregations"]["avg_age"]) # {'value': 25.0}
总和 - sum
body = {
"query":{
"match_all":{}
},
"aggs":{ # 聚合查询
"sum_age":{ # key(方便查询)
"sum":{ # 总和
"field":"age"
}
}
}
}
# 搜索所有数据,并获取age总和
result = es.search(index="my_index",doc_type="test_type",body=body)
print(result["aggregations"]["sum_age"]) # {'value': 100.0}
返回部分字段 _source
# 加"_source"
body = {
"_source":["name", "age"],
"query":{
"match":{
"name":"python"}
}
}
result=es.search(index="my_index",body=body)
# print(result)
for item in result["hits"]["hits"]:
print(item)
print(item["_source"])