全文搜索是最常见的需求,开源的ElasticSearch(以下简称Elastic)是目前全文搜索引擎的首选。它可以快速存储、搜索和分析海量数据。维基百科、StackOverflow、Github都在使用它。Elastic的底层是开源库Lucene。但是,你不能直接使用Lucene,你必须自己编写代码来调用它的接口。Elastic是Lucene的一个封装,提供了RESTAPI的操作接口,开箱即用。本文从头开始,解释如何使用Elastic构建您自己的全文搜索引擎。每一步都有详细的说明,大家跟着一起学吧。1、安装Elastic需要Java8环境。如果你的机器没有安装Java,可以参考这篇文章,注意确保环境变量JAVA_HOME设置正确。安装Java后,可以按照官方文档安装Elastic。直接下载压缩包比较简单。$wgethttps://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.5.1.zip$unzipelelasticsearch-5.5.1.zip$cdelasticsearch-5.5.1/接下来进入解压后的目录运行以下命令,开始松紧带。$./bin/elasticsearch如果此时报错“maxvirtualmemoryareasvm.maxmapcount[65530]istoolow”,运行如下命令。$sudosysctl-wvm.max_map_count=262144如果一切正常,Elastic将运行在默认端口9200上。此时,打开另一个命令行窗口,请求该端口,您将得到一个描述。$curllocalhost:9200{"name":"atntrTf","cluster_name":"elasticsearch","cluster_uuid":"tf9250XhQ6ee4h7YI11anA","version":{"number":"5.5.1","build_hash":"19c13d0","build_date":"2017-07-18T20:44:24.823Z","build_snapshot":false,"lucene_version":"6.6.0"},"tagline":"YouKnow,forSearch"}在上面的代码中,请求9200端口,Elastic返回一个包含当前节点、集群、版本等信息的JSON对象。按Ctrl+C,Elastic将停止运行。默认情况下,Elastic只允许本地访问。如果需要远程访问,可以修改Elastic安装目录下的config/elasticsearch.yml文件,去掉network.host的注释,修改其值为0.0.0.0,然后重启Elastic。network.host:0.0.0.0在上面的代码中,将其设置为0.0.0.0以便任何人都可以访问它。在线服务不要这样设置,要设置到特定的IP。2.基本概念2.1Node和ClusterElastic本质上是一个分布式数据库,可以让多台服务器协同工作,每台服务器可以运行多个Elastic实例。单个Elastic实例称为节点。一组节点形成一个集群。2.2IndexElastic会对所有字段进行索引,处理后写入一个倒排索引(InvertedIndex)。查找数据时,直接查找索引。因此,Elastic数据管理的顶层单元称为Index(索引)。它是单个数据库的同义词。每个索引(即数据库)的名称必须是小写的。下面的命令可以查看当前节点的所有Index。$curl-XGET'http://localhost:9200/_cat/indices?v'2.3DocumentIndex中的单个记录称为Document。许多文档形成一个索引。文档以JSON格式表示,示例如下。{"user":"张三","title":"engineer","desc":"databasemanagement"}同一个Index中的文档不需要相同的结构(scheme),但最好保持相同,这有助于提高搜索效率。2.4TypeDocument可以分组。比如在weatherIndex中,可以按城市(北京和上海)分组,也可以按气候(晴天和雨天)分组。这个分组叫做Type,是一个虚拟的逻辑分组,用来过滤Document。不同的类型应该有相似的结构(模式)。例如,id字段不能是本组中的字符串和另一个组中的值。这与关系数据库中的表不同。具有完全不同属性的数据(例如产品和日志)应该存储为两个Index,而不是将两个Type存储在一个Index中(尽管可以这样做)。以下命令可以列出每个Index中包含的Type。$curl'localhost:9200/_mapping?pretty=true'按照规划,Elastic6.x版本只允许每个Index包含一个Type,7.x版本会彻底去掉Type。3.创建和删除Index要创建新的Index,可以直接向Elastic服务器发送PUT请求。下面的例子是创建一个名为weather的新Index。$curl-XPUT'localhost:9200/weather'服务器返回一个JSON对象,其中的acknowledge字段表示操作成功。{"acknowledged":true,"shards_acknowledged":true}然后,我们发送一个DELETE请求来删除这个Index。$curl-XDELETE'localhost:9200/weather'4.中文分词设置首先安装中文分词插件。这里使用的是ik,其他插件(比如smartcn)也可以考虑。$./bin/elasticsearch-plugininstallhttps://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.5.1/elasticsearch-analysis-ik-5.5.1.zip以上代码安装5.5.1版本用于Elastic5.5.1的插件。然后重启Elastic,新安装的插件就会自动加载。然后,新建一个Index,指定需要分词的字段。这一步根据数据结构的不同而不同,以下命令仅针对本文。基本上所有需要查找的中文字段都要单独设置。$curl-XPUT'localhost:9200/accounts'-d'{"mappings":{"person":{"properties":{"user":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_max_word"},"title":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_max_word"},"desc":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_max_word"}}}}}'在上面的代码中,首先创建一个名为accounts的Index,其中包含一个名为person的Type。人有三个领域。usertitledesc三个字段都是中文,类型都是text(文本),所以需要指定中文分词器,而不是默认的英文分词器。Elastic的分词器称为分析器。我们为每个字段指定分词器。"user":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_max_word"}上面代码中analyzer是字段text的tokenizer,search_analyzer是字段text的tokenizer搜索词。ik_max_word分词器是由插件ik提供的,它可以用最大分词数对文本进行分词。五、数据操作5.1添加新记录向指定的/Index/Type发送PUT请求,在Index中添加一条新记录。例如,向/accounts/person发送请求以添加新的人员记录。$curl-XPUT'localhost:9200/accounts/person/1'-d'{"user":"张三","title":"engineer","desc":"databasemanagement"}'返回的jsonserver对象,会给出Index、Type、Id、Version等信息。{"_index":"accounts","_type":"person","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"created":true}仔细看会发现请求路径是/accounts/person/1,最后的1是记录的Id。它不一定是数字,任何字符串(如abc)都可以。添加新记录时,也可以不指定Id,改成POST请求。$curl-XPOST'localhost:9200/accounts/person'-d'{"user":"李四","title":"Engineer","desc":"SystemManagement"}'上面代码中,到/accounts/person发出POST请求以添加记录。此时服务器返回的JSON对象中,_id字段为随机字符串。{"_index":"accounts","_type":"person","_id":"AV3qGfrC6jMbsbXb6k1p","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"created":true}注意,如果不先创建Index(本例为accounts),直接执行上面的命令,Elastic不会报错,但是会直接生成指定的Index。所以,输入的时候一定要小心,不要把Index的名字弄错了。5.2查看记录向/Index/Type/Id发出GET请求,查看这条记录。$curl'localhost:9200/accounts/person/1?pretty=true'以上代码请求查看/accounts/person/1的记录,url的参数pretty=true表示返回的是easy-阅读格式。返回数据中found字段表示查询成功,_source字段返回原始记录。{"_index":"accounts","_type":"person","_id":"1","_version":1,"found":true,"_source":{"user":"张三","title":"Engineer","desc":"DatabaseManagement"}}如果Id不正确,则找不到数据,found字段为false。$curl'localhost:9200/weather/beijing/abc?pretty=true'{"_index":"weather","_type":"beijing","_id":"abc","found":false}5.3删除记录删除记录就是发出DELETE请求。$curl-XDELETE'localhost:9200/accounts/person/1'这里不要删除这条记录,后面会用到。5.4更新记录更新记录就是使用PUT请求重新发送数据。$curl-XPUT'localhost:9200/accounts/person/1'-d'{"user":"张三","title":"engineer","desc":"数据库管理,软件开发"}'{"_index":"accounts","_type":"person","_id":"1","_version":2,"result":"updated","_shards":{"total":2,"successful":1,"failed":0},"created":false}在上面的代码中,我们将原来的数据从“数据库管理”改为“数据库管理,软件开发”。返回结果中,有几个字段发生了变化。"_version":2,"result":"updated","created":false可以看出记录的Id没有变,但是版本(version)从1变成了2,操作类型(result)已从created更改为updated,created字段变为false因为这不是新记录。6、数据查询6.1返回所有记录使用GET方式直接请求/Index/Type/_search,将返回所有记录。$curl'localhost:9200/accounts/person/_search'{"took":2,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},“hits”:{“total”:2,“max_score”:1.0,“hits”:[{“_index”:“accounts”,“_type”:“person”,“_id”:“AV3qGfrC6jMbsbXb6k1p”,“_score”:1.0,"_source":{"user":"李四","title":"工程师","desc":"系统管理"}},{"_index":"accounts","_type":"person","_id":"1","_score":1.0,"_source":{"user":"张三","title":"engineer","desc":"数据库管理,软件开发"}}]}}上面代码中,返回结果的taken字段表示操作耗时(毫秒),timed_out字段表示是否超时,hits字段表示命中记录,子字段的含义如下。total:返回记录数,在本例中为2。max_score:最高匹配度,在本例中为1.0。hits:返回记录的数组。在返回的记录中,每条记录都有一个_score字段,表示匹配的节目,默认是按照这个字段降序排列。6.2全文搜索Elastic的查询非常特殊,使用自己的查询语法,需要带数据体的GET请求。$curl'localhost:9200/accounts/person/_search'-d'{"query":{"match":{"desc":"software"}}}'以上代码使用Match查询,并指定匹配条件是desc字段包含“软件”一词??。返回结果如下。{"took":3,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":0.28582606,"hits":[{"_index":"accounts","_type":"person","_id":"1","_score":0.28582606,"_source":{"user":"ZhangSan","title":"Engineer","desc":"数据库管理,软件开发"}}]}}Elastic默认返回10条结果,你可以通过size字段改变这个设置。$curl'localhost:9200/accounts/person/_search'-d'{"query":{"match":{"desc":"management"}},"size":1}'上面的代码指定了每一个time只返回一个结果。您还可以通过from字段指定位移。$curl'localhost:9200/accounts/person/_search'-d'{"query":{"match":{"desc":"management"}},"from":1,"size":1}'上面代码指定从位置1开始(默认是从位置0开始),只返回一个结果。6.3逻辑操作如果有多个搜索关键字,Elastic认为它们是一个or关系。$curl'localhost:9200/accounts/person/_search'-d'{"query":{"match":{"desc":"softwaresystem"}}}'上面的代码搜索软件或系统。如果要对多个关键字执行查询,则必须使用布尔查询。$curl'localhost:9200/accounts/person/_search'-d'{"query":{"bool":{"must":[{"match":{"desc":"software"}},{"匹配”:{“desc”:“系统”}}]}}}'7。参考链接ElasticSearch官方手册APracticalIntroductiontoElasticsearch
