facet搜索
/**
*
* 搜索功能优化-关键词搜索
* 搜索范围:商品名称、店铺名称
*
* @param keywords
* @param skip
* @param size
* @return
*/
@Override
public List<KeywordSearchByName> searchByName(String keywords, Integer skip, Integer size) {
solrServer = SolrContext.getServer();
SolrQuery query = new SolrQuery();// 建立一个新的查询
query.setQuery(keywords);
query.setFacet(true);// 设置facet=on
query.addFacetField(new String[] {"v1_groupId"});// 设置需要facet的字段
query.setFacetLimit(20);// 限制facet返回的数量
query.setFacetMissing(false);// 不统计null的值
query.setFacetMinCount(1);// 设置返回的数据中每个分组的数据最小值,比如设置为1,则统计数量最小为1,不然不显示
QueryResponse response = null;
try {
response = solrServer.query(query);
System.out.println("查询时间:" + response.getQTime());
List<FacetField> facets = response.getFacetFields();// 返回的facet列表
for (FacetField facet : facets) {
// 获取分组字段FacetField
System.out.println(facet.getName());
System.out.println("----------------");
List<FacetField.Count> counts = facet.getValues();
for (FacetField.Count count : counts) {
System.out.println(count.getName() + ":" + count.getCount());
}
System.out.println();
}
} catch (SolrServerException e) {
e.printStackTrace();
}
return null;
}
group搜索
/**
*
* 搜索功能优化-关键词搜索
* 搜索范围:商品介绍、店铺介绍、服务地址
*
* @param keywords
* @param skip
* @param size
* @return
*/
@Override
public List<KeywordSearchByOther> searchByOther(String keywords, Integer skip, Integer size) {
solrServer = SolrContext.getServer();
SolrQuery query = new SolrQuery();// 建立一个新的查询
query.setQuery(keywords);
query.setParam(GroupParams.GROUP,true);//是否分组
query.setParam(GroupParams.GROUP_FIELD,"v1_teacherId");//分组的域
query.setParam(GroupParams.GROUP_LIMIT,"100");//每组显示的个数,默认为1
query.setStart(skip); //起始索引值
query.setRows(size);//显示几条数据
QueryResponse response = null;
try {
response = solrServer.query(query, SolrRequest.METHOD.POST);
Map<String, Integer> info = new HashMap<String, Integer>();
GroupResponse groupResponse = response.getGroupResponse();
if(groupResponse != null) {
List<GroupCommand> groupList = groupResponse.getValues();
for(GroupCommand groupCommand : groupList) {
List<Group> groups = groupCommand.getValues();
for(Group group : groups) {
info.put(group.getGroupValue(), (int)group.getResult().getNumFound());
System.out.println(group.getGroupValue()+(int)group.getResult().getNumFound());
}
}
}
}catch (SolrServerException e) {
e.printStackTrace();
}
return null;
}
solr的group搜索
按组查询的字段不能是int类型,必须是string类型,不然会包空指针异常,并且必须有q元素的查询,直接group.query不行
solr查询非法字符处理
非法字符串有这些
&& || ! ( ) { } [ ] ^ " ~ * ? : \ /
过滤的方法很简单,用 \ 进行转义
solr 官方的处理方法
https://svn.apache.org/repos/asf/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/util/ClientUtils.java
public static String escapeQueryChars(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// These characters are part of the query syntax and must be escaped
if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':'
|| c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'
|| c == '*' || c == '?' || c == '|' || c == '&' || c == ';' || c == '/'
|| Character.isWhitespace(c)) {
sb.append('\\');
}
sb.append(c);
}
return sb.toString();
}
solr的facet搜索
facet的字段string获取名称乱码,int类型不乱吗
solr搜索注意事项
v1_modle:类型为int,下面搜索能搜索出数据
<str name="q">普通 && v1_modle:1 && v1_fullpath:运动</str>
v1_modle:类型为string,下面搜索不能搜索出数据
<str name="q">普通 && v1_modle:1 && v1_fullpath:运动</str>
solr按分类搜索结果集分页解决方案:
// 服务
if (count.getName().equals("1")) {
// 按14个类目搜索返回条数
long[] array = categroySearch(1, keywords);
if (null != array && array.length > 0) {
long totalnum = 0;
for (int i = 0; i < array.length; i++) {
totalnum = totalnum + array[i];
if (array[i] > 0) {
if (skip == 0) {
// 判断size
if (totalnum >= size) {
// 获取下标i前的所有分类数据
// 按分类、关键词、group老师Id分组查询
System.out.print("skip = 0 ; break"+"===下标:"+i);
break;
} else {
continue;
}
}
if (skip > 0) {
// 判断skip、size
if ((totalnum - skip) > 0) {
if ((totalnum - skip) >= size) {
// 获取下标i前的所有分类数据
// 按分类、关键词、group老师Id分组查询
System.out.print("skip > 0 ; break"+"===下标:"+i);
break;
} else {
continue;
}
}
}
}
}
if(totalnum == 0 || totalnum < skip){
System.out.print("null");
}else {
System.out.print("all");
}
}
}
solr 高亮显示
要用response.getHighlighting()接受转换高亮结果
/**
* solrJ搜索 高亮显示
*
* @author pudongping
*
* @param server
* solr客户端
* @param queryString
* 查询串
* @param pageNum
* 分页 页码
* @param pageSize
* 每页显示大小
* @return
*/
public static Page<QzoneArticle> queryComHighlight(SolrServer server, String queryString, int pageNum,int pageSize){
SolrQuery query = new SolrQuery();
query.setQuery(queryString);
query.setHighlight(true);//开启高亮功能
query.addHighlightField("description");//高亮字段
query.addHighlightField("keywords");
query.setHighlightSimplePre("<font color='red'>");//渲染标签
query.setHighlightSimplePost("</font>");//渲染标签
query.setStart((pageNum-1)*pageSize);
query.setRows(pageSize);
QueryResponse response = null;
try {
response = server.query(query);
} catch (SolrServerException e) {
e.printStackTrace();
return null;
}
//查询结果集
SolrDocumentList lists = response.getResults();
//对象结果集
List<QzoneArticle> items = new ArrayList<QzoneArticle>();
//查询到的记录总数
long totalRow = Long.valueOf(response.getResults().getNumFound()).intValue();
String tmpId = "";
Map<String,Map<String,List<String>>> highlightMap=response.getHighlighting();
for (SolrDocument solrDocument : lists) {
QzoneArticle at = new QzoneArticle();
tmpId=solrDocument.getFieldValue("id").toString();
at.setId(tmpId);
at.setAuthor(solrDocument.getFieldValue("author").toString());
List<String> descList=highlightMap.get(tmpId).get("description");
List<String> keywsList=highlightMap.get(tmpId).get("keywords");
if(descList!=null && descList.size()>0){
at.setDescription(descList.get(0));
}else{
//获取并设置高亮的字段title
at.setDescription(solrDocument.getFieldValue("description").toString());
}
if(keywsList!=null && keywsList.size()>0){
at.setKeywords(keywsList.get(0));
}else{
at.setKeywords(solrDocument.getFieldValue("keywords").toString());
}
items.add(at);
}
//填充page对象
return new Page<QzoneArticle>(pageNum, pageSize, totalRow, items);
}
solr高亮,文本太多,想截取关键词前后多少字的一段文本
hl.fl
hl.fl
是说明你要关键字的摘要在那个field中取,我们一般是content字段。 hl.useFastVectorHighlighter
该参数很重要,如果你不看代码,是很难发现他的好处,默认是false,即文本段的划分是按每50个字符来划分,然后在这个50个字符中取关键字相关的摘要,摘要长度为100,参考后面的参数(hf.fragsize),如果我们在参数中指定值为true,那么SOLR会根据关键词的在文本中的偏移量来计算摘要信息,前提是你的field要加上 termPositions="true" termOffsets="true"
这两项。
hl.snippets
hl.snippets
参数是返回高亮摘要的段数,因为我们的文本一般都比较长,含有搜索关键字的地方有多处,如果hl.snippets
的值大于1的话,会返回多个摘要信息,即文本中含有关键字的几段话,默认值为1,返回含关键字最多的一段描述。solr会对多个段进行排序。
hl.fragsize
hl.fragsize
参数是摘要信息的长度。默认值是100,这个长度是出现关键字的位置向前移6个字符,再往后100个字符,取这一段文本。
hl.boundaryScanner、hl.bs.maxScan、hl.bs.chars
boundaryScanner
是边界扫描,就是怎么取我们高亮摘要信息的起始位置和结束位置,这个是我们摘要信息的关键,因为我们模式高亮摘要的开始和结束可能是某句话中截段的。上面这三个参数需要放在一起来说明,因为后两个是在我们没有给hl.boundaryScanner
设定值,即默认值时才会有效,对应的class为:SimpleBoundaryScanner,上面讲了hl.fragsize
参数,
SimpleBoundaryScanner
会根据hl.fragsize
参数决定的关键字的起始偏移量和结束偏移量,重新计算摘要的起始偏移量,首先说开始偏移量,源码如下:
public int findStartOffset(StringBuilder buffer, int start) {
// avoid illegal start offset
if( start > buffer.length() || start < 1 ) return start;
//maxScan是hl.bs.maxScan的值,它是说明从关键字出现的位置往前6个字符开始向前,在maxScan个字符内找是否出 现一个
//一个由参数hl.bs.chars指定的分界符。即从这里作为摘要的起始偏移。 如果往前maxScan个字符内没有发现指定的字符,
//则按起始便宜为start,即关键词往前的6个字符。
int offset, count = maxScan;
for( offset = start; offset > 0 && count > 0; count-- ){
// found?
if( boundaryChars.contains( buffer.charAt( offset - 1 ) ) ) return offset;
offset--;
}
// if we scanned up to the start of the text, return it, its a "boundary"
if (offset == 0) {
return 0;
}
// not found
return start;
}
结束便宜量和计算起始偏移量是一样的,只不过是从关键词的位置往后100个字符的位置往后找分隔符,maxScan的默认值为10,hl.sc.chars
的默认值为.,!?
hl.boundaryScanner
参数我们不指定值时,按上面的算法计算高亮摘要信息,但solr还提供了一种算法,即breakIterator,我们在请求参数中添加&hl.boundaryScanner=breakIterator时生效,这是solr通过java jdk的BreakIterator来计算分界符的。他相关的参数为,hl.bs.type,这个是主要的参数,决定BreakIterator怎么划分界定符,值有:CHARACTER, WORD, SENTENCE and LINE,SENTENCE 是按句子来划分,即你高亮摘要信息是一个完整的句子,而不会被截断。
solr高亮的配置参数说明
参数详细说明:
hl.fl
: 用空格或逗号隔开的字段列表。要启用某个字段的highlight功能,就得保证该字段在schema中是stored。如果该参数未被给出,那么就会高亮默认字段 standard handler会用df参数,dismax字段用qf参数。你可以使用星号去方便的高亮所有字段。如果你使用了通配符,那么要考虑启用hl.requiredFieldMatch选项。
hl.requireFieldMatch
:如果置为true,除非该字段的查询结果不为空才会被高亮。它的默认值是false,意味着它可能匹配某个字段却高亮一个不同的字段。如果hl.fl使用了通配符,那么就要启用该参数。尽管如此,如果你的查询是all字段(可能是使用copy-field 指令),那么还是把它设为false,这样搜索结果能表明哪个字段的查询文本未被找到hl.usePhraseHighlighter:如果一个查询中含有短语(引号框起来的)那么会保证一定要完全匹配短语的才会被高亮。hl.highlightMultiTerm如果使用通配符和模糊搜索,那么会确保与通配符匹配的term会高亮。默认为false,同时hl.usePhraseHighlighter要为true。
hl.snippets
:这是highlighted片段的最大数。默认值为1,也几乎不会修改。如果某个特定的字段的该值被置为0(如f.allText.hl.snippets=0),这就表明该字段被禁用高亮了。你可能在hl.fl=*时会这么用。hl.fragsize:每个snippet返回的最大字符数。默认是100.如果为0,那么该字段不会被fragmented且整个字段的值会被返回。大字段时不会这么做。
hl.mergeContiguous
:如果被置为true,当snippet重叠时会merge起来。
hl.maxAnalyzedChars
:会搜索高亮的最大字符,默认值为51200,如果你想禁用,设为-1
hl.alternateField
:如果没有生成snippet(没有terms 匹配),那么使用另一个字段值作为返回。
hl.maxAlternateFieldLength
:如果hl.alternateField启用,则有时需要制定alternateField的最大字符长度,默认0是即没有限制。所以合理的值是应该为hl.snippets * hl.fragsize这样返回结果的大小就能保持一致。
hl.formatter
:一个提供可替换的formatting算法的扩展点。默认值是simple,这是目前仅有的选项。显然这不够用,你可以看看org.apache.solr.highlight.HtmlFormatter.java 和 solrconfig.xml中highlighting元素是如何配置的。注意在不论原文中被高亮了什么值的情况下,如预先已存在的em tags,也不会被转义,所以在有时会导致假的高亮。
hl.fragmenter
:这个是solr制定fragment算法的扩展点。gap是默认值。regex是另一种选项,这种选项指明highlight的边界由一个正则表达式确定。这是一种非典型的高级选项。为了知道默认设置和fragmenters (and formatters)是如何配置的,可以看看solrconfig.xml中的highlight段。regex 的fragmenter有如下选项:
hl.regex.pattern
:正则表达式的pattern
hl.regex.slop
:这是hl.fragsize能变化以适应正则表达式的因子。默认值是0.6,意思是如果hl.fragsize=100那么fragment的大小会从40-160.
solr搜索只关键词高亮,分类关键词不高亮实现
关键词在高亮的前面先setQuery(“keywords”),然后在高亮封装完的后面query.setFilterQueries("过滤的分类词")
public Map<String,List<PlayService>> searchPlayCategory(String keywords, Integer cursor, Integer skip, Integer size) {
Map<String,List<PlayService>> mapplays = new HashMap<String,List<PlayService>>();
List<String> strlist = getCategoryNameBycursorPlay(cursor);
if (null != strlist && strlist.size() > 0) {
for (int j = 0; j < strlist.size(); j++) {
List<PlayService> plays = new ArrayList<>();
StringBuffer sb = new StringBuffer(128);
sb.append("'\"" + keywords + "\"' && v1_modle:2 ");
SolrQuery query = new SolrQuery();// 建立一个新的查询
query.setQuery(sb.toString());
query.setHighlight(true).setHighlightSimplePre("<span style='color:#FA022C;'>")
.setHighlightSimplePost("</span>");
query.addHighlightField("v1_serviceTitle");//高亮字段
query.setParam("hl.fragsize","15");
query.setParam("hl.usePhraseHighlighter","true");
query.setFilterQueries(" v1_fullpath:" + strlist.get(j));
QueryResponse response = null;
try {
response = SolrContext.getServer().query(query, SolrRequest.METHOD.POST);
Map<String, Map<String, List<String>>> hightlight = response.getHighlighting();
plays = response.getBeans(PlayService.class);
for (int i = 0; i < plays.size(); i++) {
//hightlight的键为Item的id,值唯一,我们设置的高亮字段为title
if (null != hightlight.get(plays.get(i).getServiceSkuId()).get("v1_serviceTitle")) {
String hlString = hightlight.get(plays.get(i).getServiceSkuId()).get("v1_serviceTitle").toString();
if (null != hlString) {
plays.get(i).setServiceName(hlString.replaceAll("[\\[\\]]",""));
}
}
}
} catch (SolrServerException e) {
e.printStackTrace();
}
if (null != plays && plays.size() > 0) {
mapplays.put(strlist.get(j), plays);
}
}
}
return mapplays;
}