项目中需要实时的返回一下统计的东西,因此就要进行分组,在获取一些东西,代码拿不出来,因此分享一篇,还是很使用的。
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; }
链接:https://www.jianshu.com/p/41364bf865fc