全文检索工具elasticsearch:第一章:理论知识

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 全文检索工具elasticsearch:第一章:理论知识

搜索



什么是搜索, 计算机根据用户输入的关键词进行匹配,从已有的数据库中摘录出相关的记录反馈给用户。


常见的全网搜索引擎,像百度、谷歌这样的。但是除此以外,搜索技术在垂直领域也有广泛的使用,比如淘宝、京东搜索商品,万芳、知网搜索期刊,csdn中搜索问题贴。也都是基于海量数据的搜索。


  如何处理搜索


   用传统关系性数据库


20190118201851619.png

弊端:  


1、 对于传统的关系性数据库对于关键词的查询,只能逐字逐行的匹配,性能非常差。


2、匹配方式不合理,比如搜索“小密手机” ,如果用like进行匹配, 根本匹配不到。但是考虑使用者的用户体验的话,除了完全匹配的记录,还应该显示一部分近似匹配的记录,至少应该匹配到“手机”。


  专业全文索引是怎么处理的


全文搜索引擎目前主流的索引技术就是倒排索引的方式。

  传统的保存数据的方式都是

     记录→单词

而倒排索引的保存数据的方式是

  单词→记录

例如

 搜索“红海行动”

但是数据库中保存的数据如图:

20190118201911968.png

那么搜索引擎是如何能将两者匹配上的呢?

基于分词技术构建倒排索引

首先每个记录保存数据时,都不会直接存入数据库。系统先会对数据进行分词,然后以倒排索引结构保存。如下:

20190118201921202.png

然后等到用户搜索的时候,会把搜索的关键词也进行分词,会把“红海行动”分词分成:红海和行动两个词。


这样的话,先用红海进行匹配,得到id=1和id=2的记录编号,再用行动匹配可以迅速定位id为1,3的记录。


那么全文索引通常,还会根据匹配程度进行打分,显然1号记录能匹配的次数更多。所以显示的时候以评分进行排序的话,1号记录会排到最前面。而2、3号记录也可以匹配到。


 全文检索工具elasticsearch


 lucene与elasticsearch


咱们之前讲的处理分词,构建倒排索引,等等,都是这个叫lucene的做的。那么能不能说这个lucene就是搜索引擎呢?


还不能。lucene只是一个提供全文搜索功能类库的核心工具包,而真正使用它还需要一个完善的服务框架搭建起来的应用。


好比lucene是类似于jdk,而搜索引擎软件就是tomcat 的。


目前市面上流行的搜索引擎软件,主流的就两款,elasticsearch和solr,这两款都是基于lucene的搭建的,可以独立部署启动的搜索引擎服务软件。由于内核相同,所以两者除了服务器安装、部署、管理、集群以外,对于数据的操作,修改、添加、保存、查询等等都十分类似。就好像都是支持sql语言的两种数据库软件。只要学会其中一个另一个很容易上手。


从实际企业使用情况来看,elasticSearch的市场份额逐步在取代solr,国内百度、京东、新浪都是基于elasticSearch实现的搜索功能。国外就更多了 像维基百科、GitHub、Stack Overflow等等也都是基于ES的


 elasticSearch的使用场景


  1. 为用户提供按关键字查询的全文搜索功能。
  2. 著名的ELK框架(ElasticSearch,Logstash,Kibana),实现企业海量日志的处理分析的解决方案。大数据领域的重要一份子。

 elasticSearch的安装


全文检索工具elasticsearch:第二章:安装配置


  elasticsearch的基本概念


cluster

整个elasticsearch 默认就是集群状态,整个集群是一份完整、互备的数据。

node

集群中的一个节点,一般只一个进程就是一个node

shard

分片,即使是一个节点中的数据也会通过hash算法,分成多个片存放,默认是5片。

index

相当于rdbms的database, 对于用户来说是一个逻辑数据库,虽然物理上会被分多个shard存放,也可能存放在多个node中。

type

类似于rdbms的table,但是与其说像table,其实更像面向对象中的class , 同一Json的格式的数据集合。

document

类似于rdbms的 row、面向对象里的object

field

