Solr教程-安装运行

Solr是一个分布式高性能全文搜索引擎,是基于Lucene开发的非常流行的企业搜索引擎平台。Solr提供了非常丰富的搜索功能,可以用来构建非常复杂的搜索业务。 这个系列教程旨在介绍Solr的常用功能。 准备 在开始之前你需要准备一些必要的硬件及软件设施: JDK 1.8 (或者更高版本) Solr 7.5.x Postman (可选) CentOS 7.x (本系列以此OS为准) VirtualBox (可选) CentOS可以选择安装,Solr是支持多操作系统的,但是在Linux下体验更好,大部分生产环境也都是Linux系统,建议使用CentOS来学习以及使用Solr。 VirtualBox可以选择安装,如果你是Windows操作系统,建议使用VirtualBox安装CentOS来学习使用Solr。 后面的操作都是基于CentOS 7.x版本操作系统来运行。 安装Solr 安装Solr需要Java 1.8或者更高版本,可以使用java -version命令检查你系统上安装的Java版本。由于Java语言有好几个实现,有些实现并不能非常好的支持Solr,根据Lucene JavaBugs来检查你的Java是否对Solr有很好的支持。Solr支持的操作系统包括Linux, MacOS, Windows。 到https://lucene.apache.org/solr/mirrors-solr-latest-redir.html下载对应操作系统的7.5.0版本的Solr安装包。 solr-7.5.0.tgz 是针对Linux/Unix/MacOS系统的安装包 solr-7.5.0.zip 是针对Windows系统的安装包 solr-7.5.0-src.tgz 是源码包 tar zxf solr-7.5.0.tgz cd solr-7.5.0 bin/solr start 然后使用浏览器打开http://localhost:8983,你就可以看到Solr Admin UI的界面了,这是Solr自带的管理界面,使用非常方便,在里面可以查看状态,管理core,进行查询。 以bin/solr start运行的solr是以standalone模式(非分布式)运行的,如果是学习可以使用此模式快速的尝试。但是建议使用后面将要介绍的SolrCloud模式来运行,SolrCloud模式可以很好的进行扩展,应对海量数据的实时搜索,动态剔除/增加机器节点,动态转移数据分片(shard),所有这些操作都可以不停机进行操作。生产环境也建议使用SolrCloud模式运行。 这是以SolrCloud模式启动solr的命令,只需要添加一个-c参数即可 bin/solr start -c 详细的命令行参数请参考Solr控制脚本参考 SolrCloud启动之后会占用8983端口,默认会启动一个内置的zookeeper实例,占用9983端口(在solr的端口上加1000)。 多节点的SolrCloud安装-生产环境部署 一般生产环境部署会把zookeeper单独部署,其他的solr节点单独部署。 外部Zookeeper 下面假设是生产环境部署zookeeper,建议至少需要三个zookeeper节点。 开发环境以及测试使用一个zookeeper也没有关系。 假设已经有三个机器来运行zookeeper来组建一个小的zookeeper集群: 192.168.1.2 192.168.1.3 192.168.1.4 步骤为:下载zookeeper-3.4.13 -> 解压 -> 配置conf/zoo.cfg -> 启动 curl -o zookeeper-3.4.13.tar.gz 'https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.13/zookeeper-3.4.13.tar.gz' tar -xzvf zookeeper-3.4.13.tar.gz cd zookeeper-3.4.13 cp conf/zoo_sample.cfg conf/zoo.cfg sed -i 's/dataDir=.*/dataDir=.\/data/g' conf/zoo.cfg mkdir data echo -e "\nserver.1=192.168.1.2:2888:3888\nserver.2=192.168.1.3:2888:3888\nserver.3=192.168.1.4:2888:3888" >> conf/zoo.cfg echo 1 > data/myid 解释一下上面的一些命令: ...

March 5, 2019

Solr SearchComponent开发实战

