赞
踩
基础概念了解:
结构化数据:可以用二维表结构来表示的数据。如关系型数据,一个学生表,存储学生的姓名、性别、年龄等信息,用一些关系型数据库就可以解决(Mysql、Oracle等),但是这些列都是定死的,无法扩展;Mysql后续也引入了json数据类型,提高扩展性,但是使用起来还是有点不太方便的。
非结构化数据:无法用二维表来表示的数据。如视频、日志、文档类数据,可以保存到NoSql非关系型数据库中,如Redis、MongoDB,多以k-v结构存储
半结构数据:如一些xml、json数据可存储到Redis、MongoDB等NoSql数据库,但是查询处理不是那么容易的。
那么海量数据包括结构、非结构数据准确查询,ES就产生了。
Elastic Stack的核心:包括Elasticsearch(存储搜索数据的)、Kibana(展示数据的)、Beats和Logstatch(也被称为是ELK Stack)。
ElasticSearch简称ES,是一个开源的高扩展的分布式全文搜索引擎,是整个Elastic Stack的技术栈的核心。它可以近乎实时存储、检索数据;扩展性很好,可以扩展上百台服务器,处理PB级别的数据。
其实常见的搜索工具除了ES外还有Solr,它们都是在lucene的基础上开发的。两者区别不是很大,但是如果涉及到除了搜索文本外还需要处理分析查询、分布式索引、日志管理、监控和指标等,那么毫无疑问,ES是更好的选择。
到es官网下载:下载地址
选择下载的产品
下载Elasticsearch解压缩即可。目录基本上都是能看懂的,bin(可执行文件)、conf(配置)、logs(日志)、jdk
进入到bin后,执行elasticsearch.bat即可执行es(linux系统是sh文件
)。
要注意:9300端口是es集群组件的通信端口,而9200端口是浏览器访问的http协议的Restful端口。启动es后,打开浏览器访问localhost:9200即可。
可以选择跟自己系统已经安装的jdk匹配的es版本,也可以直接去使用es自带的jdk,而不去使用系统已经安装的(不推荐),在elasticsearch-env.bat文件中,修改(将一些不必要的东西使用 rem
注释即可 )
rem if defined JAVA_HOME (
rem set JAVA="%JAVA_HOME%\bin\java.exe"
rem ) else (
set JAVA="%ES_HOME%\jdk\bin\java.exe"
set JAVA_HOME="%ES_HOME%\jdk"
rem )
还可能遇到双击启动后,窗口闪退问题,通过路径访问追踪错误,如果是空间不足
的问题,可以修改config/jvm.options文件,将1g调整为更高的即可。
# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space
-Xms1g
-Xmx1g
es是分布式、Restful风格的搜索和分析。
分布式大家应该都了解,像我们做的一些项目,分别部署到不同的服务器上,达到分布式的部署。
Restful就是指满足http协议,不仅可以访问资源,还可以新增、修改、删除一些资源,像我们之前的URI,就是统一资源定位符,只是用来访问一些资源的,而涉及到Restful风格就会具有更多的功能性,请求如:GET、POST、DELETE、HEAD等。这些请求方式会涉及到幂等性问题(访问多次返回的结果如果是一样的,就是幂等性),而PUT、DELETE就是典型的幂等性接口。
ES发出响应的数据就是JSON格式
看到很多博客里基本上都是把相关的数据格式跟mysql对应起来学习了,我这边也不例外,来张图了解下
ES中,Index可以看做是数据库,Type是表,Document是行数据,然后Fields就是列,但是这些概念违背了ES全文索引的原则和基本思想,后来引入一种新的概念:倒排索引。
既然有倒排索引,那么必然就有正排索引,比如在数据库的一张学生表中,有id和name两个字段,我要通过id去查找到相关的name,如果有模糊查询,我要根据name模糊查询就是正排索引;
倒排索引就是我在新的表中,保存着相应的查询关键字和id值(可能是多个值),我根据关键字去找到对应的id们,然后去学生表中查询id即可。
上图中的Type(也就是mysql中的表)概念已经被弱化了,ES6中,现在一个index中只能包含一个type,在ES7中,Type的概念已经被删除了。
在ES中创建索引就是创建index,可以通过Postman或现流行的国产ApiFox调用都可。
向ES服务器发送PUT
请求:http://localhost:9200/shopping,这个shopping就是index的名称,PUT请求代表是修改的意思。
请求发送后,返回json格式数据,其中index就是索引名称
如果再次请求,服务器会告知你这个index已经存在了,而且牢记新建索引是不支持POST请求的
查询单个索引
向ES服务器发送GET
请求:http://localhost:9200/shopping,还是上文中创建的那个索引,只需要把请求方式改为GET即可。
查询所有索引
向ES服务器发送GET
请求:http://localhost:9200/_cat/indices?v,即可展示所有,目前只有一个索引
向ES服务器发送DELETE
请求:http://localhost:9200/shopping,即可删除索引
向ES服务器发送POST
请求:http://localhost:9200/shopping/_doc,然后在请求体中写入需要的文档数据,发送请求即可。
{
"title":"手机",
"category":"小米",
"img":"http://www.gulixueyuan.com/xm.jpg",
"price":3900.00
}
而且新增文档成功后,会返回的_id是es服务器生成并返回的,每次新建后都会返回不一样的id,所以要满足非幂等性必须要使用POST方式。
生成自定义id
那么我们肯定不会去使用es生成的id,太难记,我们需要自定义id,当我们自定义id时,每次请求后返回的id肯定是相同的,那么会产生幂等性,那么我们无论是使用PUT还是POST都是可以的。
向ES服务器发送POST
请求:http://localhost:9200/shopping/_doc/文档id,然后在请求体中写入需要的文档数据,发送请求即可。
主键id查询
向ES服务器发送GET
请求:http://localhost:9200/shopping/_doc/1001,发送请求即可。(1001是上面创建的文档id)
如果要查询的文档id不存在,那么上图中返回的json数据中,found字段就是false.
全查询
向ES服务器发送GET
请求:http://localhost:9200/shopping/_search,发送请求即可
修改分为两种,一种是完全覆盖,一种是修改部分。
全量修改使用PUT请求
向ES服务器发送PUT
请求:http://localhost:9200/shopping/_doc/1001,写入所有要修改的字段及值,发送请求即可
要注意的是:全量修改时,如果你把某些字段给删了,那么这些字段就不存在于文档中了
局部数据更新
向ES服务器发送POST
请求:http://localhost:9200/shopping/_update/1001,写入要修改的字段及值,发送请求即可,比如要修改文档中的title属性
{
"doc":{
"title":"华为手机"
}
}
向ES服务器发送DELETE
请求:http://localhost:9200/shopping/_doc/1001,发送请求即可
如果删除一个不存在的文档,那么会被告知未找到
向ES服务器发送GET
请求:http://localhost:9200/shopping/_search?q=category:小米,查询所有category是小米的数据
但是大多情况下,路径中有中文时会被转码,所以请求参数还是要写到请求体中的。
GET
请求,访问http://localhost:9200/shopping/_search,带参数
{
"query":{
"match":{
"category":"小米"
}
}
}
同样GET
请求访问以上http://localhost:9200/shopping/_search链接,然后请求体中带参数。
{
"query":{
"match":{
"category":"小米"
}
},
"from":0, // 表示从0的偏移量开始读取
"size":2 // 读取两条数据
}
这个和mysql分页是差不多的,一个是偏移量,一个是行数。公式:(pageNo-1)*页面行数
。
"_source"中就是返回给我们的数据,那么我只想要一个title怎么限制呢?
直接在请求体中设置_source
即可
{
"query":{
"match":{
"category":"小米"
}
},
"from":0,
"size":2,
"_source":["title"]
}
仍然是上面的GET接口,http://localhost:9200/shopping/_search,然后请求体加参数即可。
{
"query":{
"match":{
"category":"小米"
}
},
"from":0,
"size":2,
"sort":{
"price":{ // 以price字段排序,desc降序,asc升序
"order":"desc"
}
}
}
和上文中接口一致,只需在请求体参数语法上改变
must可以理解为and,should理解为or。
{ "query":{ "bool":{ "must":[ { "match":{ "category":"小米" } }, { "match":{ "price":5000 } } ] } } }
{ "query":{ "bool":{ "should":[ { "match":{ "category":"小米" } }, { "match":{ "category":"华为" } } ] } } }
下图为or查询
同上接口,请求体参数改变,以下查询为category是小米或华为的数据,且price大于4500的
{ "query":{ "bool":{ "should":[ { "match":{ "category":"小米" } }, { "match":{ "category":"华为" } } ], "filter":{ "range":{ "price":{ "gt": 4500 } } } } } }
当我们保存文档数据时,es会将数据分词拆解,并将拆解后的数据保存到倒排索引
当中,即使我们查询时只使用关键词的一部分,那么它还是会被查询出来,就叫做全文检索。
GET
请求访问http://localhost:9200/shopping/_search,请求体增加参数
{
"query":{
"match":{
"category":"米"
}
}
}
那么所有category带米
字的所有数据会被查询出来,而且查询时的参数也会被进行拆解查询
{
"query":{
"match":{
"category":"米华"
}
}
}
那么所有category带米
或华
字的所有数据会被查询出来
当然,也可以进行完全匹配
{
"query":{
"match_phrase":{
"category":"米华"
}
}
}
必须是米华
的才会被查询出来。
GET
请求访问http://localhost:9200/shopping/_search,请求体增加参数
{
"query":{
"match_phrase":{
"category":"小米"
}
},
"highlight":{ // 设置高亮
"fields": { // 高亮的字段
"category":{}
}
}
}
GET
请求访问http://localhost:9200/shopping/_search,请求体增加参数
{
"aggs":{
"price_group":{ // 分组名称,随意起名
"terms":{ // 分组
"field":"price" // 分组字段
}
}
},
"size": 0 // 表示不展示原始数据,只展示分组后的
}
若要求平均值
{
"aggs":{
"price_avg":{ // 名称,随意起名
"avg":{ // 平均值
"field":"price" // 求平均值的字段
}
}
},
"size": 0 // 表示不展示原始数据,只展示分组后的
}
映射关系是给index索引设置的。
es6版本http://localhost:9200/user/mapping/_mapping,其他版本http://localhost:9200/user/_mapping
POST
请求http://localhost:9200/user/_mapping,请求体中参数
{ "properties":{ "name":{ "type":"text", "index":true // 表示被索引 }, "sex":{ "type":"keyword", //表示不被拆解 "index":true }, "tel":{ "type":"keyword", "index":false } } }
设置后,同一个接口改为GET
请求方式,可以查询出刚刚设置的mapping.
那么创建一个用户,PUT
请求http://localhost:9200/user/_doc/1001,方法体参数
{
"name":"小米",
"sex":"男性",
"tel":"2222"
}
创建完毕后,查询下试试
GET
请求,http://localhost:9200/user/_search,请求体参数
{
"query":{
"match":{
"name":"小"
}
}
}
刚刚的name属性设置的是text类型,也就是会被拆解,所有name中有小
字的都会被查询出来,如果是keyword类型,则不会被拆解,如查询sex
{
"query":{
"match":{
"sex":"男"
}
}
}
那么查询数据为空,而像tel设置的不被索引,当匹配时将会提示报错。
我们还可以在创建索引库时,字段指定copy_to
,然后让数据拷贝到某个字段中,然后多字段查询;或者可以使用multi_match
进行多字段查询
新建索引时指定copy_to
# 新建索引时,指定copy_to PUT /test { "mappings": { "properties": { "name":{ "type": "text", "analyzer": "standard", "copy_to": "all" }, "brand":{ "type": "keyword", "copy_to": "all" }, "all":{ "type": "text" } } } } # 加两条数据 PUT /test/_doc/1001 { "name":"天生丽质", "brand":"天" } PUT /test/_doc/1002 { "name":"高贵优雅", "brand":"外" } # 通过all字段查询 GET /test/_search { "query": { "match": { "all": "丽外" } } }
结果是,name中带丽
的,和brand中带外
的都会被查询出来
那么如果不指定all字段,我们还可以通过其他的方式进行多字段匹配查询
GET /test/_search
{
"query": {
"multi_match": {
"query": "丽外",
"fields": ["brand", "name"]
}
}
}
结果只查询出来一条,啊?为啥呢,那么重点来了。我们在定义的索引中all
字段用的type类型是text,就是会被分词,但是多条件查询中,brand用的是keyword,不会被分词,copy_to
只是将值赋给了all字段,每条伪数据类似如下
{
"all": ["天生丽质", "天"]
}
{
"all": ["高贵优雅", "外"]
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。