相当于字段、属性


 利用kibana学习 elasticsearch restful api (DSL)


执行bin目录下的kibana程序:

cd /opt/kibana-5.6.4-linux-x86_64/bin

./kibana


   es中保存的数据结构


public class  Movie {


String id;


    String name;


    Double doubanScore;


    List<Actor> actorList;


}



public class Actor{


String id;


String name;


}


这两个对象如果放在关系型数据库保存,会被拆成2张表,但是elasticsearch是用一个json来表示一个document。


所以他保存到es中应该是:

{


 “id”:”1”,


 “name”:”operation red sea”,


 “doubanScore”:”8.5”,


 “actorList”:[  


{“id”:”1”,”name”:”zhangyi”},


{“id”:”2”,”name”:”haiqing”},


{“id”:”3”,”name”:”zhanghanyu”}


]


}


对数据的操作增删改查


查看es中有哪些索引

GET /_cat/indices?v


es 中会默认存在一个名为.kibana的索引

health status index   uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   .kibana sBDZ-v6YQMWx9GaQOmSQQg   1   1          1            0      3.2kb          3.2kb


表头的含义


health

green(集群完整) yellow(单点正常、集群不完整) red(单点不正常)

status

是否能使用

index

索引名

uuid

索引统一编号         

pri

主节点几个

rep

从节点几个

docs.count

文档数

docs.deleted

文档被删了多少

store.size

整体占空间大小

pri.store.size

主节点占


增加一个索引

PUT /movie_index

{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "movie_index"
}


删除一个索引

     ES 是不删除也不修改任何数据

DELETE /movie_index

{
  "acknowledged": true
}


新增文档 

格式 :PUT /index/type/id

PUT /movie_index/movie/1


{ "id":1,


 "name":"operation red sea",


 "doubanScore":8.5,


 "actorList":[  


{"id":1,"name":"zhang yi"},


{"id":2,"name":"hai qing"},


{"id":3,"name":"zhang han yu"}


]


}


PUT /movie_index/movie/2


{


 "id":2,


 "name":"operation meigong river",


 "doubanScore":8.0,


 "actorList":[  


{"id":3,"name":"zhang han yu"}


]


}



PUT /movie_index/movie/3


{


 "id":3,


 "name":"incident red sea",


 "doubanScore":5.0,


 "actorList":[  


{"id":4,"name":"zhang chen"}


]


}

20190118203637138.png

如果之前没建过index或者type,es 会自动创建。

 直接用id查找

GET movie_index/movie/1


 修改整体替换

和新增没有区别

PUT /movie_index/movie/3


{


 "id":"3",


 "name":"incident red sea",


 "doubanScore":"5.0",


 "actorList":[  


{"id":"1","name":"zhang chen"}


]


}

20190118203834262.png

修改—某个字段

POST movie_index/movie/3/_update

{

  "doc": {

    "doubanScore":"7.0"

  }

}


修改—某个字段和 修改整体替换二者选一,否则:

20190118204539341.png


删除一个document

DELETE movie_index/movie/3

{
  "found": true,
  "_index": "movie_index",
  "_type": "movie",
  "_id": "3",
  "_version": 18,
  "result": "deleted",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  }
}


搜索type全部数据

GET movie_index/movie/_search


结果