先介绍一下Solr的SearchHandler,SearchHandler是用来处理查询请求的配置。一个SearchHandler里面包含了若干个SearchComponent,实际的逻辑处理就在SearchComponent里面进行处理,还可以自行开发符合自己业务逻辑的SearchComponent,然后经过配置可以灵活的组合各种SearchComponent。各个SearchComponent就像流水线一样处理请求,各自负责自己的逻辑处理。 基于Solr 7.5.0测试 配置 最常使用的/select查询,使用的就是下面默认的配置: <requestHandler name="/select" class="solr.SearchHandler"> <lst name="defaults"> <str name="echoParams">explicit</str> <int name="rows">10</int> </lst> </requestHandler> 里面配置的defaults可以参考官方文档 除了最常使用的默认搜索配置之外,这个/select配置还支持First-components和Last-componets的配置,也就是说在defaults的前面或者后面可以添加一些SearchComponent。具体的流程可以参考下面的图: defaults +-----------+ | query | +------------------+ | facet | +-----------------+ | First-Components | --> | mlt | --> | Last-Components | +------------------+ | highlight | +-----------------+ | ...... | +-----------+ 如果添加了First-Components和Last-Components之后,完整的配置看起来就是下面的样子: <requestHandler name="/select" class="solr.SearchHandler"> <arr name="first-components"> <str>mycomponent1</str> <str>mycomponent2</str> </arr> <lst name="defaults"> <str name="echoParams">explicit</str> <int name="rows">10</int> </lst> <arr name="last-components"> <str>spellcheck</str> <str>mycomponent3</str> </arr> </requestHandler> 还可以完全自定义整个查询的handler的流程,不使用默认的的SearchComponent: <requestHandler name="/select_2" class="solr.SearchHandler"> <arr name="components"> <str>mycomponent1</str> <str>query</str> <str>mycomponent2</str> <str>debug</str> <str>mycomponent3</str> </arr> </requestHandler> 可以结合自己实现的SearchComponents以及官方提供的一起来实现一个符合自己业务逻辑的查询请求。 代码实现 先上代码,看一下一个TestComponent的实现最基本的方法需要实现哪些: ...

March 1, 2019

Solr集群索引以及查询对负载的影响

今天对Solr 7.6.0的集群的索引以及查询对负载的影响做一点测试,记录一下测试结果。 collection的shards和leader分布情况如下 ip和name对应如下表: ip name 172.31.1.253 solr1 172.31.7.73 solr2 172.31.2.236 solr3 172.31.8.221 solr4 172.31.9.13 solr5 索引 测试的点是往没有leader的机器发文档进行索引,统计到的cpu utilization如下图。 从图中可以看出,发往没有leader的机器进行索引,所有机器的cpu都差不多。从而推论不管往哪个机器发文档进行索引,各个机器的cpu utilization都是均匀的。 索引数据如下: title_t字段为随机生成80-100个字符的文本,文本为英文数据。 id为数字。只有这两个字段,每一个批次为100条数据,每个批次间隔5秒。 一次索引完成大概在1.2-1.3秒左右。 下面是索引中文时机器的cpu指标,中文使用单字分词,有title, content。content字段字符数量大概在1000-2000之间。索引时间在5-60秒不等。每一批为100条数据。 从图中可以看到solr5(接收请求)机器负载稍微高一点点。 查询 使用的查询数据为随机生成的200000个关键词,间隔200毫秒。collection里面的数据量在10000左右。 上图分成三个区间来看:15:30之前,15:30-16:05,16:05之后,每一个区间使用不同的查询方法。 另外,solr5那个机器是没有leader在上面的。 15:30之前 这个时间区间使用的是所有请求串行发送,也就是一个接一个的发送,CPU的负载整体都不是很高,所有请求都发送到solr5那个机器上面,很明显的看到solr5机器的cpu负载要高于其他机器,其他机器的负载差不多。 15:30-16:05 这个时间区间使用跟上面一样的方法,只是并发数为10,请求也是只发送到solr5机器上面。可以看到solr5的负载明显高于其他机器。其他4个机器负载差不多。 16:05之后 这个时间区间使用同样的查询数据,10个并发,请求发送到solr1上面。从图上可以看到solr1的负载要高于其他4个机器,其他4个机器负载差不多。这一个测试的结果说明,不管一个节点上有没有leader,这个机器都会去比承担一部分的查询请求。 结论 索引的时候各机器的负载差不多。查询的时候,请求的机器负载高于其他机器,其他机器负载相差不大。所以要集群查询的时候可以使用轮询的方式,每一次查询请求可以发送到不同的机器,这样就可以保证所有机器的负载相当了。

January 10, 2019

Solr开发分词插件

