标签:
转载请注明出处:http://www.cnblogs.com/zhuxiaojie/p/5764680.html
本教程基于solr5.5
至于为什么要用solr5.5,因为最新的6.10,没有中文的分词器支持,这里使用的是ik分词器,刚好支持到5.5
ik分词器下载地址 :https://github.com/EugenePig/ik-analyzer-solr5 , 下载完之后使用maven命令, mvn package 即可生成jar文件,或者下载我编译好的 http://pan.baidu.com/s/1o7P0846
solr5.5下载地址: http://apache.fayea.com/lucene/solr/5.5.2/
tomcat8下载,下载地址就不说了
下载完成之后,解压solr文件,解压tomcat
在tomcat/webapp/solr/WEB-INF/文件夹中,建立classes目录
<fieldType name="text_ik" class="solr.TextField"> <analyzer type="index" useSmart="false" class="org.wltea.analyzer.lucene.IKAnalyzer" /> <analyzer type="query" useSmart="true" class="org.wltea.analyzer.lucene.IKAnalyzer" /> </fieldType>
域,我个人也称它为字段,它在solr中有特定的含义,就类似数据库中表的列一样,规范着写入的数据,我们先来做个例子。
可以看到,我这次插入的文档,有id,title当然,在solr中,每一条记录都必须有着一个唯一的id,它就类似数据库中的主键,不可重复。这条记录的插入是成功的。
但是,如果我把title改成title1,这就与定义的字段不一样了,就会报错,如下图所示
可以看到,这里提示,未知的字段 title1.
先拿出一条配置来看一下
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
认识一下这些属性
name:域名
type:域的类型,必须匹配类型,不然会报错
indexed:是否要作索引
stored:是否要存储
required:是否必填,一般只有id才会设置
multiValued:是否有多个值,如果设置为多值,里面的值就采用数组的方式来存储,比如商品图片地址(大图,中图,小图等)
同样的,也先拿出一条来看看
<dynamicField name="*_i" type="string" indexed="true" stored="true" multiValued="true" />
何谓动态域呢?就是这个域的名称,是由表达式组成的,只要名称满足了这个 表达式,就可以用这个域
同样的认识一下这些属性
name:域的名称,该域的名称是通过一个表达式来指定的,只要符合这这个规则,就可以使用这个域。比如 aa_i,bb_i,13_i等等,只要满足这个表达式皆可
type:对应的值类型,相应的值必须满足这个类型,不然就会报错
indexed:是否要索引
stored:是否要存储
...其它的属性与普通的域一至
给出一条配置
<uniqueKey>id</uniqueKey>
指定一个唯一的主键,每一个文档中,都应该有一个唯一的主键,这个值不要随便改
给出一条配置
<copyField source="cat" dest="text"/>
说明一下相应的属性
source:源域
dest:目标域
复制域,将源域的内容复制到目标域中
注意:目标域必须是允许多值的,如下,nultiValued必须为true,因为可能多个源域对应一个目标域,所以它需要以数组来存储
<field name="text" type="string" indexed="true" stored="true" multiValued="true"/>
同样的给出一段配置,这段稍微有点复杂
<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100"> <analyzer type="index"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" /> <!-- in this example, we will only use synonyms at query time <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/> --> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> <analyzer type="query"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" /> <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType>
给出相应属性的说明
name:域的名称
class:指定solr的类型
analyzer:分词器的配置
type: index(索引分词器),query(查询分词器)
tokenizer:配置分词器
filter:过滤器
经过上面的学习,差不多了解了一些常用的配置,如今我们用field来配置实际的业务字段,有属性如下
当然,中文分词还是要用的,因为我们在前面的 1.2 章节中,已经配置了一个fieldType的中文分词,所以我们现在一律用中文分词的域类型
主键的id就不需要配置了,默认已经把id配置为主键了,默认的配置如下
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
商品名称(需要分词,需要存储)
<field name="name" type="text_ik" indexed="true" stored="true" />
商品分类(不需要分词,需要存储)
<field name="catalog" type="int" indexed="false" stored="true" />
商品分类名称(需要分词,需要存储)
<field name="catalog_name" type="text_ik" indexed="true" stored="true" />
商品价格(不分词,需要存储)
<field name="price" type="double" indexed="false" stored="true" />
商品描述(需要分词,不需要存储)
<field name="description" type="text_ik" indexed="true" stored="false" />
商品图片(不需要分词,需要存储)
<field name="picture" type="string" indexed="false" stored="true" />
复制域的应用
前面我们了解了复制域,但是却不知道它的应用场景,现在我们结合实际情况来讲一下复制域
用户在搜索框搜索的时候,有可能输入的是商品名称,也有可能输入的是商品描述,也有可能输入的是一个商品类型,那么这些值的搜索,肯定在后台是对应一个域的,那么既然如此,我们就可以把这些域合并成一个,这样在后台只需要单独的对这一个域进行搜索就可以了
先定义一个目标域
<field name="keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
复制域,把商品名称,商品描述,商品类型名称复制到上面的这个域中
<copyField source="name" dest="keywords"/> <copyField source="catalog_name" dest="keywords"/> <copyField source="description" dest="keywords"/>
solr默认是没有开启dataimport这个功能的,所以我们要经过一点配置来开启它
<requestHandler name="/dataimport" class="solr.DataImportHandler"> <lst name="defaults"> <str name="config">data-config.xml</str> </lst> </requestHandler>
<dataConfig> <!-- 这是mysql的配置,学会jdbc的都应该看得懂 --> <dataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/solr/useUnicode=true&characterEncoding=utf-8" user="root" password="密码"/> <document> <!-- name属性,就代表着一个文档,可以随便命名 --> <!-- query是一条sql,代表在数据库查找出来的数据 --> <entity name="product" query="select * from products"> <!-- 每一个field映射着数据库中列与文档中的域,column是数据库列,name是solr的域(必须是在managed-schema文件中配置过的域才行) --> <field column="pid" name="id"/> <field column="name" name="product_name"/> <field column="catalog" name="product_catalog"/> <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>
代码地址 https://github.com/zxj19951029/useSolrj
上面一章节已经讲完了solr的安装与配置,现在说一下使用solrj来维护solr的索引及操作,solrj就是一个java的客户端,是一个jar包的使用
首先引入MAVEN的依赖,solrj的版本号要对应solr的版本号
<dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-solrj</artifactId> <version>5.5.2</version> </dependency>
首先说明,在solr中,增加与修改都是一回事,当这个id不存在时,则是添加,当这个id存在时,则是修改
代码很好理解,直接给出
private String serverUrl = "http://192.168.1.4:8080/solr/core1"; /** * 增加与修改<br> * 增加与修改其实是一回事,只要id不存在,则增加,如果id存在,则是修改 * @throws IOException * @throws SolrServerException */ @Test public void upadteIndex() throws SolrServerException, IOException{ //已废弃的方法 //HttpSolrServer server = new HttpSolrServer("http://192.168.1.4:8080/solr/core1"); //创建 HttpSolrClient client = new HttpSolrClient(serverUrl); SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", "zxj1"); doc.addField("product_name", "javaWEB技术"); doc.addField("product_catalog", "1"); doc.addField("product_catalog_name", "书籍"); doc.addField("product_price", "11"); doc.addField("product_description", "这是一本好书"); doc.addField("product_picture", "图片地址"); client.add(doc); client.commit(); client.close(); }
删除的代码也直接给出,看代码里面的注释就可以了
/** * 删除索引 * @throws Exception */ @Test public void deleteIndex()throws Exception{ HttpSolrClient client = new HttpSolrClient(serverUrl); //1.删除一个 client.deleteById("zxj1"); //2.删除多个 List<String> ids = new ArrayList<>(); ids.add("1"); ids.add("2"); client.deleteById(ids); //3.根据查询条件删除数据,这里的条件只能有一个,不能以逗号相隔 client.deleteByQuery("id:zxj1"); //4.删除全部,删除不可恢复 client.deleteByQuery("*:*"); //一定要记得提交,否则不起作用 client.commit(); client.close(); }
查询稍微复杂一点,但是与solr管理界面的条件一致
这里先给出一些查询的说明,建议查看这篇文章,个人感觉还是不错的http://blog.csdn.net/gufengshanyin/article/details/21098879
1. “:” 指定字段查指定值,如返回所有值*:*
2. “?” 表示单个任意字符的通配
3. “*” 表示多个任意字符的通配(不能在检索的项开始使用*或者?符号)
4. “~” 表示模糊检索,如检索拼写类似于”roam”的项这样写:roam~将找到形如foam和roams的单词;roam~0.8,检索返回相似度在0.8以上的记录。
5. 邻近检索,如检索相隔10个单词的”apache”和”jakarta”,”jakarta apache”~10
6. “^” 控制相关度检索,如检索jakarta apache,同时希望去让”jakarta”的相关度更加好,那么在其后加上”^”符号和增量值,即jakarta^4 apache
7. 布尔操作符AND、||
8. 布尔操作符OR、&&
9. 布尔操作符NOT、!、- (排除操作符不能单独与项使用构成查询)
10. “+” 存在操作符,要求符号”+”后的项必须在文档相应的域中存在
11. ( ) 用于构成子查询
12. [] 包含范围检索,如检索某时间段记录,包含头尾,date:[200707 TO 200710]
给出基本的代码看一下,仅仅作为一个基本的查询,高级的查询,各位要自己结合文档
package zxj.solrj; import java.util.List; import java.util.Map; import org.apache.solr.client.solrj.SolrQuery; 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.junit.Test; /** * 搜索 * @author Administrator * */ public class IndexSearch { private String serverUrl = "http://192.168.1.4:8080/solr/core1"; @Test public void search()throws Exception{ HttpSolrClient client = new HttpSolrClient(serverUrl); //创建查询对象 SolrQuery query = new SolrQuery(); //q 查询字符串,如果查询所有*:* query.set("q", "product_name:小黄人"); //fq 过滤条件,过滤是基于查询结果中的过滤 query.set("fq", "product_catalog_name:幽默杂货"); //sort 排序,请注意,如果一个字段没有被索引,那么它是无法排序的 // query.set("sort", "product_price desc"); //start row 分页信息,与mysql的limit的两个参数一致效果 query.setStart(0); query.setRows(10); //fl 查询哪些结果出来,不写的话,就查询全部,所以我这里就不写了 // query.set("fl", ""); //df 默认搜索的域 query.set("df", "product_keywords"); //======高亮设置=== //开启高亮 query.setHighlight(true); //高亮域 query.addHighlightField("product_name"); //前缀 query.setHighlightSimplePre("<span style=‘color:red‘>"); //后缀 query.setHighlightSimplePost("</span>"); //执行搜索 QueryResponse queryResponse = client.query(query); //搜索结果 SolrDocumentList results = queryResponse.getResults(); //查询出来的数量 long numFound = results.getNumFound(); System.out.println("总查询出:" + numFound + "条记录"); //遍历搜索记录 //获取高亮信息 Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting(); for (SolrDocument solrDocument : results) { System.out.println("商品id:" + solrDocument.get("id")); System.out.println("商品名称 :" + solrDocument.get("product_name")); System.out.println("商品分类:" + solrDocument.get("product_catalog")); System.out.println("商品分类名称:" + solrDocument.get("product_catalog_name")); System.out.println("商品价格:" + solrDocument.get("product_price")); System.out.println("商品描述:" + solrDocument.get("product_description")); System.out.println("商品图片:" + solrDocument.get("product_picture")); //输出高亮 Map<String, List<String>> map = highlighting.get(solrDocument.get("id")); List<String> list = map.get("product_name"); if(list != null && list.size() > 0){ System.out.println(list.get(0)); } } client.close(); } }
注意:没有索引的域,是不能用作排序的
标签:
原文地址:http://www.cnblogs.com/zhuxiaojie/p/5764680.html