elasticsearch(ES)的使用

1. 安装ES

1.1 安装ES

安装es: 官网下载解压

  • 启动 bin/elasticsearch -d (后台启动)

  • 判断是否成功的标志 curl http://localhost:9200/?pretty
    9200是es的默认端口,可以在config/elasticsearch.yml中修改

    启动成功如下图所示:
    在这里插入图片描述

1.2. 安装插件

  • head 插件
    head插件是es的一个可视化插件,类似于workbench跟mysql的关系

    进入github head github 按照readme 文件安装,我这里选择的是以谷歌浏览器插件的方式安装,如下图
    在这里插入图片描述

  • marvel插件
    marvel插件主要是用来监控的,也可以用来当console来使用

  • postman
    用于和es交互,发送请求

2. ElasticSearch添加密码验证

1. 修改配置文件
在es包下的config目录修改es的默认配置文件elasticsearch.yml,在该文件后追加如下配置

xpack.security.enabled: true
## 加密方式
xpack.license.self_generated.type: basic
xpack.security.transport.ssl.enabled: true

2. 设置密码
重启es让配置文件生效,再到es包下bin目录执行以下命令

.\elasticsearch-setup-passwords interactive

效果如下
在这里插入图片描述
3. postman中登录
在这里插入图片描述
其他地方也是使用同样的方法登录,用户名为:elastic,密码为:123456

3. 概念及用postman初体验

3.1 基本概念:索引、文档、映射

节点 Node、集群 Cluster 和分片 Shards
ElasticSearch 是分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个实例。单个实例称为一个节点(node),一组节点构成一个集群(cluster)。分片是底层的工作单元,文档保存在分片内,分片又被分配到集群内的各个节点里,每个分片仅保存全部数据的一部分。

索引 Index、类型 Type 和文档 Document
如果我们要访问一个文档元数据应该包括囊括 index/type/id 这三种类型

对比 MySQL 数据库:

index → db
type → table
document → row

每一条数据就是一个document, 此处id可以在插入数据时指定,不指定也会自动生成

映射
映射(mapping) 是用来事先定义插入数据中字段的数据类型、字段的权重、分词器等属性,就如同在关系型数据库中创建数据表时会设置字段的类型。在 Elasticsearch 中,映射可分为动态映射和静态映射,静态映射是写入数据之前对字段的属性进行手工设置,动态映射是可直接创建索引并写入文档,文档中字段的类型是 Elasticsearch 自动识别的,不需要在创建索引的时候设置字段的类型。

参考 Elasticsearch 索引映射类型及mapping属性详解

3.2 创建数据

所有语言可以使用 RESTful API 通过端口 9200 和 Elasticsearch 进行通信。一个 Elasticsearch 请求和任何 HTTP 请求一样由若干相同的部件组成:

curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
  • VERB是 HTTP 方法,可选 : GET、 POST、 PUT、 HEAD 或者 DELETE
  • PROTOCOL 即 http 或者 https
  • HOST Elasticsearch 集群中任意节点的主机名,用 localhost 代表本地机器上的节点
  • PORT 运行 Elasticsearch HTTP 服务的端口号,默认是 9200
  • PATH API 的终端路径(例如 _count 将返回集群中文档数量)Path 可能包含多个组件,例如:_cluster/stats 和 _nodes/stats/jvm
  • QUERY_STRING 任意可选的查询字符串参数 (例如 ?pretty 将格式化地输出 JSON 返回值,使其更容易阅读)
  • BODY 一个 JSON 格式的请求体

HTTP 命令PUT新建文档, GET 可以用来检索文档,同样的,可以使用 DELETE 命令来删除文档,以及使用 HEAD 指令来检查文档是否存在。如果想更新已存在的文档,只需再次 PUT

下面使用postman和es来交互进行数据创建和查询:

创建数据
创建员工目录,为每个员工的文档(document)建立索引,每个文档包含了相应员工的所有信息

  1. 每个文档的类型为employee
  2. employee类型归属于索引megacorp
  3. megacorp索引存储在Elasticsearch集群中
PUT /megacorp/employee/3
{
    "first_name" :  "Douglas",
    "last_name" :   "Fir",
    "age" :         35,
    "about":        "I like to build cabinets",
    "interests":  [ "forestry" ]
}

在postman中操作如下
在这里插入图片描述
插入三个数据可以使用head插件查看相关信息:
在这里插入图片描述

3.3 查询数据

可以通过http get来获信息

GET /megacorp/employee/1

返回

