一 . 什么是solr
solr是以lucene为内核开发的企业级搜索应用 应用程序可以通过http请求方式来提交索引,查询索引,提供了比lucene更丰富的查询语言,是一个高性能,高可用环境全文搜索引擎
二 .solr安装配置
1.下载solr安装包solr所有版本 (http://archive.apache.org/dist/lucene/solr/) 这里下载 solr-5.5.4
2.安装 解压将solr-5.5.4\server\solr-webapp下的webapp 拷贝到tomcat\webapps目录下 改名为solr 启动tomcat直接访问 会出现缺少SLF4j包异常 应该去 应该去 解压包 /server/lib/ext下找到并拷贝到 tomcat/solr/lib目录下 然后重启 继续访问 出现以下错误
java.lang.NoSuchMethodError: javax.servlet.ServletInputStream.isFinished()Z
org.apache.solr.servlet.SolrDispatchFilter.consumeInputFully(SolrDispatchFilter.java:284)
org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:274)
org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:208)
明显是Servlet版本不一致 tomcat6不支持solr5.54 加大tomcat版本 tomcat7也不支持 换成tomcat8 启动后访问 依然错误org.apache.solr.common.SolrException: Error processing the request. CoreContainer is either not initialized or shutting down.
org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:217)
org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:208)
是因为需要配置solrhome和solrhome的配置环境3.配置solrhome
找到 tomcat\solr\WEB-INF\web.xml 编辑 找到以下这段(配置solrhome) 去掉注释 将第二个参数配置为本地创建的一个新目录即可
<env-entry>
<env-entry-name>solr/home</env-entry-name>
<env-entry-value>F:\solrHome</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>
到solr解压包/server/solr目录拷贝所有文件到 以上web.xml指定的路径F:\solrHom下 重启tomcat 访问http://localhost:8080/solor/index.html 或者 http://localhost:8080/solr/admin.html
4.配置core(core类似于数据库可以插入多个document(数据库表行)每个document拥有多个 field 数据库的列)
solrhome下新建一个core目录 比如core1
拷贝 solr解压包下\server\solr\configsets\basic_configs\conf到新建目录 core1中
进入solr管理网页 点击 core admin 添加该core1
点击Add core后 成功后 检查 core目录 发现多了 core.properties和data两个资源
5.配置文件理解
core/conf目录下的两个配置文件非常重要
managed-schema 主要用于配置 可以提交到该core的所有field定义,field的类型定义,唯一标识符等
常用配置如下:
定义字段 _version_ 类型为long indexed="true" 会进行分词索引 stored="true"表示存储到磁盘
<field name="_version_" type="long" indexed="true" stored="true"/>
定义字段 id required="true" 表示所有的document必须添加id字段 multiValued="false" 表示是否是多值字段
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
定义动态字段 所以_i结尾的字段都可以写入到当前的core
<dynamicField name="*_i" type="int" indexed="true" stored="true"/>
定义唯一标识符的字段
<uniqueKey>id</uniqueKey>
定义字段类型的别名
<fieldType name="string" class="solr.StrField" sortMissingLast="true" />
solrconfig.xml 主要用于配置solor的主要配置信息 比如lucene版本 缓存 数据目录 请求路径映射 等 表示lucene版本
<luceneMatchVersion>5.5.4</luceneMatchVersion>
表示数据目录 默认是data目录
<dataDir>${solr.data.dir:}</dataDir>
自动提交配置
<autoCommit>
当超过15000ms后自动提交所有数据
<maxTime>${solr.autoCommit.maxTime:15000}</maxTime>
是否马上就可以查询到
<openSearcher>false</openSearcher>
</autoCommit>
表示当路径为 /select时查询所有的数据
<requestHandler name="/select" class="solr.SearchHandler">
<!-- default values for query parameters can be specified, these
will be overridden by parameters in the request
-->
<lst name="defaults">
<str name="echoParams">explicit</str>
<int name="rows">10</int>
</lst>
</requestHandler>
尝试在界面上添加数据和查询数据添加数据
查询结果
查询的参数列表
q表示查询的条件 字段名:值的格式
fq表示filter query 过滤条件 和q是and的关系支持各种逻辑运算符 (参考https://cwiki.apache.org/confluence/display/solr/The+Standard+Query+Parser)
sort表示排序 的字段 字段名 asc|desc
start 表示从第几行开始 rows表示查询的总行数
fl表示查询显示的列 比如只需要查询 name_s,sex_i 这两列 使用,隔开
df表示默认的查询字段 一般不设置
Raw Query Parameters表示原始查询字段 可以使用 start=0&rows=10这种url的方式传入参数
wt(write type)表示写入的格式 可以使用json和xml
shards 多核同时搜索 solrhome拷贝core1为core2 管理平台添加core 设置参数为 路径,路径来设置需要搜索的核
String shards = "localhost:8080/solr/core1,localhost:8080/solr/core2";
query.set("shards", shards);
其他参考(https://cwiki.apache.org/confluence/display/solr/Common+Query+Parameters)5.配置中文分词器
默认solr 没有使用中文分词器 所有搜索的词 都是整个句子就是一个词 搜索时 将单词全部写入才能搜索或者使用* 需要配置中文分词器
目前比较好用的分词器 是IK 2012年停更 只支持到 Lucene4.7 所有 solr5.5 需要lucene5支持 需要修改部分源码来支持solr5.5
找到 IKAnalyzer类 需要重写 protected TokenStreamComponents createComponents(String fieldName) 方法
找到 IKTokenizer类 需要重写构造方法 public IKTokenizer(Reader in, boolean useSmart) 为 public IKTokenizer(boolean useSmart) {
在任意项目中 使用maven 引用lucene5 和ik
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
<exclusions>
<exclusion>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
</exclusion>
</exclusions>
</dependency>
在项目中 添加完整的包名和类名 和 ik中一致 拷贝源代码代码修改对应的方法即可
IKAnalyzer
/**
*
*/
package org.wltea.analyzer.lucene;
import java.io.Reader;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Tokenizer;
/**
*/
public final class IKAnalyzer extends Analyzer {
private boolean useSmart;
public boolean useSmart() {
return useSmart;
}
public void setUseSmart(boolean useSmart) {
this.useSmart = useSmart;
}
/**
*/
public IKAnalyzer() {
this(false);
}
/**
*/
public IKAnalyzer(boolean useSmart) {
super();
this.useSmart = useSmart;
}
/**这里就去掉了 Reader的一个参数
*/
@Override
protected TokenStreamComponents createComponents(String fieldName) {
Tokenizer _IKTokenizer = new IKTokenizer(this.useSmart());
return new TokenStreamComponents(_IKTokenizer);
}
}
IKTokenizer/**
*
*/
package org.wltea.analyzer.lucene;
import java.io.IOException;
import java.io.Reader;
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;
public final class IKTokenizer extends Tokenizer {
private IKSegmenter _IKImplement;
private final CharTermAttribute termAtt;
private final OffsetAttribute offsetAtt;
private final TypeAttribute typeAtt;
private int endPosition;
//去掉了其中Reader的第一个构造参数
public IKTokenizer(boolean useSmart) {
super();//去掉super中的构造参数
offsetAtt = addAttribute(OffsetAttribute.class);
termAtt = addAttribute(CharTermAttribute.class);
typeAtt = addAttribute(TypeAttribute.class);
_IKImplement = new IKSegmenter(input, useSmart);
}
@Override
public boolean incrementToken() throws IOException {
clearAttributes();
Lexeme nextLexeme = _IKImplement.next();
if (nextLexeme != null) {
termAtt.append(nextLexeme.getLexemeText());
termAtt.setLength(nextLexeme.getLength());
offsetAtt.setOffset(nextLexeme.getBeginPosition(), nextLexeme.getEndPosition());
endPosition = nextLexeme.getEndPosition();
typeAtt.setType(nextLexeme.getLexemeTypeString());
return true;
}
return false;
}
/*
* (non-Javadoc)
* @see org.apache.lucene.analysis.Tokenizer#reset(java.io.Reader)
*/
@Override
public void reset() throws IOException {
super.reset();
_IKImplement.reset(input);
}
@Override
public final void end() {
// set final offset
int finalOffset = correctOffset(this.endPosition);
offsetAtt.setOffset(finalOffset, finalOffset);
}
}
将编译好的class文件替换原始jar包即可,将架包加入到\apache-tomcat-8.0.44\webapps\solr\WEB-INF\lib下将solrhome下 配置文件managed-schema 添加一个字段类型 使用ik分词器
<fieldType name="text_ik" class="solr.TextField" >
<analyzer type="index" isMaxWordLength="false" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
<analyzer type="query" isMaxWordLength="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
不能修改 StrField 不支持自定义分词器
<fieldType name="string" class="solr.StrField" sortMissingLast="true" >
</fieldType>
<dynamicField name="*_ik" type="text_ik" indexed="true" stored="true" multiValued="true"/>
重启 或者 cloud环境下重新生成collection 插入数据即可实现中文分词 通过某些中文关键字搜索三.solr客户端
solr提供的solrj java客户端可以使用java来添加和查询索引
使用maven引入solrj的依赖库
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>5.5.4</version>
</dependency>
使用客户端操作添加和查询索引的代码package cn.et;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrQuery.ORDER;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.response.Group;
import org.apache.solr.client.solrj.response.GroupCommand;
import org.apache.solr.client.solrj.response.GroupResponse;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.GroupParams;
public class TestSolr {
//获取solr的URL
static String url="http://localhost:8080/solr/core1";
static SolrClient solr;
static{
solr= new HttpSolrClient(url);
}
public static void main(String[] args) throws Exception{
group();
}
//删除,根据id删除
public static void deleteById() throws Exception{
UpdateResponse ur=solr.deleteById("2");
solr.commit();
solr.close();
}
//删除,根据内容删除
public static void deleteByParam() throws Exception{
UpdateResponse ur=solr.deleteByQuery("foodname_ik:鸡");
solr.commit();
solr.close();
}
//分组
public static void group() throws SolrServerException, IOException{
//查询的值
SolrQuery solrquery=new SolrQuery("content_ik:桂林");
//启用分组
solrquery.setParam(GroupParams.GROUP, true);
//按什么分组
solrquery.setParam(GroupParams.GROUP_FIELD, "type_s");
//是否获取统计的分组数量
solrquery.setParam("group.ngroups", true);
//显示每个分组显示的数量
//sq.setParam(GroupParams.GROUP_LIMIT, "5");
//默认只取分组中的第一条,需要设计
solrquery.setParam(GroupParams.GROUP_LIMIT,"10");
//获取到查询
QueryResponse que= solr.query(solrquery);
//获取分组的结果
GroupResponse groupResponse = que.getGroupResponse();
List<GroupCommand> values = groupResponse.getValues();
//循环遍历
for(GroupCommand groupCommand:values){
//获取到所有分组
List<Group> groups = groupCommand.getValues();
for(Group group:groups){
//获取到分组的组名
System.out.println(group.getGroupValue());
//获取到分组里面的所有成员
SolrDocumentList result = group.getResult();
for(SolrDocument doc:result){
//获取到成员的名字
System.out.println(doc.getFieldValue("content_ik"));
}
System.out.println("------------");
}
}
}
//查询
public static void read() throws Exception{
SolrQuery squery= new SolrQuery();
//获取查询的值
squery.setQuery("foodname_ik:鸡");
//过滤查询不考虑得分,过滤不能高亮
//squery.setFilterQueries("foodname_ik:鸡");
//根据id倒叙排列
squery.setSort("id", ORDER.asc);
//分页查询两个参数
//从第几个下标开始查询
squery.setStart(0);
//每次显示几个
squery.setRows(2);
//设计高亮
//是否高亮
squery.setHighlight(true);
//高亮的词
squery.set("foodname_ik");
squery.set("hl.fl", "foodname_ik");
//前缀
squery.setHighlightSimplePre("<font color=red");
//后缀
squery.setHighlightSimplePost("</font>");
//squery.set(HighlightParams.FIELDS, "foodname_ik");
//查询
QueryResponse qr=solr.query(squery);
//获取最终的document
SolrDocumentList results=qr.getResults();
Map<String, Map<String, List<String>>> highlighting = qr.getHighlighting();
//循环遍历出来
for(SolrDocument doc:results){
String id=doc.getFieldValue("id").toString();
System.out.println(doc.getFieldValue("id"));
System.out.println(doc.getFieldValue("foodname_ik"));
Map<String, List<String>> map = highlighting.get(id);
List<String> list = map.get("foodname_ik");
String highStr=list.get(0);
System.out.println(highStr);
}
solr.close();
}
//写入索引库
public static void writer() throws Exception{
SolrInputDocument document =new SolrInputDocument();
document.addField("id", "5");
document.addField("foodname_ik", "香酥鸡");
//写入索引库
UpdateResponse res= solr.add(document);
solr.commit();
solr.close();
}
}
使用javabean的方式操作
javabean定义
package cn.et.test;
import org.apache.solr.client.solrj.beans.Field;
public class Food {
public Food() {
}
@Field
private String id;
@Field
private String foodname_ik;
}
运行
package cn.et.test;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.FacetField.Count;
import org.apache.solr.client.solrj.response.PivotField;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.util.NamedList;
import org.junit.Test;
public class SolrTest {
//获取solr的URL
static String url="http://localhost:8080/solr/core1";
//写入
@Test
public void testWriteDoc() throws SolrServerException, IOException{
//连接solr
HttpSolrClient solr=new HttpSolrClient(url);
//new实体类
Food fd=new Food();
fd.setId("15");
fd.setFoodname_ik("道口烧鸡");
//写入
solr.addBean(fd);
solr.commit();
solr.close();
}
//查询
@Test
public void testReadDoc() throws SolrServerException, IOException{
//连接solr
HttpSolrClient hsc=new HttpSolrClient(url);
SolrQuery sq=new SolrQuery();
//传入搜索的值
sq.setQuery("foodname_ik:烧鸡");
//按什么排序
sq.set("sort", "id asc");
//分页查询两个参数
//从第几个下标开始查询
sq.setStart(0);
//每次显示几个
sq.setRows(1);
//获取最终的document
List<Food> sdl=hsc.query(sq).getBeans(Food.class);
//循环遍历出来
for(Food sd:sdl){
System.out.println(sd.getFoodname_ik());
}
hsc.close();
}
/**
* Facet 面 用于对搜索的结果进行分类
* 比如按国家分类 addFacetField 表示按某些字段进行分类是普通分类 结果为:
* country_s
美国:6
中国:5
sq.addFacetQuery("age_i:[1 TO 20]");
sq.addFacetQuery("age_i:[21 TO 50]");
sq.addFacetQuery("age_i:[51 TO *]");
可以将多个范围值 添加到FacetQuery可以获取到这些Query的统计数量 比如
{age_i:[1 TO 20]=3, age_i:[20 TO 50]=5, age_i:[50 TO *]=5}
其他 参考 https://wiki.apache.org/solr/SimpleFacetParameters#facet.query_:_Arbitrary_Query_Faceting
*/
@Test
public void queryFacet() throws SolrServerException, IOException{
//连接solr
HttpSolrClient scs= new HttpSolrClient(url);
SolrQuery sq= new SolrQuery();
//启动facet
sq.setFacet(true);
//按字段分类 相同的归于同一类
sq.addFacetField("country_s");
//特殊分类 添加范围
sq.addFacetQuery("age_i:[1 TO 20]");
sq.addFacetQuery("age_i:[21 TO 50]");
sq.addFacetQuery("age_i:[51 TO *]");
//facet字段分类的前缀
sq.setFacetPrefix("");
//根据count数量 升序 和降序 也可以根据索引
sq.setFacetSort("count asc");
//获取查询的值
sq.setQuery("*:*");
//查询
QueryResponse qr=scs.query(sq);
//获取到查询的所有的facet
List<FacetField> ff = qr.getFacetFields();
//获取到范围分类的对应统计数量
System.out.println(qr.getFacetQuery());
//获取到根据字段分类的对应统计数量
for(FacetField ft:ff){
//按什么字段分类
System.out.println(ft.getName());
List<Count> cut = ft.getValues();
for(Count count:cut){
//类名和所属个数
System.out.println(count.getName()+count.getCount());
}
}
scs.commit();
scs.close();
}
/**
* Facet 参考https://wiki.apache.org/solr/SimpleFacetParameters#Pivot_.28ie_Decision_Tree.29_Faceting
* 可以按照多维度来进行分类
* 比如按照国家分类后 再按照省份分类(国家和省份字段不要使用中文分词器 否则分类被拆成很多类别)
*
* 结果一般为:
* 美国6:
* 加利福尼亚州3
* 德克萨斯州3
* 中国5:
* 湖南省3
* 广东省2
* @throws IOException
* @throws SolrServerException
*/
@Test
public void queryF() throws SolrServerException, IOException{
//连接solr
HttpSolrClient scs= new HttpSolrClient(url);
SolrQuery sq= new SolrQuery();
//启动facet
sq.setFacet(true);
//按国家和省份进行二维分类 同一个字符串使用,隔开
sq.addFacetPivotField("country_s,provice_s");
//获取查询的值
sq.setQuery("*:*");
//查询
QueryResponse qr=scs.query(sq,SolrRequest.METHOD.POST);
//获取到查询的所有的facet
NamedList<List<PivotField>> ff = qr.getFacetPivot();
//遍历查询
for(Map.Entry<String, List<PivotField>> me :ff){
//获取最大的分类
List<PivotField> lp = me.getValue();
for(PivotField pf:lp){
System.out.println("一级分类"+pf.getValue()+pf.getCount());
//获取最大里分类的小分类
List<PivotField> pit = pf.getPivot();
for(PivotField pl:pit){
System.out.println("二级分类"+pl.getValue()+pl.getCount());
}
}
}
scs.commit();
scs.close();
}
}
四.solr一些其他高级查询(参考代码)
package cn.et.solor;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.Group;
import org.apache.solr.client.solrj.response.GroupCommand;
import org.apache.solr.client.solrj.response.GroupResponse;
import org.apache.solr.client.solrj.response.PivotField;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.FacetField.Count;
import org.apache.solr.common.params.GroupParams;
import org.apache.solr.common.util.NamedList;
import org.junit.Test;
/**
* 支持一些高级特性 比如高亮 分类 分组 mtl(相似)
{"id":"1","country_s":"美国","provice_s":"加利福尼亚州","city_s":"旧金山","age_i":"30","name_s":"John","desc_s":"John is come from austrina John,s Dad is Johh Super"}
{"id":"2","country_s":"美国","provice_s":"加利福尼亚州","city_s":"好莱坞","age_i":"40","name_s":"Mike","desc_s":"Mike is come from austrina Mike,s Dad is Mike Super"}
{"id":"3","country_s":"美国","provice_s":"加利福尼亚州","city_s":"圣地牙哥","age_i":"50","name_s":"Cherry","desc_s":"Cherry is come from austrina Cherry,s Dad is Cherry Super"}
{"id":"4","country_s":"美国","provice_s":"德克萨斯州","city_s":"休斯顿","age_i":"60","name_s":"Miya","desc_s":"Miya is come from austrina Miya,s Dad is Miya Super"}
{"id":"5","country_s":"美国","provice_s":"德克萨斯州","city_s":"大学城","age_i":"70","name_s":"fubos","desc_s":"fubos is come from austrina fubos,s Dad is fubos Super"}
{"id":"6","country_s":"美国","provice_s":"德克萨斯州","city_s":"麦亚伦","age_i":"20","name_s":"marry","desc_s":"marry is come from austrina marry,s Dad is marry Super"}
{"id":"7","country_s":"中国","provice_s":"湖南省","city_s":"长沙市","age_i":"18","name_s":"张三","desc_s":"张三来自长沙市 是公务员一名"}
{"id":"8","country_s":"中国","provice_s":"湖南省","city_s":"岳阳市","age_i":"15","name_s":"李四","desc_s":"李四来自岳阳市 是一名清洁工"}
{"id":"9","country_s":"中国","provice_s":"湖南省","city_s":"株洲市","age_i":"33","name_s":"李光四","desc_s":"李光四 老家岳阳市 来自株洲 是李四的侄子"}
{"id":"10","country_s":"中国","provice_s":"广东省","city_s":"深圳市","age_i":"67","name_s":"王五","desc_s":"王五来自深圳市 是来自深圳的一名海关缉私精英"}
{"id":"11","country_s":"中国","provice_s":"广东省","city_s":"广州市","age_i":"89","name_s":"王冠宇","desc_s":"王冠宇是王五的儿子"}
*/
public class TestCloud {
/**
* 连接solrcloud
* @return
*/
public CloudSolrClient getCloudSolrClient(){
String zkHost="localhost:2181";
CloudSolrClient csc=new CloudSolrClient(zkHost);
csc.setDefaultCollection("collection1");//集合名称
return csc;
}
/**
* solrcloud保存 修改 删除和单机相同
*/
@Test
public void save() throws IOException, SolrServerException{
CloudSolrClient csc=getCloudSolrClient();
UserInfo ui=new UserInfo();
ui.setId("4");
ui.setName_s("王五");
ui.setAge_i(100);
csc.addBean(ui);
csc.commit();
csc.close();
}
/**
* solrcloud 删除
*/
//@Test
public void delete() throws IOException, SolrServerException{
CloudSolrClient csc=getCloudSolrClient();
csc.deleteByQuery("*:*");
csc.commit();
csc.close();
}
/**
* solrcloud高亮显示
* 必须设置中文分词器
*/
@Test
public void queryHign() throws IOException, SolrServerException{
CloudSolrClient csc=getCloudSolrClient();
SolrQuery sq=new SolrQuery();
sq.setQuery("desc_s:王五");
sq.addHighlightField("desc_s");
sq.setHighlight(true);
sq.setHighlightSimplePre("<font color=red>");
sq.setHighlightSimplePost("</font>");
QueryResponse qr=csc.query(sq);
List<UserInfo> userInfo=qr.getBeans(UserInfo.class);
Map<String, Map<String, List<String>>> highlighting = qr.getHighlighting();
System.out.println(highlighting);
for(UserInfo ui:userInfo){
System.out.println(ui.getName_s());
}
System.out.println(userInfo.size());
csc.commit();
csc.close();
}
/**
* Facet 面 用于对搜索的结果进行分类
* 比如按国家分类 addFacetField 表示按某些字段进行分类是普通分类 结果为:
* country_s
美国:6
中国:5
sq.addFacetQuery("age_i:[1 TO 20]");
sq.addFacetQuery("age_i:[21 TO 50]");
sq.addFacetQuery("age_i:[51 TO *]");
可以将多个范围值 添加到FacetQuery可以获取到这些Query的统计数量 比如
{age_i:[1 TO 20]=3, age_i:[20 TO 50]=5, age_i:[50 TO *]=5}
其他 参考 https://wiki.apache.org/solr/SimpleFacetParameters#facet.query_:_Arbitrary_Query_Faceting
*/
@Test
public void queryFacet() throws IOException, SolrServerException{
CloudSolrClient csc=getCloudSolrClient();
SolrQuery sq=new SolrQuery();
sq.setFacet(true);
//按字段分类 相同的归于一类
sq.addFacetField("country_s");
//特殊分类 添加范围
sq.addFacetQuery("age_i:[1 TO 20]");
sq.addFacetQuery("age_i:[21 TO 50]");
sq.addFacetQuery("age_i:[51 TO *]");
//这只facet字段分类的前缀
sq.setFacetPrefix("");
//根据 count 数量 升序和降序 也可以根据索引
sq.setFacetSort("count asc");
sq.setQuery("*:*");
QueryResponse qr=csc.query(sq);
List<FacetField> ff=qr.getFacetFields();
//获取到范围分类的对应统计数量
System.out.println(qr.getFacetQuery());
//获取到根据字段分类的对应统计数量
for(FacetField ftmp:ff){
System.out.println(ftmp.getName());
List<Count> cou=ftmp.getValues();
for (Count count : cou){
System.out.println(count.getName()+":"+ count.getCount());
}
}
csc.commit();
csc.close();
}
/**
* Facet 参考https://wiki.apache.org/solr/SimpleFacetParameters#Pivot_.28ie_Decision_Tree.29_Faceting
* 可以按照多维度来进行分类
* 比如按照国家分类后 再按照省份分类(国家和省份字段不要使用中文分词器 否则分类被拆成很多类别)
*
* 结果一般为:
* 美国6:
* 加利福尼亚州3
* 德克萨斯州3
* 中国5:
* 湖南省3
* 广东省2
*/
@Test
public void queryFacetPivot() throws IOException, SolrServerException{
CloudSolrClient csc=getCloudSolrClient();
SolrQuery sq=new SolrQuery();
sq.setFacet(true);
//按国家和省份进行二维分类 同一个字符串使用,隔开
sq.addFacetPivotField("country_s,provice_s");
sq.setQuery("*:*");
QueryResponse qr=csc.query(sq,SolrRequest.METHOD.POST);
NamedList<List<PivotField>> ff=qr.getFacetPivot();
//获取到根据字段分类的对应统计数量
for(Map.Entry<String,List<PivotField>> me:ff){
List<PivotField> lpf=me.getValue();
for(PivotField pf:lpf){
System.out.println("一级分类:"+pf.getValue()+pf.getCount()+"---->");
List<PivotField> clpf=pf.getPivot();
for(PivotField cpf:clpf){
System.out.println("二级分类:"+cpf.getValue()+cpf.getCount());
}
}
}
csc.commit();
csc.close();
}
/**
* 分组是分类的升级 同时可以获取到分组下的一部分元素(https://cwiki.apache.org/confluence/display/solr/Result+Grouping)
分组的字段的数据如果是集群环境 要求数据被写入到一个分片中 否则无法分组查询
*/
@Test
public void queryGroup() throws IOException, SolrServerException{
CloudSolrClient csc=getCloudSolrClient();
SolrQuery sq=new SolrQuery();
//sq.setParam("shards.tolerant", true);
sq.setParam(GroupParams.GROUP,true);
sq.setParam(GroupParams.GROUP_FIELD, "country_s");
sq.setParam("group.ngroups", true);
//sq.setParam(GroupParams.GROUP_LIMIT, "5");
sq.setQuery("*:*");
sq.setRows(10);
QueryResponse qr=csc.query(sq);
GroupResponse ff=qr.getGroupResponse();
for(GroupCommand me:ff.getValues()){
System.out.println(me.getName());
List<Group> groups=me.getValues();
System.out.println(groups);
}
csc.commit();
csc.close();
}
}