如果已经有了一个自定义的分词库,那要怎么才能集成到Solr里面去呢,本文带你一步一步入坑–不对–,带你集成自定义分词库。 如果还没有自定义的分词库,希望写自己的Solr分词插件,可以先参考自定义Lucene的分词器Analyzer 根据自定义Lucene的分词器Analyzer一文,假设现在已经具备了DemoTokenizer类。因为在Solr里面可以只需要Tokenizer就可以了,而不一定需要Analyzer。 先看一下Solr里schema配置分词的定义: <fieldType name="text_demo" class="solr.TextField" positionIncrementGap="100"> <analyzer> <tokenizer class="com.example.lucene.DemoTokenizerFactory" wordType="hello" /> <!-- some other filters --> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType> 这里配置了一个字段类型为text_demo,使用了com.example.lucene.DemoTokenizerFactory这个工厂方法来处理对应字段的分词,同时还设置了一个属性wordType值为hello,在后面的代码中读取这个属性的值。这里配置了DemoTokenizerFactory分词处理类,那么就必须要有这么一个类了: package com.example.lucene; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.util.TokenizerFactory; import org.apache.lucene.util.AttributeFactory; import java.util.Map; // 需要继承org.apache.lucene.analysis.util.TokenizerFactory类并实现create方法 public class DemoTokenizerFactory extends TokenizerFactory { // 可以保存在上面schema定义的额外的属性参数 private String wordType = ""; // 需要一个构造方法,通过构造方法的args参数来读取schema设置的自定义的参数 public DemoTokenizerFactory(Map<String, String> args) { super(args); // 调用完父类的构造函数后,可以读取schema里tokenizer节点定义的wordType参数 // 保存在类属性里面,然后在create方法来使用这个传入的参数 wordType = args.getOrDefault("wordType", ""); } @Override public Tokenizer create(AttributeFactory factory) { // 在这里直接new一个tokenizer对象即可 return new DemoTokenizer(); } } 通过上面定义的DemoTokenizerFactory工厂类,就可以提供Solr需要的自定义分词功能了。如果你的自定义分词器需要额外的参数,比如调用一个rest api来进行分词,需要一个host和port,就可以通过在schema定义属性的办法来使分词可以更有弹性和适应不同的rest api而不需要改Solr自定义分词代码。 ...

May 19, 2018

Solr实现句子段落搜索

如果想要在Solr实现在某个句子/段落搜索的话,需要一点点技巧来达到这个需求。首先是在索引的时候做一点点手脚,然后再查询的时候再用点魔法就可以实现了。 索引处理 假设有一个文档,内容字段包含如下文本: sentence abc. sentence axx. another paragraph, sentence xyz. 这里有四个句子,以半角的.分割;两个段落,空行分割。 在索引的时候,先把句子拆开,使用content.sentence字段来索引每个句子,content.sentence为多值类型的字段。同样的处理方法把段落分开,使用content.paragraph字段来索引每个段落。配置如下 <field name="content.sentence" type="text" indexed="true" stored="true" multiValued="true"/> <field name="content.paragraph" type="text" indexed="true" stored="true" multiValued="true"/> 索引里面的一点小技巧就是在配置分词的时候,设置positionIncrementGap参数,假设下面是配置text这个fieldtype: <fieldType name="text" class="solr.TextField" positionIncrementGap="501"> 这里设置positionIncrementGap为501,为啥是501后面详细说明。索引数据的时候记得拆分句子和段落到对应的字段,对应字段是数组类型,如句子: {"content.sentence": ["sentence abc.", "sentence axx.", "another paragraph.", "sentence xyz."]} 到这里索引就配置完了。 查询魔法 说是魔法,其实也就是一点小技巧,使用Solr的phrase query,然后设置slop即可。比如在句子中查询: q=content.sentence:"sentence abc"~500 这个意思就是在content.sentence字段查询短语sentence abc,slop设置为500。 slop的意思是经过多少个步骤可以转换成sentence abc。这个跟编辑距离的概念是一样的,具体Solr/Lucene内部使用得是什么距离需要去阅读源码(或者看文档)。 多值字段每一个内容在保存位置信息的时候会根据positionIncrementGap的值设置偏移,如果第一个值最后的position是2,那么第二个值的开始就是2+positionIncrementGap。 这里的查询用得是500,上面索引配置的是501,差一个位置就可以确保前一个值和后一个值中间隔开500个字符,由于这个值是固定的,所以要确保一个段落要在500个词以内才可以。句子也是一样的道理。

April 17, 2018

Solr控制脚本参考