{
  "_index" :   "megacorp",
  "_type" :    "employee",
  "_id" :      "1",
  "_version" : 1,
  "found" :    true,
  "_source" :  {
      "first_name" :  "John",
      "last_name" :   "Smith",
      "age" :         25,
      "about" :       "I love to go rock climbing",
      "interests":  [ "sports", "music" ]
    }
}

在这里插入图片描述
_index 索引名称
_type 类型名称
_id id(这个id可以自己指定也可以自动生成)
_version 版本号,每次改动会+1
found true表示在document存在
_source document的全部内容

轻量级搜索
查询字符串

GET /megacorp/employee/_search?q=last_name:Smith

返回

{
    "took": 31,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 0.4700036,
        "hits": [
            {
                "_index": "megacorp",
                "_type": "employee",
                "_id": "1",
                "_score": 0.4700036,
                "_source": {
                    "first_name": "John",
                    "last_name": "Smith",
                    "age": 25,
                    "about": "I love to go rock climbing",
                    "interests": [
                        "sports",
                        "music"
                    ]
                }
            },
            {
                "_index": "megacorp",
                "_type": "employee",
                "_id": "2",
                "_score": 0.4700036,
                "_source": {
                    "first_name": "Jane",
                    "last_name": "Smith",
                    "age": 32,
                    "about": "I like to collect rock albums",
                    "interests": [
                        "music"
                    ]
                }
            }
        ]
    }
}

在这里插入图片描述
Elasticsearch提供丰富且灵活的查询语言叫做DSL查询(Query DSL),它允许你构建更加复杂、强大的查询。DSL(Domain Specific Language特定领域语言)以JSON请求体的形式出现。我们可以这样表示之前关于“Smith”的查询

GET /megacorp/employee/_search
{
    "query" : {
        "match" : {
            "last_name" : "Smith"
        }
    }
}

在这里插入图片描述
过滤器
更复杂的搜索,使用过滤器(filter)来实现sql中where的效果,比如:你想要搜索一个叫Smith,且年龄大于30的员工,可以这么检索.

{
    "query": {
        "bool": {
            "filter": {
                "range": {
                    "age": {
                        "gt": 30
                    }
                }
            },
            "must": {
                "match": {
                    "last_name": "Smith"
                }
            }
        }
    }
}

在这里插入图片描述
短语搜索

GET /megacorp/employee/_search
{
    "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    }
}

match_phrase与match的区别在于,前者会把rock climbing(搜索条件)作为一个整体,而后者会命中rock balabala climbing(后者只要命中其中一个词就算命中,比如rock climbing中命中rock就算匹配中了,就会返回结果)
在这里插入图片描述

4. 常用语法

4.1 match query

用于搜索单个字段,首先会针对查询语句进行解析(经过 analyzer),主要是对查询语句进行分词,分词后查询语句的任何一个词项被匹配,文档就会被搜到,默认情况下相当于对分词后词项进行 or 匹配操作

GET article/_search
{
  "query": {
    "match": {
      "title": {
        "query": "Elasticsearch 查询优化"
      }
    }
  }
}

等同于

GET article/_search
{
  "query": {
    "match": {
      "title":  "Elasticsearch 查询优化"
    }
  }
}

也等同于

GET article/_search
{
  "query": {
    "match": {
      "title": {
        "query": "Elasticsearch 查询优化",
        "operator": "or"
      }
    }
  }
}

如果要匹配所有关键词使用and

GET article/_search
{
  "query": {
    "match": {
      "title": {
        "query": "Elasticsearch 查询优化",
        "operator": "and"
      }
    }
  }
}

4.2 match_phrase query

match_phrase query 首先会把 query 内容分词,分词器可以自定义,同时文档还要满足以下两个条件才会被搜索到:

  • 分词后所有词项都要出现在该字段中(相当于 and 操作)
  • 字段中的词项顺序要一致
GET test_idx/test_tp/_search
{
  "query": {
    "match_phrase": {
      "desc": "what life"
    }
  }
}

4.3 term query

term 查询用来查找指定字段中包含给定单词的文档,term 查询语句不被解析,只有查询词和文档中的词精确匹配才会被搜索到,应用场景为查询人名、地名等需要精准匹配的需求

GET books/_search
{
  "query": {
    "term": {
      "title": "思想"
    }
  }
}

4.4 terms query

terms 查询是 term 查询的升级,可以用来查询文档中包含多个词的文档。比如,想查询 title 字段中包含关键词 “java” “python” 的文档,构造查询语句如下:

{
  "query": {
    "terms": {
      "title": ["java", "python"]
    }
  }
}

4.5 filter query