{


 "took": 2,    //耗费时间 毫秒


 "timed_out": false, //是否超时


 "_shards": {


   "total": 5,   //发送给全部5个分片


   "successful": 5,


   "skipped": 0,


   "failed": 0


 },


 "hits": {


   "total": 3,  //命中3条数据


   "max_score": 1,   //最大评分


   "hits": [  // 结果


     {


       "_index": "movie_index",


       "_type": "movie",


       "_id": 2,


       "_score": 1,


       "_source": {


         "id": "2",


         "name": "operation meigong river",


         "doubanScore": 8.0,


         "actorList": [


           {


             "id": "1",


             "name": "zhang han yu"


           }


         ]


       }


         。。。。。。。。


         。。。。。。。。


     }


按条件查询(全部)

GET movie_index/movie/_search

{

  "query":{

    "match_all": {}

  }

}


按分词查询 

GET movie_index/movie/_search

{

  "query":{

    "match": {"name":"red"}

  }

}


注意结果的评分

20190118205002353.png

按分词子属性查询 

GET movie_index/movie/_search

{

  "query":{

    "match": {"actorList.name":"zhang"}

  }

}

结果:

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 1,
    "hits": [
      {
        "_index": "movie_index",
        "_type": "movie",
        "_id": "2",
        "_score": 1,
        "_source": {
          "id": 2,
          "name": "operation meigong river",
          "doubanScore": 8,
          "actorList": [
            {
              "id": 3,
              "name": "zhang han yu"
            }
          ]
        }
      },
      {
        "_index": "movie_index",
        "_type": "movie",
        "_id": "1",
        "_score": 1,
        "_source": {
          "id": 1,
          "name": "operation red sea",
          "doubanScore": 8.5,
          "actorList": [
            {
              "id": 1,
              "name": "zhang yi"
            },
            {
              "id": 2,
              "name": "hai qing"
            },
            {
              "id": 3,
              "name": "zhang han yu"
            }
          ]
        }
      }
    ]
  }
}


match phrase

GET movie_index/movie/_search

{

    "query":{

      "match_phrase": {"name":"operation red"}

    }

}

按短语查询,不再利用分词技术,直接用短语在原始数据中匹配

我就不发结果了,太长的博客也不好看。


 fuzzy查询

GET movie_index/movie/_search

{

    "query":{

      "fuzzy": {"name":"rad"}

    }

}

校正匹配分词,当一个单词都无法准确匹配,es通过一种算法对非常接近的单词也给与一定的评分,能够查询出来,但是消耗更多的性能。


过滤--查询后过滤


GET movie_index/movie/_search


{


   "query":{


     "match": {"name":"red"}


   },


   "post_filter":{


     "term": {


       "actorList.id": 3


     }


   }


}

————————————————

版权声明:本文为CSDN博主「我是廖志伟」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/java_wxid/article/details/86543331过滤--查询后过滤


GET movie_index/movie/_search


{


   "query":{


     "match": {"name":"red"}


   },


   "post_filter":{


     "term": {


       "actorList.id": 3


     }


   }


}


先查询后过滤效率慢,好比,我先从全国所有人中先过滤其他省份的留下广东的,再查询比先查询全国所有人再过滤广东的


过滤--查询前过滤(推荐)


GET movie_index/movie/_search


{


   "query":{


       "bool":{


         "filter":[ {"term": {  "actorList.id": "1"  }},


                    {"term": {  "actorList.id": "3"  }}


          ],


          "must":{"match":{"name":"red"}}


        }


   }


}


过滤--按范围过滤


GET movie_index/movie/_search


{


  "query": {


    "bool": {


      "filter": {


        "range": {


           "doubanScore": {"gte": 8}


        }


      }


    }


  }


}


关于范围操作符:

gt

大于

lt

小于

gte

大于等于

lte

小于等于


排序


GET movie_index/movie/_search


{


 "query":{


   "match": {"name":"red sea"}


 }


 , "sort": [


   {


     "doubanScore": {


       "order": "desc"


     }


   }


 ]


}


这个先按名称后按red sea排序


分页查询


GET movie_index/movie/_search


{


 "query": { "match_all": {} },


 "from": 1,


 "size": 1


}


指定查询的字段


GET movie_index/movie/_search


{


 "query": { "match_all": {} },


 "_source": ["name", "doubanScore"]


}


太多了,但我坚持,希望尽最大努力把我知道的全部写出来。


高亮


GET movie_index/movie/_search


{


   "query":{


     "match": {"name":"red sea"}


   },


   "highlight": {


     "fields": {"name":{} }


   }  


}


聚合


取出每个演员共参演了多少部电影


GET movie_index/movie/_search


{


 "aggs": {


   "groupby_actor": {


     "terms": {


       "field": "actorList.name.keyword"  


     }


   }


 }


}


每个演员参演电影的平均分是多少,并按评分排序


GET movie_index/movie/_search


{


 "aggs": {


   "groupby_actor_id": {


     "terms": {


       "field": "actorList.name.keyword" ,


       "order": {


         "avg_score": "desc"


         }


     },


     "aggs": {


       "avg_score":{


         "avg": {


           "field": "doubanScore"


         }


       }


      }


   }


 }


}


关于mapping的类型


之前说type可以理解为table,那每个字段的数据类型是如何定义的呢

查看看mapping

GET movie_index/_mapping/movie


{
  "movie_index": {
    "mappings": {
      "movie": {
        "properties": {
          "actorList": {
            "properties": {
              "id": {
                "type": "long"
              },
              "name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              }
            }
          },
          "doubanScore": {
            "type": "float"
          },
          "id": {
            "type": "long"
          },
          "name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  }
}

实际上每个type中的字段是什么数据类型,由mapping定义。


但是如果没有设定mapping系统会自动,根据一条数据的格式来推断出应该的数据格式。


true/false → boolean

1020  →  long

20.1 → double

“2017-02-01” → date

“hello world” → text +keyword

默认只有text会进行分词,keyword是不会分词的字符串。


mapping除了自动定义,还可以手动定义,但是只能对新加的、没有数据的字段进行定义。一旦有了数据就无法再做修改了。


注意:虽然每个Field的数据放在不同的type下,但是同一个名字的Field在一个index下只能有一种mapping定义。


中文分词

elasticsearch本身自带的中文分词,就是单纯把中文一个字一个字的分开,根本没有词汇的概念。但是实际应用中,用户都是以词汇为条件,进行查询匹配的,如果能够把文章以词汇为单位切分开,那么与用户的查询条件能够更贴切的匹配上,查询速度也更加快速。


分词器下载网址:https://github.com/medcl/elasticsearch-analysis-ik


安装


下载好的zip包,请解压后放到 /usr/share/elasticsearch/plugins/


cp /opt/elasticsearch-analysis-ik-5.6.4.zip  elastic


unzip elastic


rm elastic


然后重启es


service elasticsearch stop


service elasticsearch start


测试使用


使用默认


GET movie_index/_analyze


{  


 "text": "我是中国人"


}


   请观察结果


使用分词器


GET movie_index/_analyze


{  "analyzer": "ik_smart",


 "text": "我是中国人"


}


请观察结果


  另外一个分词器


   ik_max_word


GET movie_index/_analyze


{  "analyzer": "ik_max_word",


 "text": "我是中国人"


}


请观察结果


能够看出不同的分词器,分词有明显的区别,所以以后定义一个type不能再使用默认的mapping了,要手工建立mapping, 因为要选择分词器。


基于中文分词搭建索引


1、建立mapping


PUT movie_chn


{


 "mappings": {


   "movie":{


     "properties": {


       "id":{


         "type": "long"


       },


       "name":{


         "type": "text"


         , "analyzer": "ik_smart"


       },


       "doubanScore":{


         "type": "double"


       },


       "actorList":{


         "properties": {


           "id":{


             "type":"long"


           },


           "name":{


             "type":"keyword"


           }


         }


       }


     }


   }


 }


}



插入数据


PUT /movie_chn/movie/1


{ "id":1,


 "name":"红海行动",


 "doubanScore":8.5,


 "actorList":[  


 {"id":1,"name":"张译"},


 {"id":2,"name":"海清"},


 {"id":3,"name":"张涵予"}


]


}


PUT /movie_chn/movie/2


{


 "id":2,


 "name":"湄公河行动",


 "doubanScore":8.0,


 "actorList":[  


{"id":3,"name":"张涵予"}


]


}



PUT /movie_chn/movie/3


{


 "id":3,


 "name":"红海事件",


 "doubanScore":5.0,


 "actorList":[  


{"id":4,"name":"张晨"}


]


}


查询测试


GET /movie_chn/movie/_search


{


 "query": {


   "match": {


     "name": "红海战役"


   }


 }


}



GET /movie_chn/movie/_search


{


 "query": {


   "term": {


     "actorList.name": "张译"


   }


 }


}


自定义词库


修改/usr/share/elasticsearch/plugins/ik/config/中的IKAnalyzer.cfg.xml


<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">


<properties>


       <comment>IK Analyzer 扩展配置</comment>


       <!--用户可以在这里配置自己的扩展字典 -->


       <entry key="ext_dict"></entry>


        <!--用户可以在这里配置自己的扩展停止词字典-->


       <entry key="ext_stopwords"></entry>


       <!--用户可以在这里配置远程扩展字典 -->


        <entry key="remote_ext_dict">http://192.168.67.163/fenci/myword.txt</entry>


       <!--用户可以在这里配置远程扩展停止词字典-->


       <!-- <entry key="remote_ext_stopwords">words_location</entry> -->


</properties>


按照标红的路径利用nginx发布静态资源


在nginx.conf中配置


 server {


       listen  80;


       server_name  192.168.67.163;


       location /fenci/ {


          root es;


   }


  }



并且在/usr/local/nginx/下建/es/fenci/目录,目录下加myword.txt


myword.txt中编写关键词,每一行代表一个词。



然后重启es服务器,重启nginx。


在kibana中测试分词效果


更新完成后,es只会对新增的数据用新词分词。历史数据是不会重新分词的。如果想要历史数据重新分词。需要执行:


POST movies_index_chn/_update_by_query?conflicts=proceed


有点长了,分章节吧,第三章继续写。

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
4月前
|
JSON 自然语言处理 Java
ElasticSearch 实现分词全文检索 - 搜素关键字自动补全(Completion Suggest)
ElasticSearch 实现分词全文检索 - 搜素关键字自动补全(Completion Suggest)
123 1
|
4月前
|
自然语言处理 Java 关系型数据库
ElasticSearch 实现分词全文检索 - 聚合查询 cardinality
ElasticSearch 实现分词全文检索 - 聚合查询 cardinality
155 1
|
4月前
|
自然语言处理 Java 关系型数据库
ElasticSearch 实现分词全文检索 - SpringBoot 完整实现 Demo 附源码【完结篇】
ElasticSearch 实现分词全文检索 - SpringBoot 完整实现 Demo 附源码【完结篇】
69 0
|
4月前
|
存储 自然语言处理 Java
ElasticSearch 实现分词全文检索 - 经纬度定位商家距离查询
ElasticSearch 实现分词全文检索 - 经纬度定位商家距离查询
59 0
|
4月前
|
自然语言处理 Java
ElasticSearch 实现分词全文检索 - 高亮查询
ElasticSearch 实现分词全文检索 - 高亮查询
78 0
|
4月前
|
缓存 自然语言处理 Java
ElasticSearch 实现分词全文检索 - filter查询
ElasticSearch 实现分词全文检索 - filter查询
49 0
|
4月前
|
自然语言处理 Java
ElasticSearch 实现分词全文检索 - 复合查询
ElasticSearch 实现分词全文检索 - 复合查询
65 0
|
1月前
|
存储 安全 数据管理
如何在 Rocky Linux 8 上安装和配置 Elasticsearch
本文详细介绍了在 Rocky Linux 8 上安装和配置 Elasticsearch 的步骤,包括添加仓库、安装 Elasticsearch、配置文件修改、设置内存和文件描述符、启动和验证 Elasticsearch,以及常见问题的解决方法。通过这些步骤,你可以快速搭建起这个强大的分布式搜索和分析引擎。
46 5
|
2月前
|
存储 JSON Java
elasticsearch学习一:了解 ES,版本之间的对应。安装elasticsearch,kibana,head插件、elasticsearch-ik分词器。
这篇文章是关于Elasticsearch的学习指南,包括了解Elasticsearch、版本对应、安装运行Elasticsearch和Kibana、安装head插件和elasticsearch-ik分词器的步骤。
219 0
elasticsearch学习一:了解 ES,版本之间的对应。安装elasticsearch,kibana,head插件、elasticsearch-ik分词器。
|
3月前
|
NoSQL 关系型数据库 Redis
mall在linux环境下的部署(基于Docker容器),Docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongo
mall在linux环境下的部署(基于Docker容器),docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongodb、minio详细教程,拉取镜像、运行容器
mall在linux环境下的部署(基于Docker容器),Docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongo