本文仅针对搜索与Elasticsearch小白,先介绍了全文搜索的原理,然后介绍了Elasticsearch中的一些基本概念,接着讲解如何在Elasticsearch中插入文档构建查询索引,最后介绍Elasticsearch的线上查询API的使用方式。
如何实现全文搜索?最简单的方法就是用正则去匹配文档中的字符串。这种方式看似粗暴,但却不乏使用场景,比如Linux中的grep命令,Windows中用Ctrl+F在文件中进行查找等。
这种方式的缺点就是效率低,需要扫描全部文件,有时候搜索一个磁盘可能检索大半个小时。但Mac的spotlight搜索却是非常快,这是如何实现的了?
大家都知道,在数据库中直接全表查询的时间复杂度是o(n),如果对索引列进行查询,其时间复杂度为o(logn),如果数据以key-value形式存储,查询时间复杂度将降为o(1)。那么在全文搜索中我们直接建立从查询词到文档的映射是不是也就获得了o(1)的查询性能?这种词汇到文档的映射被称之为倒排索引。那么倒排索引是如何构建的呢?一般流程如下:
加入你有3篇文档
经过切词后得到每篇文档的词袋表示,即每篇文档所包含的词
然后再构建从词汇到文档ID的映射
当然实际在切词后还会做一些去停用词、词目还原等操作,以提升索引质量。
下面将介绍如何用Elasticsearch完成上面倒排索引的构建过程,然后在倒排索引上进行各种查询。
索引:含相同属性的文档集合。相当于关系型数据库中的一个database
类型:索引可以定义一个或者多个类型,文档必须属于一个类型,其相当于关系型数据库中的表,是通过mapping定义的。mapping中主要包括字段名、字段数据类型和字段索引类型这3个方面的定义,相当于关系型数据库中的schema。
文档:可以被索引的基本数据单位,也是全文搜索中被搜索的对象,可以对应一个网页,一篇txt文档或者一个商品。 相当于关系型数据库中的表中的一行记录。
举个例子,假设现在有大量的数据,有网页,有图片,有视频,有学术论文等,现在你打算基于这些数据用ES打造一个搜索引擎。由于这些数据高度异构,你可能对网页、图片、视频、论文等分别构建不同的”索引“。以Bing搜索为例,其上方的不同选项卡,对应着不同的索引。
在学术论文索引中,为了支持作者查询,机构查询,发表时间查询等多种查询模式,你需要定义多种”类型“,在定义”类型“时,你需要通过mapping文件定义各个字段的类型,处理方式(如切词、过滤等),然后所有文档将基于这个mapping文件来构建倒排索引。可以说一个”类型”对应一个倒排索引表。
具体的一篇论文即为一篇”文档“。
有时候一个索引的数据量非常大,甚至超出了单机的存储能力,这个时候需要对索引分片存储,分别存到不同机器上。
为了防止节点故障到时索引分片丢失,一般会对分片进行备份。备份除了可以保障数据安全性,还可以分担搜索的压力。
ES创建索引默认5个分片,1个备份,分片只能在创建索引的时候指定而备份可以后期动态修改。
去官网下载Elasticsearch安装包,并解压到指定目录。
然后进入安装目录,启动命令:
./bin/elasticseatch
然后运行
curl localhost:9200
如果返回集群信息,说明安装成功。
ES提供了丰富的REST API
直接运行
curl -XPUT "localhost:9200/index_test"
会创建名为index_test的索引,ES会根据插入的数据自动创建type与mapping,可以通过配置文件关闭ES的自动创建mapping功能。
也可以手动指定mapping,请求方式如下
curl -XPUT "localhost:9200/index_test" -d ' # 注意这里的'号 { "settings": { "index": { "number_of_replicas": "1", # 设置复制数 "number_of_shards": "5" # 设置主分片数 } }, "mappings": { # 创建mapping "test_type": { # 在index中创建一个新的type(相当于table) "properties": { "name": { # 创建一个字段(string类型数据,使用普通索引) "type": "string", "index": "not_analyzed" }, "age": { "type": "integer" } } } } }
也可以通过json文件的方式指定mapping :
{ "test_type": { # 注意,这里的test_type与json文件名必须一致 "properties": { "name": { "type": "string", "index": "not_analyzed" }, "age": { "type": "integer" } } } }
可以通过请求
curl -XDELETE 'localhost:9200/index_test/_mapping/test_type'
删除mapping或者通过
curl -XGET 'localhost:9200/index_test/_mapping/test_type'
查看mapping。
可以通过请求
curl -XDELETE "localhost:9200/index_test"
删除该索引。
可通过请求
curl -XPUT 'localhost:9200/index_test/test_type/1?pretty' -d ' # 这里的pretty参数的作用是使得返回的json显示地更加好看。1是文档的id值(唯一键)。 { "name": "zhangsan", "age" : "12" }'
添加或更新文档。也可以通过id来删除文档
curl -XDELETE 'localhost:9200/index_test/test_type/1?pretty'
查询单个文档
curl -XGET 'localhost:9200/index_test/test_type/1?pretty'。
通过url地址来指定查询范围
如在字段name中查找“一页书”;
{ "query":{ "match":{ "name":"一页书" } } }
如查询age在18-60之间的文档
{ "query":{ "range":{ "age{ "gte":18, "lte":60 } } } }
也支持filter操作,速度比query快,需要结合布尔类型使用。
如查找name中包含“一页书“且age为18的文档。
{ "query":{ “bool”: { "must":[ { "match":{ "name":"一页书" } }, { "filter":{ "term"{ "age":18 } } } ] } } }
支持must(与)、should(或)、must not等逻辑运算。
{ "query": { "match_phrase": { "content" : { "query" : "我的宝马多少马力", "slop" : 1 } } } }
当然搜索是一项庞大的工程,要达到商业可用,还需要做很多事情,比如如何导入批量数据与流数据,Query纠错与改写,搜索排序,下拉推荐,个性化等,后面将一一进行介绍。