Solr包含了一个命令行工具bin/solr可以用来执行非常多的常用操作。可以启动、停止Solr,创建删除collection/core,对ZooKeeper的操作,以及检查solr状态和配置分片。 bin/solr的使用格式如下: bin/solr cmd [options] 启动 Start 启动与重启 start命令用来启动solr。restart可以用来重启solr,不管是从停止状态还是在运行状态。 start和restart有很多的选项,可以指定运行在SolrCloud模式,可以使用自带的样例配置集,同时还可以指定hostname, port, ZooKeeper。 bin/solr start [options] bin/solr start -help bin/solr restart [options] bin/solr restart -help 当使用restart命令时,命令参数必须与start的命令参数一致。重启的过程是先执行stop命令停止solr,然后再启动solr,所以restart的参数必须要跟start的参数一致。 启动参数 bin/solr命令行脚本提供了非常多的选项来定制solr服务,例如改变监听的端口。其实默认的参数已经能够满足很多情况的需求了。 -a "<string>" 指定JVM参数,例如-X。如果是以-D传递的JVM参数,-a选项可以忽略。 Example: bin/solr start -a "-Xdebug -Xrunjdwp:transport=dt_socket, server=y,suspend=n,address=1044" -cloud 在SolrCloud模式启动,同时启动内置的ZooKeeper。这个选项可以简写为:-c。如果已经有一个ZooKeeper实例在运行,可以使用-z选项参数替换内置的ZooKeeper。查看更多SolrCloud细节。 Example:bin/solr start -c -d <dir> 定义server目录,默认为server。server目录的作用是提供web服务,rest api。这个选项一般不需要覆盖。当在同一台机器启动多个solr实例时,更常用的办法是使用-s选项来指定不同的solr.home,以存放不同的配置和数据。 Example:bin/solr start -d newServerDir -e <name> 以样例配置启动solr。solr提供的这些样例配置以帮助你快速开始熟悉solr的使用,或者某些特性。 这些样例包括: cloud techproducts dih schemaless 点击Running with Example Configurations查看详情。 Example:bin/solr start -e schemaless -f 在前台运行。使用-e选项的时候此选项不能使用。 Example:bin/solr start -f -h <hostname> 指定hostname,如果此选项没有指定,默认使用localhost。 ...

March 19, 2018

Solr7中的learnin to rank使用

什么是learning to rank在此往篇文章不做介绍,这里只介绍如何使用。主要针对cloud模式启动的solr。使用的版本是solr-7.1.0,本文以外的请参考官方文档 安装 solr-ltr-7.1.0.jar已经自带在solr的发行包里面,只需要在solrconfig.xml配置即可,把下面的内容添加到solrconfig.xml <lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-ltr-\d.*\.jar" /> <queryParser name="ltr" class="org.apache.solr.ltr.search.LTRQParserPlugin"/> <cache name="QUERY_DOC_FV" class="solr.search.LRUCache" size="4096" initialSize="2048" autowarmCount="4096" regenerator="solr.search.NoOpRegenerator" /> <transformer name="features" class="org.apache.solr.ltr.response.transform.LTRFeatureLoggerTransformerFactory"> <str name="fvCacheName">QUERY_DOC_FV</str> </transformer> 添加了以上的配置后还需要把dist目录下的solr-ltr-7.1.0.jar拷贝到server/solr-webapp/webapp/WEB-INF/lib/目录下面,如果不添加的话,用rest api设置model的时候会报class not found exception。 上传feature [ { "store": "featureStore", "name": "title", "class": "org.apache.solr.ltr.feature.SolrFeature", "params": { "q": "{!func}query({!v=title:${query}})" } }, { "store": "featureStore", "name": "content", "class": "org.apache.solr.ltr.feature.SolrFeature", "params": { "q": "{!func}query({!v=content:${query}})" } }, { "store": "featureStore", "name": "originalScore", "class": "org.apache.solr.ltr.feature.OriginalScoreFeature", "params": {} }, { "store": "featureStore", "name": "level", "class": "org.apache.solr.ltr.feature.FieldValueFeature", "params": { "field": "level" } }, { "store": "featureStore", "name": "year", "class": "org.apache.solr.ltr.feature.FieldValueFeature", "params": { "field": "year" } } ] 上面是四个feature: ...

March 19, 2018

solr7配置同义词

