es介绍
1 es:出现是为了解决大数据量搜索的问题
2 符合resful协议,使用java封装,分布式搜索引擎
3 es概念:集群,节点,分片,副本(保证高可用),全文检索
4 把一段文章进行分词,把所有次都建立索引
5 倒排索引:根据分词对文章建立索引
6 es概念:索引,类型,文档
7 es安装:7.9.x,公司7.5
8 6.x之前 和 6.x 和7.x有区别
-6.x之前,一个索引下可以有多个类型
-6.x:还支持这样使用,不允许在一个索引下建多个类型
-7.x:不支持使用,也不允许创建
9 es安装,
-安装jdk:1.8以上
-es安装:解压即用
-kinaba版本要对应,官方提供,不仅仅做客户端使用,解压即用,配置文件指定连接的es服务端地址
-es-head:第三方es客户端,node
-npm install
-npm run start
-需要在es服务端配置允许跨域
View Code
索引操作
# 1 对索引的增删查改
# 2 创建索引
PUT lqz2
{
"settings": {
"index":{
"number_of_shards":5,
"number_of_replicas":1
}
}
}
'''
number_of_shards
每个索引的主分片数,默认值是 5 。这个配置在索引创建后不能修改。
number_of_replicas
每个主分片的副本数,默认值是 1 。对于活动的索引库,这个配置可以随时修改。
'''
# 3 查看索引
#获取lqz2索引的配置信息
GET lqz2/_settings
#获取所有索引的配置信息
GET _all/_settings
#同上
GET _settings
#获取lqz和lqz2索引的配置信息
GET lqz,lqz2/_settings
# 修改索引
## 不建议修改(改只能改副本的数量)
PUT lqz/_settings
{
"number_of_replicas": 3
}
# 不允许改
PUT lqz/_settings
{
"number_of_shards": 3
}
# 删除索引
DELETE lqz
View Code
映射管理
1 mapping type 用来创建类型(类型是表的概念)
2 映射用来定义字段的类型及相关属性
3 非关系型数据库不需要先创建类型,直接存文档,es会默认推测字段类型
4 我们来使用mapping定义,会更准确
5 字段数据类型
string类型:text,keyword,text会分词,keyword不分词了
数字类型:long,integer,short,byte,double,float
日期类型:data
布尔类型:boolean
binary类型:binary
复杂类型:object(实体,对象),nested(列表)
geo类型:geo-point,geo-shape(地理位置)
专业类型:ip,competion(搜索建议)
6 映射参数
View Code
属性 | 描述 | 适合类型 |
store | 值为yes表示存储,no表示不存储,默认为no | all |
index | yes表示分词,no表示不分词,默认为true | text |
null_value | 如果字段为空,可以设置一个默认值,比如"NA"(传过来为空,不能搜索,na可以搜索) | all |
analyzer | 可以设置索引和搜索时用的分析器,默认使用的是standard分析器,还可以使用whitespace,simple。都是英文分析器 | all |
include_in_all | 默认es为每个文档定义一个特殊域_all,它的作用是让每个字段都被搜索到,如果想让某个字段不被搜索到,可以设置为false | all |
format | 时间格式字符串模式 | date |
7 通过映射,创建类型
-6.x之前:
PUT books2
{
"mappings": {
"book":{
"properties":{
"title":{
"type":"text"
},
"price":{
"type":"integer"
},
"addr":{
"type":"keyword"
},
"company":{
"properties":{
"name":{"type":"text"},
"company_addr":{"type":"text"},
"employee_count":{"type":"integer"}
}
},
"publish_date":{"type":"date","format":"yyy-MM-dd"}
}
}
}
}
-7.x以后:
# 创建索引,指定映射
PUT books
{
"mappings": {
"properties":{
"title":{
"type":"text"
},
"price":{
"type":"integer"
},
"addr":{
"type":"keyword"
},
"company":{
"properties":{
"name":{"type":"keyword"},
"company_addr":{"type":"text"},
"employee_count":{"type":"integer"}
}
},
"publish_date":{"type":"date","format":"yyy-MM-dd"}
}
}
}
# 类型(映射创建完成后,插入数据测试)
# 插入文档测试
PUT books/_doc/1
{
"title":"大头儿子小偷爸爸",
"price":100,
"addr":"北京天安门",
"company":{
"name":"我爱北京天安门",
"company_addr":"我的家在东北松花江傻姑娘",
"employee_count":10
},
"publish_date":"2019-08-19"
}
# 再插入一条文档
PUT books/_doc/2
{
"title":"白雪公主和十个小矮人",
"price":"99",
"addr":"黑暗森里",
"company":{
"name":"我的家乡在上海",
"company_addr":"朋友一生一起走",
"employee_count":10
},
"publish_date":"2018-05-19"
}
# 向lqz2插入文档
PUT lqz2/_doc/1
{
"title":"大头儿子小偷爸爸",
"price":100,
"addr":"北京天安门",
"publish_date":"2019-08-19"
}
# 查看映射
GET books/_mapping
GET lqz2/_mapping
# 索引查看
#查看books索引的mapping
GET books/_mapping
#获取所有的mapping
GET _all/_mapping
映射管理
文档操作
# 增加文档,不需要先创建索引和类型
PUT lqz/_doc/1
{
"name":"顾老二",
"age":30,
"from": "gu",
"desc": "皮肤黑、武器长、性格直",
"tags": ["黑", "长", "直"]
}
# 注意点:**当执行`PUT`命令时,如果数据不存在,则新增该条数据,如果数据存在则修改该条数据
文档的增删改查
GET lqz/_doc/1
查看文档
# 替换修改
PUT lqz/_doc/1
{
"desc":"皮肤很黄,武器很长,性格很直",
"tags":["很黄","很长", "很直"]
}
# 局部修改
POST lqz/_update/2
{
"doc": {
"desc": "皮肤很黄,武器很长,性格很直",
"tags": ["很黑","很长", "很直"]
}
}
修改文档
DELETE lqz/_doc/4
删除文档
# 1 根据id查询
GET lqz/_doc/1
# 2 模糊查询方式一
GET lqz/_doc/_search?q=from:gu
# 3 模糊查询方式二(推荐使用)
GET lqz/_doc/_search
{
"query": {
"match": {
"from": "gu"
}
}
}
查询的几种方式
1 match
GET lqz3/_doc/_search
{
"query": {
"match": {
"from": "gu"
}
}
}
2 match_all
GET lqz3/_doc/_search
{
"query": {
"match_all": {
}
}
}
3 match_phrase:slop参数
GET t1/doc/_search
{
"query": {
"match_phrase": {
"title": {
"query": "中国世界",
"slop": 2
}
}
}
}
4 match_phrase_prefix:只针对英文单词
GET t3/doc/_search
{
"query": {
"match_phrase_prefix": {
"desc": "bea"
}
}
}
5 multi_match :多字段查询
GET t3/doc/_search
{
"query": {
"multi_match": {
"query": "beautiful",
"fields": ["title", "desc"]
}
}
}
# term查询如果要查询多个语句直接使用[]
GET w10/doc/_search
{
"query": {
"terms": {
"t1": ["beautiful", "sexy"]
}
}
}
term与match查询
# 对结果进行排序(只能排序可以排序的字段,数字类型,日期类型)
GET lqz3/_doc/_search
{
"query": {
"match": {
"from": "gu"
}
},
"sort": [
{
"age": {
"order": "asc"
}
}
]
}
对查询结果进行排序
# 分页(from是从第几条取,size是取几条)
GET lqz3/_doc/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order": "desc"
}
}
],
"from": 2,
"size": 2
}
分页
#must and的条件
GET lqz3/_doc/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"from": "gu"
}
},
{
"match": {
"age": 30
}
}
]
}
}
}
# or 条件
GET lqz3/_doc/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"from": "gu"
}
},
{
"match": {
"tags": "闭月"
}
}
]
}
}
}
# 都不是
GET lqz3/_doc/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"from": "gu"
}
},
{
"match": {
"tags": "可爱"
}
},
{
"match": {
"age": 18
}
}
]
}
}
}
# 查询某个范围
GET lqz3/_doc/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"from": "gu"
}
}
],
"filter": {
"range": {
"age": {
"gt": 25
}
}
}
}
}
}
## 大于等于小于等于
GET lqz3/_doc/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"from": "gu"
}
}
],
"filter": {
"range": {
"age": {
"lte": 25,
"gte":18
}
}
}
}
}
}
布尔查询
GET lqz3/_doc/_search
{
"query": {
"match": {
"name": "顾老二"
}
},
"_source": ["name", "age"]
}
查询字段过滤
# 结果高亮
GET lqz3/_doc/_search
{
"query": {
"match": {
"name": "石头"
}
},
"highlight": {
"pre_tags": "<b class='key' style='color:red'>",
"post_tags": "</b>",
"fields": {
"name": {}
}
}
}
对结果高亮显示
# 聚合函数 sum,min,max,avg
GET lqz3/_doc/_search
{
"query": {
"match": {
"from": "gu"
}
},
"aggs": {
"my_sum": {
"sum": {
"field": "age"
}
}
},
"_source": ["name", "age"]
}
# 分组查询
GET lqz3/_doc/_search
{
"size": 0,
"query": {
"match_all": {}
},
"aggs": {
"age_group": {
"range": {
"field": "age",
"ranges": [
{
"from": 15,
"to": 20
},
{
"from": 20,
"to": 25
},
{
"from": 25,
"to": 30
}
]
}
}
}
}
聚合查询
1 安装中文分词
-git上把对应版本的分词器包下载
-解压到es的plugins目录下
-重启es,kibana
-创建映射时候,指定分词为中文分词即可
ik分词器
Python操作es方式一
1 mysql user表插入数据,就要把数据同步到es中去
-第一种方案:
-User.object.create()
-插入到es的代码
-第二种方案
-写一个插入es的函数,传入参数,就能插入
-User.object.create()
-调用这个函数
-第三种方案:
-User.object.create()
-提交一个celery任务,异步插入
-第四种方案:
-信号,对象一创建就会触发一个信号
-信号函数种判断如果是User表模型,提交一个celery任务,异步插入
pyes 1
from elasticsearch import Elasticsearch
obj = Elasticsearch()
# 创建索引(Index)
# result = obj.indices.create(index='user2',ignore=400)
# print(result)
# 删除索引
# result = obj.indices.delete(index='lqz5', ignore=[400, 404])
# 插入数据
# data = {'userid': '1', 'username': 'lqz','password':'123'}
# result = obj.create(index='user', doc_type='_doc', id=1, body=data)
# print(result)
# 更新数据
'''
不用doc包裹会报错
ActionRequestValidationException[Validation Failed: 1: script or doc is missing
'''
# data ={'doc':{'userid': '1', 'username': 'asdfasd','password':'sdddd','test':'test'}}
# # result = obj.update(index='user', doc_type='_doc', body=data, id=1)
# result = obj.update(index='user', body=data, id=1)
# print(result)
# 删除数据
# result = obj.delete(index='user', id=1)
# print(result)
# 查询
# 查找所有文档
# query = {'query': {'match_all': {}}}
# 查找名字叫做jack的所有文档
# query = {'query': {'match': {'name': '大娘子'}}}
query={
"query": {
"match_phrase": {
"title": {
"query": "中国世界",
"slop": 2
}
}
}
}
# 查找年龄大于11的所有文档
query = {'query': {'range': {'age': {'gte': 30}}}}
allDoc = obj.search(index='lqz3', body=query)
print(allDoc)
# print(allDoc['hits']['hits'][0]['_source'])
model
Python操作es方式二
from datetime import datetime
from elasticsearch_dsl import Document, Date, Nested, Boolean,analyzer, InnerDoc, Completion, Keyword, Text,Integer
from elasticsearch_dsl.connections import connections
# 连接es
connections.create_connection(hosts=["localhost"])
# Article映射,两个字段
# title是Text类型,分词器使用ik_max_word
class Article(Document):
title = Text(analyzer='ik_max_word', search_analyzer="ik_max_word", fields={'title': Keyword()})
author = Text()
class Index:
name = 'myindex' # 指定索引名
def save(self, ** kwargs):
return super(Article, self).save(** kwargs)
if __name__ == '__main__':
# Article.init() # 创建映射
# # 保存数据
# article = Article()
# article.title = "Go语言测试Python"
# article.save() # 数据就保存了
#查询数据
# s=Article.search()
# s = s.filter('match', title="测试")
#
# results = s.execute()
# print(results)
# for article in results:
# print(article.title)
#删除数据
# s = Article.search()
# s = s.filter('match', title="测试").delete()
#修改数据
s = Article().search()
s = s.filter('match', title="测试")
results = s.execute()
print(results[0])
results[0].title="xxx"
results[0].save()
View Code
es集群搭建两种方式
- 广播
#1 在本地单独的目录中,再复制一份elasticsearch文件
# 2 分别启动bin目录中的启动文件
# 3 在浏览器里输入:http://127.0.0.1:9200/_cluster/health?pretty
-通过number_of_nodes可以看到,目前集群中已经有了两个节点了
- 单播
#现在,我们为这个集群增加一些单播配置,打开各节点内的\config\elasticsearch.yml文件。每个节点的配置如下(原配置文件都被注释了,可以理解为空,我写好各节点的配置,直接粘贴进去,没有动注释的,出现问题了好恢复):
#1 elasticsearch1节点,,集群名称是my_es1,集群端口是9300;节点名称是node1,监听本地9200端口,可以有权限成为主节点和读写磁盘(不写就是默认的)。
cluster.name: my_es1
node.name: node1
network.host: 127.0.0.1
http.port: 9200
transport.tcp.port: 9300
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300", "127.0.0.1:9302", "127.0.0.1:9303", "127.0.0.1:9304"]
# 2 elasticsearch2节点,集群名称是my_es1,集群端口是9302;节点名称是node2,监听本地9202端口,可以有权限成为主节点和读写磁盘。
cluster.name: my_es1
node.name: node2
network.host: 127.0.0.1
http.port: 9202
transport.tcp.port: 9302
node.master: true
node.data: true
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300", "127.0.0.1:9302", "127.0.0.1:9303", "127.0.0.1:9304"]
# 3 elasticsearch3节点,集群名称是my_es1,集群端口是9303;节点名称是node3,监听本地9203端口,可以有权限成为主节点和读写磁盘。
cluster.name: my_es1
node.name: node3
network.host: 127.0.0.1
http.port: 9203
transport.tcp.port: 9303
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300", "127.0.0.1:9302", "127.0.0.1:9303", "127.0.0.1:9304"]
# 4 elasticsearch4节点,集群名称是my_es1,集群端口是9304;节点名称是node4,监听本地9204端口,仅能读写磁盘而不能被选举为主节点。
cluster.name: my_es1
node.name: node4
network.host: 127.0.0.1
http.port: 9204
transport.tcp.port: 9304
node.master: false
node.data: true
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300", "127.0.0.1:9302", "127.0.0.1:9303", "127.0.0.1:9304"]
由上例的配置可以看到,各节点有一个共同的名字my_es1,但由于是本地环境,所以各节点的名字不能一致,我们分别启动它们,它们通过单播列表相互介绍,发现彼此,然后组成一个my_es1集群。谁是老大则是要看谁先启动了!
View Code
es脑裂问题
1 有多个节点下线,又上线,正常应该加入到集群种,由于网络等各种原因导致几个节点成为了新的集群
2 配置文件种规定,集群节点数必须大于总节点数除以二+1
es打分机制
`TF`是词频(term frequency),而`IDF`是逆文档频率
TF词频:词条在文档中出现的次数
IDF`是逆文档频率 :如果一个词条在索引中的不同文档中出现的次数越多,那么它就越不重要
每天逼着自己写点东西,终有一天会为自己的变化感动的。这是一个潜移默化的过程,每天坚持编编故事,自己不知不觉就会拥有故事人物的特质的。 Explicit is better than implicit.(清楚优于含糊)