solr全文检索学习
序言:
前面我们说了全局检索Lucene,但是我们发现Lucene在使用上还是有些不方便的,例如想要看索引的内容时,就必须自己调api去查,再例如一些添加文档,需要写的代码还是比较多的
另外我们之前说过Lucene只是一个全文检索的工具包,并不算一个完整的搜索引擎。很多功能还是需要我们自己去完善,去实现的。
solr 和 ElasticSearch 是基于Lucene开发的功能比较完备的全文检索引擎。
Solr是一个高性能,采用Java开发,基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。
Solr实际上是一个Web应用,需要servlet容器,可以用Tomcat或者Jetty,新版本Solr默认是用Jetty的,如果要改Tomcat,则要自己做配置。
Solr提供了一些http接口,通过POST请求进行文档的操作,GET请求进行文档的查询。
要想是用solr,我们就需要先下载 https://lucene.apache.org/solr/
相关文档:https://lucene.apache.org/solr/guide/8_7/getting-started.html
solr目录说明:
下载后解压目录如下
整合Tomcat的你们自己百度,我直接用内置的Jetty
solr服务启动:
启动solr时,不要直接双击startup.cmd,不然窗口一闪而过,最后还是没有启动项目
在命令行里面执行,启动后不要关闭命令行,因为默认是后台进程,关闭命令行后服务器也会关闭
启动:solr start [-p 端口 -s home目录地址]
关闭:直接关闭命令行,或者,solr stop -all 关闭全部,或者,solr stop -p 8983 只关闭8983端口的服务器
重启:solr restart -p 端口
帮助:solr –help ,或者,solr start -help
启动完后直接浏览器访问 http://localhost:8983/
一般来说solr的管理后台界面是不需要认证的,但是我们不可能让所有人都可以访问,所以我们需要修改认证
Jetty服务器启动认证:
首先在解压包的 /server/etc 目录中随便建个properties文件,文件名随意,填入用户密码角色
然后在 /server/contexts 目录中的 solr-jetty-context.xml 复制那段代码,文件名要和刚刚你建的文件名一致
<Get name="securityHandler"> <Set name="loginService"> <New class="org.eclipse.jetty.security.HashLoginService"> <Set name="name">verify—name</Set> <Set name="config"> <SystemProperty name="jetty.home" default="." />/etc/verify.properties </Set> </New> </Set> </Get>
接着去 \server\solr-webapp\webapp\WEB-INF 目录中的 web.xml文件中注释掉选中的代码
紧接着加上一段代码,指定资源认证
<security-constraint> <web-resource-collection> <web-resource-name>Solr</web-resource-name> <url-pattern>/*</url-pattern> <http-method>GET</http-method> <http-method>DELETE</http-method> <http-method>POST</http-method> <http-method>PUT</http-method> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> <role-name>user</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>verify-name</realm-name> </login-config>
效果:
相关参考博客:
https://blog.csdn.net/u011561335/article/details/90695860
https://www.cnblogs.com/w1995w/p/10566218.html
整合Tomcat时开启认证:
https://www.cnblogs.com/anny0404/p/5570666.html
solr的管理界面功能:
Dashboard:
仪表盘,显示了该Solr实例开始启动运行的时间、版本、系统资源、jvm等信息
Logging:
日志信息,还可以修改记录的日志级别
Core :
core核心管理,类似与MySQL里面表的概念
注意,新建一个core核心,不能简单的在页面上操作就行,需要在本地磁盘添加一些配置文件,否则就会报错。
首先你要去你的solr的home目录建一个文件夹,取名随意,建议和新core的名字一致
紧接着去 \server\solr\configsets\_default 目录里面复制conf这个文件夹,里面有必须的配置文件
回到我们刚刚新建的目录,粘贴下去,重启solr
点击add core,然后刷新页面,就出现新的了
java properties
Solr在JVM 运行环境中的属性信息,包括类路径、文件编码、jvm内存设置等信息。
Tread Dump:
显示Solr Server中当前活跃线程信息,同时也可以跟踪线程运行栈信息。
Core selector
选择一个SolrCore进行详细操作
Overview:
概况
analysis:
通过此界面可以测试索引分析器和搜索分析器的执行情况
Dataimport:
可以定义数据导入处理器,从关系数据库将数据导入 到Solr索引库中。
Documents:
通过此菜单可以创建索引、更新索引、删除索引等操作
/update表示更新索引,solr默认根据id(唯一约束)域来更新Document的内容,如果根据id值搜索不到id域则会执行添加操作,如果找到则更新
Files:
Plugins:
Query:
可以进行各种查询
Replication :
Schema:
配置ik分词器:
solr的管理界面基本就这样了
下面我们配置以下ik分词器
配置上和之前我们弄Lucene是一样的,这是官网地址 https://github.com/magese/ik-analyzer-solr
下载jar包放到 /WEB-INF/lib 文件夹中
复制几个配置文件和词典到classes文件夹中
如果要用那几个词典的话,要自己加上
去你要使用ik分词器的core的约束文件上进行修改
加入以下这一段话
<fieldType name="text_ik" class="solr.TextField"> <analyzer type="index"> <tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" conf="ik.conf" useSmart="false"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> <analyzer type="query"> <tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" conf="ik.conf" useSmart="true"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType>
接下来你就可以自己给Field指定ik分词器了
同时页面也会显示ik分词器了
不过需要注意的是,那个core需要用ik分词器,那个core就需要配置,无法全局配置
solr配置文件:
有几个配置文件我们会经常用到,所以有必要了解下:
core.properties:
里面存放了核心的名字
solrconfig.xml:
这个类其实主要就是配置一些handler,下面是精简后的一些代码,像
/query
/update 这样的处理接口,也是在这里进行配置的,包括我们后期需要加的导入功能,也是要在这里加一个handler
<?xml version="1.0" encoding="UTF-8" ?> <luceneMatchVersion>8.7.0</luceneMatchVersion> <dataDir>${solr.data.dir:}</dataDir> <directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}" /> <codecFactory class="solr.SchemaCodecFactory" /> <indexConfig> <lockType>${solr.lock.type:native}</lockType> </indexConfig> <jmx /> <updateHandler class="solr.DirectUpdateHandler2"> <updateLog> <str name="dir">${solr.ulog.dir:}</str> <int name="numVersionBuckets">${solr.ulog.numVersionBuckets:65536}</int> </updateLog> <autoCommit> <maxTime>${solr.autoCommit.maxTime:15000}</maxTime> <openSearcher>false</openSearcher> </autoCommit> <autoSoftCommit> <maxTime>${solr.autoSoftCommit.maxTime:-1}</maxTime> </autoSoftCommit> </updateHandler> <query> <maxBooleanClauses>${solr.max.booleanClauses:1024}</maxBooleanClauses> <filterCache class="solr.FastLRUCache" size="512" initialSize="512" autowarmCount="0" /> <queryResultCache class="solr.LRUCache" size="512" initialSize="512" autowarmCount="0" /> <documentCache class="solr.LRUCache" size="512" initialSize="512" autowarmCount="0" /> <cache name="perSegFilter" class="solr.search.LRUCache" size="10" initialSize="0" autowarmCount="10" regenerator="solr.NoOpRegenerator" /> <enableLazyFieldLoading>true</enableLazyFieldLoading> <queryResultWindowSize>20</queryResultWindowSize> <queryResultMaxDocsCached>200</queryResultMaxDocsCached> <listener event="newSearcher" class="solr.QuerySenderListener"> <arr name="queries"> </arr> </listener> <listener event="firstSearcher" class="solr.QuerySenderListener"> <arr name="queries"> </arr> </listener> <useColdSearcher>false</useColdSearcher> </query> <circuitBreakers enabled="true"> </circuitBreakers> <requestDispatcher> <httpCaching never304="true" /> </requestDispatcher> <requestHandler name="/select" class="solr.SearchHandler"> <lst name="defaults"> <str name="echoParams">explicit</str> <int name="rows">10</int> </lst> </requestHandler> <requestHandler name="/query" class="solr.SearchHandler"> <lst name="defaults"> <str name="echoParams">explicit</str> <str name="wt">json</str> <str name="indent">true</str> </lst> </requestHandler> <initParams path="/update/**,/query,/select,/spell"> <lst name="defaults"> <str name="df">_text_</str> </lst> </initParams> <searchComponent name="spellcheck" class="solr.SpellCheckComponent"> <str name="queryAnalyzerFieldType">text_general</str> <lst name="spellchecker"> <str name="name">default</str> <str name="field">_text_</str> <str name="classname">solr.DirectSolrSpellChecker</str> <str name="distanceMeasure">internal</str> <float name="accuracy">0.5</float> <int name="maxEdits">2</int> <int name="minPrefix">1</int> <int name="maxInspections">5</int> <int name="minQueryLength">4</int> <float name="maxQueryFrequency">0.01</float> </lst> </searchComponent> <requestHandler name="/spell" class="solr.SearchHandler" startup="lazy"> <lst name="defaults"> <str name="spellcheck.dictionary">default</str> <str name="spellcheck">on</str> <str name="spellcheck.extendedResults">true</str> <str name="spellcheck.count">10</str> <str name="spellcheck.alternativeTermCount">5</str> <str name="spellcheck.maxResultsForSuggest">5</str> <str name="spellcheck.collate">true</str> <str name="spellcheck.collateExtendedResults">true</str> <str name="spellcheck.maxCollationTries">10</str> <str name="spellcheck.maxCollations">5</str> </lst> <arr name="last-components"> <str>spellcheck</str> </arr> </requestHandler> <searchComponent name="terms" class="solr.TermsComponent" /> <requestHandler name="/terms" class="solr.SearchHandler" startup="lazy"> <lst name="defaults"> <bool name="terms">true</bool> <bool name="distrib">false</bool> </lst> <arr name="components"> <str>terms</str> </arr> </requestHandler> <searchComponent class="solr.HighlightComponent" name="highlight"> <highlighting> <fragmenter name="gap" default="true" class="solr.highlight.GapFragmenter"> <lst name="defaults"> <int name="hl.fragsize">100</int> </lst> </fragmenter> <fragmenter name="regex" class="solr.highlight.RegexFragmenter"> <lst name="defaults"> <int name="hl.fragsize">70</int> <float name="hl.regex.slop">0.5</float> <str name="hl.regex.pattern">[-\w ,/\n\"']{20,200}</str> </lst> </fragmenter> <formatter name="html" default="true" class="solr.highlight.HtmlFormatter"> <lst name="defaults"> <str name="hl.simple.pre"> <![CDATA[<em>]]> </str> <str name="hl.simple.post"> <![CDATA[</em>]]> </str> </lst> </formatter> <encoder name="html" class="solr.highlight.HtmlEncoder" /> <fragListBuilder name="simple" class="solr.highlight.SimpleFragListBuilder" /> <fragListBuilder name="single" class="solr.highlight.SingleFragListBuilder" /> <fragListBuilder name="weighted" default="true" class="solr.highlight.WeightedFragListBuilder" /> <fragmentsBuilder name="default" default="true" class="solr.highlight.ScoreOrderFragmentsBuilder"> </fragmentsBuilder> <fragmentsBuilder name="colored" class="solr.highlight.ScoreOrderFragmentsBuilder"> <lst name="defaults"> <str name="hl.tag.pre"> <![CDATA[ <b style="background:yellow">,<b style="background:lawgreen">, <b style="background:aquamarine">,<b style="background:magenta">, <b style="background:palegreen">,<b style="background:coral">, <b style="background:wheat">,<b style="background:khaki">, <b style="background:lime">,<b style="background:deepskyblue">]]> </str> <str name="hl.tag.post"> <![CDATA[</b>]]> </str> </lst> </fragmentsBuilder> <boundaryScanner name="default" default="true" class="solr.highlight.SimpleBoundaryScanner"> <lst name="defaults"> <str name="hl.bs.maxScan">10</str> <str name="hl.bs.chars">.,!? </str> </lst> </boundaryScanner> <boundaryScanner name="breakIterator" class="solr.highlight.BreakIteratorBoundaryScanner"> <lst name="defaults"> <str name="hl.bs.type">WORD</str> <str name="hl.bs.language">en</str> <str name="hl.bs.country">US</str> </lst> </boundaryScanner> </highlighting> </searchComponent> <updateProcessor class="solr.UUIDUpdateProcessorFactory" name="uuid" /> <updateProcessor class="solr.RemoveBlankFieldUpdateProcessorFactory" name="remove-blank" /> <updateProcessor class="solr.FieldNameMutatingUpdateProcessorFactory" name="field-name-mutating"> <str name="pattern">[^\w-\.]</str> <str name="replacement">_</str> </updateProcessor> <updateProcessor class="solr.ParseBooleanFieldUpdateProcessorFactory" name="parse-boolean" /> <updateProcessor class="solr.ParseLongFieldUpdateProcessorFactory" name="parse-long" /> <updateProcessor class="solr.ParseDoubleFieldUpdateProcessorFactory" name="parse-double" /> <updateProcessor class="solr.ParseDateFieldUpdateProcessorFactory" name="parse-date"> <arr name="format"> <str>yyyy-MM-dd[\'T\'[HH:mm[:ss[.SSS]][z</str> <str>yyyy-MM-dd[\'T\'[HH:mm[:ss[,SSS]][z</str> <str>yyyy-MM-dd HH:mm[:ss[.SSS]][z</str> <str>yyyy-MM-dd HH:mm[:ss[,SSS]][z</str> <str>[EEE, ]dd MMM yyyy HH:mm[:ss] z</str> <str>EEEE, dd-MMM-yy HH:mm:ss z</str> <str>EEE MMM ppd HH:mm:ss [z ]yyyy</str> </arr> </updateProcessor> <updateProcessor class="solr.AddSchemaFieldsUpdateProcessorFactory" name="add-schema-fields"> <lst name="typeMapping"> <str name="valueClass">java.lang.String</str> <str name="fieldType">text_general</str> <lst name="copyField"> <str name="dest">*_str</str> <int name="maxChars">256</int> </lst> <bool name="default">true</bool> </lst> <lst name="typeMapping"> <str name="valueClass">java.lang.Boolean</str> <str name="fieldType">booleans</str> </lst> <lst name="typeMapping"> <str name="valueClass">java.util.Date</str> <str name="fieldType">pdates</str> </lst> <lst name="typeMapping"> <str name="valueClass">java.lang.Long</str> <str name="valueClass">java.lang.Integer</str> <str name="fieldType">plongs</str> </lst> <lst name="typeMapping"> <str name="valueClass">java.lang.Number</str> <str name="fieldType">pdoubles</str> </lst> </updateProcessor> <updateRequestProcessorChain name="add-unknown-fields-to-the-schema" default="${update.autoCreateFields:true}" processor="uuid,remove-blank,field-name-mutating,parse-boolean,parse-long,parse-double,parse-date,add-schema-fields"> <processor class="solr.LogUpdateProcessorFactory" /> <processor class="solr.DistributedUpdateProcessorFactory" /> <processor class="solr.RunUpdateProcessorFactory" /> </updateRequestProcessorChain> <queryResponseWriter name="json" class="solr.JSONResponseWriter"> <str name="content-type">text/plain; charset=UTF-8</str> </queryResponseWriter> <requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler"> <lst name="defaults"> <str name="config">data-config.xml</str> </lst> </requestHandler> </config>
managed-schema:
这是一个约束文件,用于指定有哪些Field,Field是否索引、是否分析、是否存在、是否多值,用了哪些分词器
uniqueKey:指定一个field为主键,Lucene中为我们弄了个自增的id作为主键,但是solr选择将id放给我们自己决定。新增文档时必须指定id
fieldType:field类型,可以指定具体是用哪个类处理,是否使用分词器
field:定义一个field,相当于以前Lucene代码的 Field fileNameField = new TextField(“file_name”,fileName, Field.Store.YES);
dynamicField:可以使用通配符,当我们不确定field名字时,就可以用动态Field
copyField:拷贝Field,将多个field的值整合在一个新的field中,并对新的field进行索引,dest属性指定的目标field必须要是多值的,否则会报错
假设我们有这样的一个配置,title和name的值都会拷贝一份到content_text中 <field name="my_name" type="text_ik" indexed="true" stored="true"/> <field name="my_title" type="text_ik" indexed="true" stored="true"/> <field name="my_content_text" type="text_ik" indexed="true" stored="true" multiValued="true"/> <copyField source="my_name" dest="my_content_text" /> <copyField source="my_title" dest="my_content_text" /> 此时我插入一条文档 { "id":"1", "my_name":"张三", "my_title":"法外狂徒" } 那他实际最后会插入成 { "id":"1", "my_name":"张三", "my_title":"法外狂徒", "my_content_text":["张三", "法外狂徒"], }
solr将以前Lucene的代码实现,改成了配置文件的形式
从数据库导入:
正常来说,没进行过任何配置的话,是没法使用导入功能的
所以我们需要进行一些配置。
首先我们要找到这两个jar包
把他们复制到 \webapp\WEB-INF\lib 这里面。然后根据你要操作的数据库,导入对应的jdbc包,我用的是mysql
我有一堆数据
到你要配置导入的core的配置文件夹中,创建一个data-config.xml文件,文件名随意
<?xml version="1.0" encoding="UTF-8" ?> <dataConfig> <dataSource type="JdbcDataSource" driver="com.mysql.cj.jdbc.Driver" url="jdbc:mysql://localhost:3306/sys?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8" user="root" password="root"/> <document> <entity name="product" query="SELECT pid,name,catalog_name,price,description,picture FROM products "> <field column="pid" name="id"/> <field column="name" name="product_name"/> <field column="catalog_name" name="product_catalog_name"/> <field column="price" name="product_price"/> <field column="description" name="product_description"/> <field column="picture" name="product_picture"/> </entity> </document> </dataConfig>
然后修改solrconfig.xml,添加上导入的handler
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler"> <lst name="defaults"> <str name="config">data-config.xml</str> </lst> </requestHandler>
完了后就重启solr就可以用了
导入的可以参考官网这个文档:https://lucene.apache.org/solr/guide/8_7/uploading-structured-data-store-data-with-the-data-import-handler.html
管理界面增删改查:
客户端增删改查:
有两个客户端,solrJ 和 Spring Data Solr ,下面我们两个都会讲到
solrJ :
先加依赖:
<!-- solrJ客户端 --> <dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-solrj</artifactId> <version>8.7.0</version> </dependency> <!-- 编解码的依赖,如果solr没有开认证,可以不加 --> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.14</version> </dependency>
因为我自己solr开启了认证,所以url地址要加上用户名和密码
private final static String solrUrl = "http://root:root@127.0.0.1:8983/solr/new_core";
而且还要自己额外手动创建一个HttpClient
SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient();
目前新版本中solr服务器加密码后,solrJ我只找到这样链接,如果还有别的方法,请留意告诉我一下,谢谢
package com.hongcheng.solrJ; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.SystemDefaultHttpClient; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.beans.Field; import org.apache.solr.client.solrj.impl.HttpClientUtil; import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.MultiMapSolrParams; import org.apache.solr.common.params.SolrParams; import org.junit.jupiter.api.Test; import org.springframework.util.IdGenerator; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Random; import java.util.UUID; class SolrJDemoTest { private final static String solrUrl = "http://root:root@127.0.0.1:8983/solr/new_core"; /** * 添加文档 * 第1步:和Solr服务器建立连接。HttpSolrClient对象建立连接。 * 第2步:创建一个SolrInputDocument对象,然后添加域。 * 第3步:将SolrInputDocument添加到索引库。 * 第4步:提交。 * */ @Test public void addDocument() throws IOException, SolrServerException { SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient(); // 第1步:和Solr服务器建立连接。HttpSolrClient对象建立连接。 HttpSolrClient solrClient = new HttpSolrClient.Builder() .withBaseSolrUrl(solrUrl) .withHttpClient(httpclient) .build(); // 第2步:创建一个SolrInputDocument对象,然后添加域。 SolrInputDocument document = new SolrInputDocument(); document.addField("id", UUID.randomUUID().toString().replace("-","")); document.addField("my_name","老铁"); document.addField("my_title","666"); System.err.println(solrClient.getInvariantParams()); solrClient.add(document); solrClient.commit(); solrClient.close(); } /** * 把一个javabean添加进去 * */ @Test public void addDocumentBean() throws IOException, SolrServerException { SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient(); // 第1步:和Solr服务器建立连接。HttpSolrClient对象建立连接。 HttpSolrClient solrClient = new HttpSolrClient.Builder() .withBaseSolrUrl(solrUrl) .withHttpClient(httpclient) .build(); solrClient.addBean(new User(UUID.randomUUID().toString().replace("-",""),"李四","群中恶霸")); solrClient.commit(); solrClient.close(); } /** * 更新 * 其实并没有实际的更新方法,都是通过add方法添加,如果id值已经存在,就删除再添加 * */ @Test public void updataDocument() throws IOException, SolrServerException { SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient(); // 第1步:和Solr服务器建立连接。HttpSolrClient对象建立连接。 HttpSolrClient solrClient = new HttpSolrClient.Builder() .withBaseSolrUrl(solrUrl) .withHttpClient(httpclient) .build(); solrClient.addBean(new User("e2d1cdb08857452ebd13a460d49e067f","李四","李四不是群中恶霸")); solrClient.commit(); solrClient.close(); } /** * 删除 * */ @Test public void daleteDocument() throws IOException, SolrServerException { SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient(); // 第1步:和Solr服务器建立连接。HttpSolrClient对象建立连接。 HttpSolrClient solrClient = new HttpSolrClient.Builder() .withBaseSolrUrl(solrUrl) .withHttpClient(httpclient) .build(); solrClient.addBean(new User("e2d1cdb08857452ebd13a460d49e067f","李四","李四不是群中恶霸")); solrClient.commit(); solrClient.close(); } /** * 删除 * */ @Test public void searchDocument() throws IOException, SolrServerException { SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient(); // 第1步:和Solr服务器建立连接。HttpSolrClient对象建立连接。 HttpSolrClient solrClient = new HttpSolrClient.Builder() .withBaseSolrUrl(solrUrl) .withHttpClient(httpclient) .build(); //创建一个query对象 SolrQuery query = new SolrQuery(); //设置查询条件,q是必须要填的,如果不填,会查不出来 query.setQuery("*:*"); //过滤条件, query.setFilterQueries("product_catalog_name:幽默杂货"); //排序条件 query.setSort("product_price", SolrQuery.ORDER.asc); //分页处理 query.setStart(0); query.setRows(10); query.setFields("id","product_name","product_price","product_catalog_name","product_description"); //设置默认搜索域 // query.set("df", "product_keywords"); //高亮显示 query.setHighlight(true); //高亮显示的域 query.addHighlightField("product_name"); //高亮显示的前缀 query.setHighlightSimplePre("<span stype=\'color:red;\'>"); //高亮显示的后缀 query.setHighlightSimplePost("</span>"); System.err.println(query); //执行查询 QueryResponse queryResponse = solrClient.query(query); //取查询结果 SolrDocumentList solrDocumentList = queryResponse.getResults(); //共查询到商品数量 System.out.println("共查询到商品数量:" + solrDocumentList.getNumFound()); //遍历查询的结果 for (SolrDocument solrDocument : solrDocumentList) { System.out.println(solrDocument.get("id")); //取高亮显示 String productName = ""; Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting(); List<String> list = highlighting.get(solrDocument.get("id")).get("product_name"); //判断是否有高亮内容 if (null != list) { productName = list.get(0); } else { productName = (String) solrDocument.get("product_name"); } System.out.println(productName); System.out.println(solrDocument.get("product_price")); System.out.println(solrDocument.get("product_catalog_name")); System.out.println(solrDocument.get("product_picture")); } solrClient.close(); } } @Getter @Setter @AllArgsConstructor class User{ @Field("id") private String id; @Field("my_name") private String name; @Field("my_title") private String title; }
Spring Data Solr:
依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-solr</artifactId> </dependency>
配置文件:
这两个是我们自己的自定义属性
spring:
data:
solr:
host: http://root:root@127.0.0.1:8983/solr #因为我自己solr服务开启了认证,所以这里我要加上用户名和密码
core: new_core
配置文件:
注入bean
package com.hongcheng.solrJ; import org.apache.http.impl.client.SystemDefaultHttpClient; import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.solr.core.SolrTemplate; @Configuration public class SolrConfig { @Value("${spring.data.solr.host}") private String solrHost; @Value("${spring.data.solr.core}") private String solrCore; /** * 配置SolrTemplate */ @Bean public SolrTemplate solrTemplate() { SystemDefaultHttpClient httpclient = new SystemDefaultHttpClient(); // 第1步:和Solr服务器建立连接。HttpSolrClient对象建立连接。 HttpSolrClient solrClient = new HttpSolrClient.Builder() .withBaseSolrUrl(solrHost) .withHttpClient(httpclient) .build(); SolrTemplate template = new SolrTemplate(solrClient); return template; } }
增删改查代码:
package com.hongcheng.springdatasolr; import com.hongcheng.solrJ.SolrConfig; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import lombok.ToString; import org.apache.solr.client.solrj.beans.Field; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.solr.core.SolrTemplate; import org.springframework.data.solr.core.query.*; import org.springframework.data.solr.core.query.result.HighlightEntry; import org.springframework.data.solr.core.query.result.HighlightPage; import javax.annotation.Resource; import java.util.List; import java.util.Optional; import java.util.UUID; @SpringBootTest(classes = SolrConfig.class) public class SpringDataSolrDemoTest { @Resource private SolrTemplate solrTemplate; /** * 添加文档 * */ @Test public void addDocument(){ solrTemplate.saveBean("new_core",new SpringDataSolrDemoTest.User(UUID.randomUUID().toString().replace("-",""),"李四","群中恶霸2222")); solrTemplate.commit("new_core"); } /** * 根据id更新文档 * */ @Test public void updateDocument(){ solrTemplate.saveBean("new_core",new SpringDataSolrDemoTest.User("d0e5d628b5724c9ab3d903ba1b88d683","李四","群中恶霸3333")); solrTemplate.commit("new_core"); } /** * 根据id删除文档 * */ @Test public void deleteDocumentById(){ solrTemplate.deleteByIds("new_core","d0e5d628b5724c9ab3d903ba1b88d683"); solrTemplate.commit("new_core"); } /** * 条件删除文档 * */ @Test public void deleteDocumentByQuery(){ SolrDataQuery solrDataQuery = new SimpleQuery(); solrDataQuery.addCriteria(new Criteria("my_title").is("恶霸")); solrTemplate.delete("new_core",solrDataQuery); solrTemplate.commit("new_core"); } /** * 根据id查找文档 * */ @Test public void searchById(){ Optional<Product> new_core = solrTemplate.getById("new_core", "4701", Product.class); System.err.println(new_core.get()); solrTemplate.commit("new_core"); } /** * 条件查询文档 * */ @Test public void searchByQuery(){ SimpleHighlightQuery simpleHighlightQuery = new SimpleHighlightQuery(); // 分页 simpleHighlightQuery.setPageRequest( PageRequest.of(0,10, Sort.Direction.ASC,"product_price")); // 设置投影 simpleHighlightQuery.addProjectionOnField("id"); simpleHighlightQuery.addProjectionOnField("product_catalog_name"); simpleHighlightQuery.addProjectionOnField("product_name"); simpleHighlightQuery.addProjectionOnField("product_price"); // 设置条件 // 可以使用查询表达式 // simpleHighlightQuery.addCriteria(new Criteria().expression("product_catalog_name:餐具 AND product_name:水果 AND product_price:[10 TO 50}")); // 也可以使用api simpleHighlightQuery.addCriteria(new Criteria("product_catalog_name").is("餐具")) .addCriteria(new Criteria("product_name").is("水果")) .addCriteria(new Criteria("product_price").between(10,50,true,false)); // 设置高亮 HighlightOptions highlightOptions = new HighlightOptions(); highlightOptions.setSimplePrefix("<span style=\'color:red;\'>"); highlightOptions.setSimplePostfix("</span>"); highlightOptions.addField("product_catalog_name","product_name"); simpleHighlightQuery.setHighlightOptions(highlightOptions); // 查询 HighlightPage<Product> new_core = solrTemplate.queryForHighlightPage("new_core", simpleHighlightQuery, Product.class); // 打印结果 System.err.println("总记录数:" + new_core.getTotalElements()); for (Product product : new_core.getContent()) { // 获取高亮部分,是不会合并到源文档中的 List<HighlightEntry.Highlight> highlights = new_core.getHighlights(product); highlights.stream().forEach((highlight)->{ highlight.getSnipplets().forEach(System.err::print); System.err.println(); }); System.err.println(product); System.err.println(); } } @Getter @Setter @AllArgsConstructor class User{ @Field("id") private String id; @Field("my_name") private String name; @Field("my_title") private String title; } @Getter @Setter @ToString @AllArgsConstructor class Product{ @Field("id") private String id; @Field("product_catalog_name") private String catalogName; @Field("product_price") private Double price; @Field("product_name") private String name; @Field("product_description") private String description; @Field("product_picture") private String picture; } }
以上便是全部了,如果哪里写错了,请留言说明,我进行修改,谢谢。