ElasticSearch在python中的使用(一)

Elasticsearch python3

pip install elastcisearch

python 对Elasticsearch的操作基于Elasticsearch类创建的实例

创建索引index(database)

from elasticsearch import Elasticsearch

es = Elasticsearch()

result = es.indices.create(index='index-name', ignore = 400)
print(result)

# >> 创建成功 {'acknowledged': True, 'shards_acknowledged': True, 'index': 'index-name'}
# >> 捕获重复创建的400错误 {'error': {'root_cause': [{'type': 'resource_already_exists_exception', 're
# ason': 'index [index-name/QM6yz2W8QE-bflKhc5oThw] already exists', 'index_uuid'
# : 'QM6yz2W8QE-bflKhc5oThw', 'index': 'index-name'}], 'type': 'resource_already_
# exists_exception', 'reason': 'index [index-name/QM6yz2W8QE-bflKhc5oThw] already
#  exists', 'index_uuid': 'QM6yz2W8QE-bflKhc5oThw', 'index': 'index-name'}, 'stat
# us': 400}

删除index(database)

from elasticsearch import Elasticsearch

es = Elasticsearch()
result = es.indices.delete(index='index-name', ignore=[400,404])
print(result)

# >> {'acknowledged': True} 成功删除
# >> {'error':{xxxx}, 'status':404} 重复删除404错误捕获

插入数据两种方式

from elasticsearch import Elasticsearch
es = Elasticsearch()
# 插入的数据为json数据, doc_type  相当于数据库中的tableName, id如果不给定会生成随机的唯一字符串
result = es.create(index='index-name', doc_type='table-name', id=1,body={'title', 'ttttt'})
# 第二种插入的方式
result = es.index(index='index_name', doc_type='table-name', id=1, body={'title':'tttt'})
print(result)
"""
>>     {'_index':'index-name',
        '_type':'table-name',
        '_id':'1',
        '_version':1,
        'result':'created', # 插入成功
        '_shards':{'total':2,'successful':1,'failed':0},
        '_seq_no':0,
        '_primary_term':1}
"""

更新数据

from elasticsearch import Elasticsearch
es = Elasticsearch()
data = {}
# 指明id将数据更新
result = es.update(index='index-name', doc_type='table-name', body={'doc':data}, id=1)
print(result)
# 会将整条记录更新
# 返回的结果 中 {'result':'update'}

删除数据

from elasticsearch import Elasticsearch
es = Elasticsearch()
result = es.delete(index='index-name',doc_type='table-name', id=1)
print(result)
# 返回数据中{'result':'delete'}

注意到: 每一条记录都有_version字段, 每当更改一次当前记录的数据,_version字段就会+1

除去增删改的功能外,Elasticsearch的特色在查询,针对中文,需要中文分词工具https://github.com/medcl/elasticsearch-analysis-ik
下载相对应版本的,解压到安装目录的plugin目录下,重启动es,插件将被加载

安装完中文分词后执行

from elasticsearch import Elasticsearch
import json

es = Elasticsearch()

# mapping为某些字段配置字段类型,分词引擎
mapping = {
    'properties': {
        'title': {
            'type': 'text',
            'analyzer': 'ik_max_word',
            'search_analyzer': 'ik_max_word'
        }
    }
}
datas = [
    {
        'title': '美国留给伊拉克的是个烂摊子吗',
        'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm',
        'date': '2011-12-16'},
    {
        'title': '公安部:各地校车将享最高路权',
        'url': 'http://www.chinanews.com/gn/2011/12-16/3536077.shtml',
        'date': '2011-12-16'},
    {
        'title': '中韩渔警冲突调查:韩警平均每天扣1艘中国渔船',
        'url': 'https://news.qq.com/a/20111216/001044.htm',
        'date': '2011-12-17'
    },
    {
        'title': '中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首',
        'url': 'http://news.ifeng.com/world/detail_2011_12/16/11372558_0.shtml',
        'date': '2011-12-18'
    }

]
# 创建database
es.indices.create(index='news', ignore=400)
# 添加分词引擎
es.indices.put_mapping(index='news', doc_type='politics', body=mapping)
# 添加数据
for i, data in enumerate(datas):
    es.index(index='news', doc_type='politics', body=data, id=i + 1)
# 查询所有
res = es.search(index='news', doc_type='politics')
# 全文检索
dql = {
    'query': {
        'match': {
            'title': '中国 领事馆'
        }
    }
}
res = es.search(index='news', doc_type='politics', body=dql)
print(json.dumps(res, indent=4, ensure_ascii=False))