Solr的同义词可以满足绝大部分的需求,可以配置双向,也可以配置单向的同义词列表,功能非常的强大。 Solr的同义词可以在两个方面设置,一是索引的时候;二是查询的时候;或者你同时设置两者,同时设置的时候会非常复杂,会产生意想不到的结果。 先从基本配置开始 假设我们有同义词列表synonyms_en.txt ability,capability 配置Solr的fieldtype <fieldType name="text_en" class="solr.TextField" positionIncrementGap="100"> <analyzer type="index"> <tokenizer class="solr.StandardTokenizerFactory"/> <!-- synonym --> <filter class="solr.SynonymGraphFilterFactory" synonyms="synonyms_en.txt" ignoreCase="true" expand="false"/> ...... </analyzer> <analyzer type="query"> ...... </analyzer> </fieldType> 先尝试索引时候设置同义词,直接在analyzer type="index"内部配置即可,SynonymGraphFilterFactory暂时还是一个试验性质的功能,以后的版本可能会有所改动。class还可以设置成solr.SynonymFilterFactory Solr自带的同义词配置有很多的参数: synonyms:指定同义词列表文件 ignoreCase:顾名思义是忽略大小写 expand:是否扩展同义词,意思是a和b是同义词,如果设置为true,那么搜索a的时候,同时搜索a和b,如果设置为false,那么搜索a的时候就搜索a,搜索b的时候也搜索a。也就是说单向双向的概念:如果设置为true,那么就是双向的;如果设置为false就是单向的,后面的词会转换成前面的词搜索。 tokenizerFactory:指定同义词列表使用的分词器 dedup:是否忽略重复,true为忽略 根据上面的设置,如果搜索ability,那么最终搜索的也是ability。如果搜索的是capability,最终搜索的时候会把capability转换成ability搜索。这样就达到了同义词搜索的功能。 查询时使用同义词 只需要把index部分的配置移到query部分即可,如下: <fieldType name="text_en" class="solr.TextField" positionIncrementGap="100"> <analyzer type="index"> ...... </analyzer> <analyzer type="query"> <tokenizer class="solr.StandardTokenizerFactory"/> <!-- synonym --> <filter class="solr.SynonymGraphFilterFactory" synonyms="synonyms_en.txt" ignoreCase="true" expand="false"/> ...... </analyzer> </fieldType> 如果这时候把expand设置成true,那么搜索任何一个词,最终都会转换成搜索:SynonymQuery(Synonym(field:ability field:capability))。注意你看到的可能会是SynonymQuery(Synonym(title.en:abil title.en:capabl)),这是使用了porter stemming之后的结果。 我的请求语句如下: http://192.168.0.106:8983/solr/lnc/select?debug=query&df=title.en&q=capability 单向,双向概念 这两个概念弄得还是有点模糊。第一:如果设置expand=false,那么同义词都会转换成第一个词进行索引(或者查询);第二:如果设置expand=true,那么一个词会扩展成多个词同时查询;第三:使用ability => capability的配置时,如果查询ability,不管expand是true还是false,都会转换成对capability的搜索。 我理解的单向可以做成下面的列表: ...

January 10, 2018

Solr7从搭建到入坑

solr7搭建运行非常简单,首先下载压缩包,然后解压,到解压出来的目录执行:bin/solr start即可启动solr。以这种方式启动的solr是single core的模式。推荐部署cloud模式,可以有更好的伸缩性与扩展性,使用:bin/solr start -c可以启动cloud模式。下面主要针对cloud模式介绍solr7.2.1的搭建与查询。 solr7自带了几个例子,可以从例子可以深入solr。 以SolrCloud模式启动Solr 要启动solr,在Linux或者Unix执行:bin/solr start -e cloud,windows下执行:bin\solr.cmd start -e cloud,默认启动2个node组成的cloud server。这将会进入一个交互模式的启动过程,如果想跳过交互模式,启动命令添加参数-noprompt。如果是交互模式,一路回车到下面的提示: Now let's create a new collection for indexing documents in your 2-node cluster. Please provide a name for your new collection: [gettingstarted] 输入techproducts回车,我们创建的collection名字是techproducts。 继续回车到选择配置的地方: Please choose a configuration for the techproducts collection, available options are: _default or sample_techproducts_configs [_default] 这里有一个configSet的概念,configSet相当于是一个所有配置文件存放的地方,所有创建的collection都要在这里读取配置文件。 _default是一个基本的配置集,还有一个包含了techproducts的配置集,这个配置集可以用来支持我们想使用的techproducts样例数据。 输入sample_techproducts_configs回车。 恭喜,Solr已经启动好了!通过在浏览器访问http://localhost:8983/solr,这将是入坑的起点。 通过上面的步骤,创建了了两个节点(2 nodes),一个运行在7574端口,另外一个运行在8983端口,一个collection techproducts,两个shard(分片),每个分片又有两个拷贝,这样的配置可以在某个分片出现问题的时候快速恢复。 |-- 127.0.1.1:7574 |-- shard1 --|-- 127.0.0.1:8983 techproducts --| |-- shard2 --|-- 127.0.1.1:7574 |-- 127.0.0.1:8983 索引techproducts样例数据 上面已经启动了solr,但是里面还没有数据,现在还不能做查询。 ...

October 8, 2017