filter 类似于 SQL 里面的where 语句,和上面的基础查询比起来,也能实现搜索的功能,同时 filter 可以将查询缓存到内存当中,这样可以大大加大下一次的查询速度

下面实现 select document FROM products WHERE price = 20

GET /store/products/_search        // store 索引名   products 商品名
{
	"query":{
	"bool":{
		"query":{
			"match_all":{}   // 匹配所有
		}
		"filter":{
			"term":{ 
				"price":20
				}
			}
		}
	}
}

4.6 range query

range query 即范围查询,用于匹配在某一范围内的数值型、日期类型或者字符串型字段的文档,比如搜索哪些书籍的价格在 50 到 100之间、哪些书籍的出版时间在 2015 年到 2019 年之间。使用 range 查询只能查询一个字段,不能作用在多个字段上

range 查询支持的参数有以下几种:

  • gt 大于
  • gte 大于等于
  • lt 小于
  • lte 小于等于
// 查询价格大于 50,小于等于 70 的书籍
GET bookes/_search
{
  "query": {
    "range": {
      "price": {
        "gt": 50,
        "lte": 70
      }
    }
  }
}

4.7 组合查询[bool query]

bool query(组合查询)是把任意多个简单查询组合在一起,使用 must、should、must_not、filter 选项来表示简单查询之间的逻辑,每个选项都可以出现 0 次到多次。

bool query 主要通过下列 4 个选项来构建用户想要的布尔查询,每个选项的含义如下:

  • must 文档必须匹配 must 选项下的查询条件,相当于逻辑运算的 AND,且参与文档相关度的评分
  • should 文档可以匹配 should 选项下的查询条件也可以不匹配,相当于逻辑运算的 OR,且参与文档相关度的评分
  • must_not 与 must 相反,文档 必须不 匹配这些条件才能被包含进来
  • filter 和 mysql 中 where 语句功能一样,对查询结果进行限制