结构化检索


# 精确查找,内部无额外的评分计算,只对文档进行包括和排除计算
# constant_score 非评分模式
# filter 以过滤的方式
# term 执行精确查找
# 返回的结果中_score恒定为1
"select * from table-name where date=`2011-12-18`"
dql_1 = {
    'query': {
        'constant_score': {
            'filter': {
                'term':
                    {
                        'date': '2011-12-18'
                    }
            }
        }
    }
}
# 但是用term查询中文却得不到想要的结果,因为中文分词工具将中文字符串分成一个个小
# token储存, 例如:中国人民  -- 中  国  人  民   中国   人民

参考自: https://blog.csdn.net/wufaliang003/article/details/81368365
中文文档 https://www.elastic.co/guide/cn/index.html

多值过滤->组合过滤器
SELECT product FROM table WHERE (price = 20 OR pid = "aacczz01") AND (price !=30)

通过布尔过滤器组合

{
  "bool" : {
      "must" :[],
      "should":[],
      "must_not":[]
  }
}

其中must=AND should=OR must_not=NOT

将上述sql改为dql

dql = {
    'query': {
        'bool':{
            'should':[
                {'term':{'price':20}},
                {'term':{'pid':'aacczz01'}}
            ],
            'must_not':[{'term':30}]
        }
    }
}

虽然bool过滤器可以集成分支,但是它也就是一个过滤器,所以可做bool过滤器嵌套

select document from table where pid = "xxxx1" or (pid=xxxx2 and price=30)

dql = {
'query':{
    'bool':{
        'should':[
            {'term':{'pir':'xxxx1'}},
            {'bool':{'must':[{'term':{'pid':'xxx2'}},{'term':{'price':30}}]}}
        ]
    }
}
}

select name from goods where price in (20,30)

# 没必要使用bool过滤,直接terms即可
dql = {
    'query':{
        'const_score':{
            'filter':{
                'terms':{
                    'price':[20,30]
                }
            }
        }
    }
}

范围查询

select document from table where price between 20 and 40

dql = {
    'query': {
        'constant_score':{
            'filter':{
                'range':{
                    'price':{
                        'gte':20,
                        'lte':40
                    }
                }
            }
        }
    }
}

使用range过滤器,

gt: >
lt: <
gte: >=
lte: <=

可以只规定一侧边界

时间类型的范围查询

from datetime import datetime
start = datetime.strptime('2018-01-01 12:11:11','%Y-%m-%d %H:%M:%S')
end = datetime.strptime('2018-01-02 11:11:11','%Y-%m-%d %H:%M:%S')
dql = {
    'query':{
        'constant_score': {
            'filter':{
                'range':{
                    'date':{
                        'gt': start,
                        'lt': end
                    }
                }
            }
        }
    }
}
dql_1 = {
    'query': {
        'constant_score': {
            'filter': {
                'range': {
                    'date': {
                        'gt': '2019-01-01T12:00:00',
                        'lt': '2019-01-01T00:00:00||+1d',
                    }
                }
            }
        }
    }
}
# 注意存储的时候转为时间对象存储,比较的时候也使用时间对象比较,使用字符串比较需要注意格式一致 

字符串也是有顺寻的 a<b<…<z,同样的,字符串也是可以用来比较的

处理空值

SELECT tags FROM posts WHERE tags IS NOT NULL

现在有数据

datas = [
    {
        'name': '美团外卖',
        'price': 20,
        'desc': '这是美团外卖',
        'tags': ['美团', None]
    }, {
        'name': '百度地图',
        'price': 30,
        'desc': '这是百度地图',
        'tags': ['地图', '百度']
    }, {
        'name': '支付宝',
        'price': 15,
        'desc': '这是支付宝',
        'tags': []

    }, {
        'name': '蚂蚁花呗',
        'price': 70,
        'desc': '这是蚂蚁花呗',
        'tags': [None, '阿里']
    },
]
# 查询tags字段为非空

dql = {
    'query': {
        'constant_score': {
            'filter': {
                'exists': {'field': 'tags'}
            }
        }
    }
}

SELECT tags FROM posts WHERE tags IS NULL

dql_1 = {
    'query': {
        'bool': {'must_not': {'exists': {'field': 'tags'}}}
    }
}

过滤缓存器,相同的过滤器会被缓存

dql = {
    'query':{
        'constant_score':{
            'filter':{
                'bool':{
                    'should':[
                        {'bool':{
                            'must':[
                                {'term':{'folder':'inbox'}},
                                {'term':{'read':False}},
                            ]
                        }},
                        {'bool':{
                            'must_not':[
                                {'term':{'folder':'inbox'}},
                                {'term':{'important':False}},
                            ]
                        }}
                    ]
                }
            }
        }
    }
}

以上两个对folder的过滤器用的是同一个过滤器,所以在执行第一个的时候就被缓存,第二次被使用的时候,直接在缓存中
获取结果

缓存条件:

  • 最近多次被查询到
  • 文档数量超出总文档数量的3%
  • 如果缓满了,将从使用次数少的中剔除旧的缓存

全文检索

  • 相关性
    • TF/IDF 地理位置临近 模糊相似 等算法
  • 分析
    • 文本转换成token<建立倒排索引, 查询倒排索引>

文本查询:

  • 基于词项
    • term
    • fuzzy
  • 基于全文
    • match
    • query_string

match不但可以处理全文字段,也可以处理精确字段

数据+查询+结果:

datas = [
   {"title": "The quick brown fox"},
   {"title": "The quick brown fox jumps over the lazy dog"},
   {"title": "The quick brown fox jumps over the quick dog"},
   {"title": "Brown fox brown dog"},
]

dql = {
   'query': {
       'match': {
           'title': 'QUICK!'
       }
   }
}
res =  [
     {
       "_index": "my_index",
       "_type": "my_type",
       "_id": "2",
       "_score": 0.59891266,
       "_source": {
         "title": "The quick brown fox jumps over the lazy dog"
       }
     },
     {
       "_index": "my_index",
       "_type": "my_type",
       "_id": "3",
       "_score": 0.39556286,
       "_source": {
         "title": "The quick brown fox jumps over the quick dog"
       }
     },
     {
       "_index": "my_index",
       "_type": "my_type",
       "_id": "1",
       "_score": 0.2876821,
       "_source": {
         "title": "The quick brown fox"
       }
     }
   ]

评分系统是根据关键字在全文中占据的比重计算的

多词语查询

dql = {
    "query": {
        "match": {
            "title": "BROWN DOG!"
        }
    }
}

多词语查询

结果:有brown或者dog,计算评分是按照两个词语来计算的,只有一个词语命中,评分低

如果想提高精度,那么就要使用and配置

dql = {
    "query": {
        "match": {
            "title": {
                "query": "brown dog",
                "operator": "and"
            }
        }
    }
}

以可通过百分比来控制精度

dql = {
    "query": {
        "match": {
            "title": {
                "query": "brown dog",
                "minimum_should_match": "75%"
            }
        }
    }
}

组合查询,结构化查询+全文查询


dql = {
    'query': {
        'bool': {
            'must': [{'match': {'title': 'quick'}}],
            'must_not': [{'match': {'title': 'lazy'}}],
            'should': [{'match': {'title': 'brown'}}, {'match': {'title': 'dog'}}],
        }
    }
}

查询的是有quick没有lazy,有或者没有(brown or dog)

组合查询也是可以控制精度的

dql = {
    'query': {
        'bool': {
            'should': [
                {'match': {'title': 'brown'}},
                {'match': {'title': 'fox'}},
                {'match': {'title': 'dog'}},
            ],
            'minimum_should_match': 2
        }
    }
}

控制最小需要有两个should匹配到

如上所示,控制精度的多词语查询可以转变成为bool查询

现在有查询


dql = {
    "query": {
        "bool": {
            "must": {
                "match": {
                    "content": { 
                        "query":    "full text search",
                        "operator": "and"
                    }
                }
            },
            "should": [ 
                { "match": { "content": "Elasticsearch" }},
                { "match": { "content": "Lucene"        }}
            ]
        }
    }
}

上述查询中must是必须字段,should中,匹配的should越多,结果相关性越高,评分越高

现在又有需求,匹配到Elasticsearch的评分要比Lucene高(权重),可以通过boost来提高字段权重,默认boost是1

dql = {
    "query": {
        "bool": {
            "must": {
                "match": {  
                    "content": {
                        "query":    "full text search",
                        "operator": "and"
                    }
                }
            },
            "should": [
                { "match": {
                    "content": {
                        "query": "Elasticsearch",
                        "boost": 3 
                    }
                }},
                { "match": {
                    "content": {
                        "query": "Lucene",
                        "boost": 2 
                    }
                }}
            ]
        }
    }
}


未完待续…

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值