GET books/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "status": 1
        }
      },
      "must_not": {
        "range": {
          "price": {
            "gte": 70
          }
        }
      },
      "must": {
        "match": {
          "title": "java"
        }
      },
      "should": [
        {
          "match": {
            "description": "虚拟机"
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}

4.8 wildcard 通配符查询

wildcard 查询:允许使用通配符 * 来进行查询

  • * 匹配任意一个或多个字符
  • ? 匹配任意一个字符
GET /library/books/_search
{
	“query”:{
	"wildcard":{
	"preview":"rab*"
	}
    }
}

4.9 boosting query

boosting 查询用于需要对两个查询的评分进行调整的场景,boosting 查询会把两个查询封装在一起并降低其中一个查询的评分。
boosting 查询包括 positive、negative 和 negative_boost 三个部分,positive 中的查询评分保持不变,negative 中的查询会降低文档评分,negative_boost 指明 negative 中降低的权值。

// 对 2015 年之前出版的书降低评分

GET books/_search
{
	"query": {
		"boosting": {
			"positive": {
				"match": {
					"title": "python"
				}
			},
			"negative": {
				"range": {
					"publish_time": {
						"lte": "2015-01-01"
					}
				}
			},
			"negative_boost": 0.2
		}
	}
}

boosting 查询中指定了抑制因子为 0.2,publish_time 的值在 2015-01-01 之后的文档得分不变,publish_time 的值在 2015-01-01 之前的文档得分为原得分的 0.2 倍。

4.10 sort 排序

使用 price、date 和 _score 进行查询,并且匹配的结果首先按照价格排序,然后按照日期排序,最后按照相关性排序

GET books/_search
{
	"query": {
		"bool": {
			"must": {
				"match": { "content": "java" }
			},
			"filter": {
				"term": { "user_id": 4868438 }
			}
		}
	},
	"sort": [{
			"price": {
				"order": "desc"
			}
		}, {
			"date": {
				"order": "desc"
			}
		}, {
			"_score": {
				"order": "desc"
			}
		}
	]
}

排序条件的顺序是很重要的。结果首先按第一个条件排序,仅当结果集的第一个 sort 值完全相同时才会按照第二个条件进行排序,以此类推

5. python中使用es

5.1 使用requests请求操作es

如下图,es中已经存入以下数据,接下来进行增删查改操作
在这里插入图片描述
程序如下

import json
import requests
import os
import base64
import logging

LOG_FORMAT = "%(asctime)s  %(levelname)s  %(message)s"
logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)

def search_es_match(host="localhost", port="9200", index="megacorp", type="employee", id="", operate="search", search_body=dict()):
    '''
    host/port: ip和端口号
    index: 索引名称
    type: es中存储数据的类型,可以为空字符串
    id: es存储的数据的id, 如果需要删除或者修改该条数据,必须指定id
    operate: 进行何种操作: 插入/修改insert, 查询search, 删除delete
    '''
    op_map = {"search":"_search", "insert":"", "delete":""}
    if operate in op_map:
        op = op_map.get(operate)
    else:
        logging.warn("没有该操作类型: %s"%operate)
        return -1
    search_url = os.path.join(f"http://{host}:{port}", index, type, id, op )
    # 有密码时候带上用户及密码
    # auth = str(base64.b64encode(f'{self.username}:{self.password}'.encode('utf-8')), 'utf-8')
    # 请求头中带有Authorization
    headers = {
        "Content-Type": "application/json",
        # 'Authorization': f'Basic {auth}'
    }
    data = json.dumps(search_body)
    requests.DEFAULT_RETRIES = 5  # 增加重试连接次数
    s = requests.session()
    s.keep_alive = False  # 关闭多余连接
    if operate == "delete":
        r = requests.delete(search_url, headers=headers)
    else:
        r = requests.post(search_url, data=data, headers=headers)
    return r.json()

if __name__ == "__main__":
    data = {
        "query":{
            "bool":{

                "filter":{
                    "term":{
                        "last_name":"smith"
                    }
                },
                
                "must":{
                    "range":{
                        "age":{
                            "gt":25
                        }
                    }
                },
                "should":{
                    "match":{
                        "about": "like to"
                    }
                }   
            }   
        }
    }

    data2 = {
        "first_name": "Li",
        "last_name": "Bai",
        "age":72,
        "about": "chuang qian ming yue guang"
    }

    data3 = {
    "first_name": "Li",
    "last_name": "Bai",
    "age":72,
    "about": "chuang qian ming yue guang, yi shi di shang shuang"
    }


    # 插入新数据
    ret1 = search_es_match(id="110", operate="insert", search_body=data2)
    print(ret1)
    # 修改数据
    ret2 = search_es_match(id="110", operate="insert", search_body=data3)
    print(ret2)
    # 删除数据
    ret3 = search_es_match(id="110", operate="delete")
    print(ret3)
    # 查询数据
    ret4 = search_es_match(operate="search", search_body=data)
    print(ret4)

插入数据
ret1 结果

{'_index': 'megacorp', '_type': 'employee', '_id': '110', '_version': 1, 'result': 'created', '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 15, '_primary_term': 5}

在这里插入图片描述
修改数据结果
ret2 结果

{'_index': 'megacorp', '_type': 'employee', '_id': '110', '_version': 2, 'result': 'updated', '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 16, '_primary_term': 5}

在这里插入图片描述
修改内容时,其他不修改的内容也需写入body中,否则默认会赋空值

删除数据结果
ret3 结果

{'_index': 'megacorp', '_type': 'employee', '_id': '110', '_version': 3, 'result': 'deleted', '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 17, '_primary_term': 5}

在这里插入图片描述
查询数据

{'took': 121, 
'timed_out': False, 
'_shards': {'total': 1, 'successful': 1, 'skipped': 0, 'failed': 0}, 
'hits': {'total': {'value': 1, 'relation': 'eq'}, 
		'max_score': 2.257855, 
		'hits': [{'_index': 'megacorp', '_type': 'employee', '_id': '2', '_score': 2.257855, '_source': {'first_name': 'Jane', 'last_name': 'Smith', 'age': 32, 'about': 'I like to collect rock albums', 'interests': ['music']}}]}}

5.2 使用 Elasticsearch 包

参考 python 操作 ElasticSearch 入门
python操作ES数据库

6. 踩坑记录

6.1 es中的term和match的区别

现在es中存储了如下数据
在这里插入图片描述
现在用filter方法来查询 last_name=Smith 的数据,如下图,却未查询到相关的数据
在这里插入图片描述
我们再把 term 换成 match 方法,可以发现能够查询到两个结果
在这里插入图片描述

原因
term是代表完全匹配,也就是精确查询,搜索前不会再对搜索词进行分词拆解,但是Smith这个词在进行存储的时候,进行了分词处理,这里使用的是默认的分词处理器进行了分词处理,使得存入的时候将Smith 存成了 smith, 如下图所示,我们用_ananlyze方法查看存入形式:
在这里插入图片描述
因此我们在用 term 方法查询时,应该查询smith,如下图所示,查询到2个结果
在这里插入图片描述
所以,在默认分词方式下,使用term来精确查询需要全部用小写字母来查询

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值