Solr Facet的应用

来源:互联网 发布:图片加音乐制作软件 编辑:程序博客网 时间:2024/06/05 19:26

Facet是Solr的高级搜索功能之一,Solr作者给出的定义是导航(Guided Navigation)、参数化查询(Paramatic Search)。Facet的主要好处是在搜索的同时,可以按照Facet条件进行分组统计,给出导航信息,改善搜索体验。Facet搜索主要分为以下几类:

1. Field Facet
搜索结果按照Facet的字段分组并统计,Facet字段通过在请求中加入”facet.field”参数加以声明,如果需要对多个字段进行Facet查询,那么将该参数声明多次,Facet字段必须被索引。例如,以下表达式是以DEAL的status和can_buy属性为facet.field进行查询:

select?q=*:*&facet=true&facet.field=status&facet.field=can_buy&wt=json

Facet查询需要在请求参数中加入”facet=on”或者”facet=true”让Facet组件起作用,返回结果:

"facet_counts”: {      "facet_queries": {},      "facet_fields":  { "status": [ "32", 96,                                      "0", 40,                                      "8", 81,                                     "16", 50,                                    "127", 80,                                     "64", 27 ] ,                       "can_buy": [ "true", 236,                                     "false", 21 ]                      },      "facet_dates": {},      "facet_ranges": {}  }

分组count信息包含在“facet_fields”中,分别按照"status"和“can_buy”的值分组,比如状态为32的DEAL数目有96个,能购买的DEAL数目(can_buy=true)是236。

Field Facet主要参数:

 facet.field:Facet的字段 facet.prefix:Facet字段前缀 facet.limit:Facet字段返回条数 facet.offset:开始条数,偏移量,它与facet.limit配合使用可以达到分页的效果 facet.mincount:Facet字段最小count,默认为0 facet.missing:如果为on或true,那么将统计那些Facet字段值为null的记录 facet.method:取值为enum或fc,默认为fc,fc表示Field Cache facet.enum.cache.minDf:当facet.method=enum时,参数起作用,文档内出现某个关键字的最少次数

2. Date Facet
日期类型的字段在索引中很常见,如DEAL上线时间,线下时间等,某些情况下需要针对这些字段进行Facet。时间字段的取值有无限性,用户往往关心的不是某个时间点而是某个时间段内的查询统计结果,Solr为日期字段提供了更为方便的查询统计方式。字段的类型必须是DateField(或其子类型)。需要注意的是,使用Date Facet时,字段名、起始时间、结束时间、时间间隔这4个参数都必须提供。
与Field Facet类似,Date Facet也可以对多个字段进行Facet。并且针对每个字段都可以单独设置参数。

简单实例参考:

&facet.date=birthday&facet.date.start=2014-01-00T09:15:00Z&facet.date.end=2014-12-00T09:15:00Z&facet.date.gap=%2B1MONTH
 返回结果如下所示:

"facet_counts":{    "facet_queries":{},    "facet_fields":{},    "facet_dates":{      "birthday":{        "2013-12-31T09:15:00Z":0,        "2014-01-31T09:15:00Z":0,        "2014-02-28T09:15:00Z":0,        "2014-03-28T09:15:00Z":0,        "2014-04-28T09:15:00Z":0,        "2014-05-28T09:15:00Z":0,        "2014-06-28T09:15:00Z":0,        "2014-07-28T09:15:00Z":0,        "2014-08-28T09:15:00Z":0,        "2014-09-28T09:15:00Z":1,        "2014-10-28T09:15:00Z":5,        "2014-11-28T09:15:00Z":3,        "gap":"+1MONTH",        "start":"2013-12-31T09:15:00Z",        "end":"2014-12-28T09:15:00Z"}},    "facet_ranges":{}}}
Date Facet参数说明:

1).facet.date该参数表示需要进行Date Facet的字段名,与facet.field一样,该参数可以被设置多次,表示对多个字段进行Date Facet.2).facet.date.start起始时间,时间格式为1995-12-31T23:59:59Z3).facet.date.end结束时间.4).facet.date.gap时间间隔.如果start为2009-1-1,end为2010-1-1.gap设置为+1MONTH表示间隔1个月,那么将会把这段时间划分为12个间隔段.        注意+因为是特殊字符所以应该用%2B代替.5).facet.date.hardend取值可以为true|false,默认为false.它表示gap迭代到end处采用何种处理.举例说明start为2009-1-1,end为2009-12-25,gap为+1MONTH,hardend为false的话最后一个时间段为2009-12-1至2010-1-1;hardend为true的话最后一个时间段为2009-12-1至2009-12-25.6).facet.date.other<pre name="code" class="plain">&facet.date=birthday&facet.date.start=2014-01-00T09:15:00Z&facet.date.end=2014-12-00T09:15:00Z&facet.date.gap=%2B1MONTH&facet.date.other=all&f.birthday.facet.mincount=3 --单独对某个字段起作用,把统计值小于3的过滤掉

取值范围为before|after|between|none|all,默认为none,before会对start之前的值做统计,after会对end之后的值做统计,between会对start至end之间所有值做统计.如果hardend为true的话,那么该值就是各个时间段统计值的和.none表示该项禁用.all表示before,after,all都会统计.

实例参考,演示fact.date.other、跟单独对某个字段起作用
&facet.date=birthday&facet.date.start=2014-01-00T09:15:00Z&facet.date.end=2014-12-00T09:15:00Z&facet.date.gap=%2B1MONTH&facet.date.other=all&f.birthday.facet.mincount=3 --单独对某个字段起作用,把统计值小于3的过滤掉
返回结果如下:
"facet_counts":{    "facet_queries":{},    "facet_fields":{},    "facet_dates":{      "birthday":{        "2014-10-28T09:15:00Z":5,        "2014-11-28T09:15:00Z":3,        "gap":"+1MONTH",        "start":"2013-12-31T09:15:00Z",        "end":"2014-12-28T09:15:00Z",        "before":0,        "after":0,        "between":9}},    "facet_ranges":{}}}


3、Facet Range

范围统计分组统计,跟Date Facet一样,只是他们定位的字段的类型不同,Data Fact是做日期的分组统计的,而Fact Range是做数字分组统计的,在次强调,是做数字分组统计的,对于字符串,日期是不可以的。

参数跟上面的Date Facet基本一致,如下,就不做解释了,参考Date Facet的各个参数

1.facet.range2.facet.range.start3.facet.range.end4.facet.range.gap5.facet.range.hardend6.facet.range.other7.facet.range.include
参考实例:

&facet.range=price&facet.range.start=1000&facet.range.end=5000&facet.range.gap=1000&f.price.facet.mincount=2--单独对某个字段起作用,把统计值小于2的过滤掉
返回结果如下:

 "facet_counts":{    "facet_queries":{},    "facet_fields":{},    "facet_dates":{},    "facet_ranges":{      "price":{        "counts":[          "1000.0",3,          "2000.0",3,          "3000.0",2],        "gap":1000.0,        "start":1000.0,        "end":5000.0}}}}


4、 Facet Query
Facet Query利用类似于filter query的语法提供了更为灵活的Facet。通过facet.query参数,可以对任意字段进行筛选。


上图中的品牌,颜色,网络,大家说,价格,热点,屏幕尺寸,系统,机身颜色,购买方式这些都是对手机进行分类的一个个维度,有些维度是手机自身所包含的属性,比如品牌,颜色,网络,系统等,而像价格,屏幕尺寸这就是区间范围了。

对于Facet域建议最好是只创建索引不进行分词不进行存储,因为Facet域的值只是用来显示给用户看的,根据域值进行统计总数,那如果你想要对品牌进行普通查询,你可能需要对品牌域进行分词且你需要在页面上显示品牌的域值,这似乎跟Facet的设计初衷自相矛盾了,其实你可以使用Solr里的copyField来解决,对于普通查询,你可以直接对品牌域使用TextField进行索引分词,而对于Facet统计,你可以使用CopyField且指定域类型为solr.StringField即不进行分词即可。

下面我们就拿上回做MySQL数据增量更新示例里使用的测试数据来做我们的Facet测试,启动你的Tomcat,打开Query界面,如图:

 勾选了Facet即表示开启了Facet高级查询功能,不过遗憾的是,Solr Web UI的Query表单查询界面里支持的Facet配置参数太少了,就3个,所以通过这个UI界面进行Facet测试会受到限制,所以我建议大家还是通过直接在浏览器里输入请求URL来进行测试吧,只要保证你的Tomcat是处于正常启动状态即可。把Facet勾选后,在facet.field栏里填写你要对哪个域创建一个维度,如图:

 查询结果如图:

 实际请求URL为:

 

http://localhost:8080/solr/core2/select?wt=json&indent=true&q=*:*&facet=true&facet.field=brand


Facet.pivot -  Computes a Matrix of Constraint Counts across multiple Facet Fields. by Yonik Seeley.

facet.pivot自己的理解,就是按照多个维度进行分组查询

url : http://localhost:8080/solr/core2/select?wt=json&indent=true&q=*:*&facet=true&facet.pivot=brand,color

返回结果如下:

 url: http://localhost:8080/solr/core2/select?wt=json&indent=true&q=*:*&facet=true&facet.pivot={!key=bc}brand,color

给brand,color起个bc别名,我只是为了演示下key取别名也可以作用在facet.pivot参数上,效果图如下:


以下是代码实现:

QueryParser.java

query.setFacet(true);query.addFacetField("provinceAlias","cityAlias","scenics_ws","categoryName","fthemes_ws");query.setFacetMinCount(1);

query:

List<FacetField> ffList = response.getFacetFields();    ResultFacet struct = ResultFacet.parse(ffList);if (struct.getTags().size() > 0){    struct.getTags().get(0).setCount(data.getTotal());}

ResultFacet.java

private List<Pair<String, Long>> province;private List<Pair<String, Long>> city;private List<Pair<String, Long>> properties;private List<Pair<String, Long>> scenics;private List<Pair<String, Long>> tags;
public static ResultFacet parse(List<FacetField> list){ResultFacet f=new ResultFacet();for(FacetField ff:list){switch(ff.getName()){case "provinceAlias": f.setProvince(toVoList(ff.getValues()));break;case "cityAlias":f.setCity(toVoList(ff.getValues()));break;case "categoryName":List<Pair<String,Long>> tags=new ArrayList<>();tags.add(new Pair<String, Long>("全部",1L));List<Pair<String, Long>>  qtags=toVoList(ff.getValues());tags.addAll(qtags);f.setTags(tags);break;case "scenics_ws":f.setScenics(toVoList(ff.getValues()));break;case "fthemes_ws":f.setProperties(toVoList(ff.getValues()));break;}}return f;}
private static List<Pair<String, Long>> toVoList(List<Count> values){List<Pair<String, Long>> list=new ArrayList<>();Pair<String, Long> p=null;for(Count c:values){p=new Pair<String, Long>(c.getName(),c.getCount());list.add(p);}return list;}

Pair<K,V>:

private K name;private V count;public Pair(K name, V count){super();this.name = name;this.count = count;}

查询效果如下图:




参考文章:

solr中facet及facet.pivot理解  http://blog.csdn.net/a925907195/article/details/42241265

跟益达学Solr5之Facet一瞥  http://iamyida.iteye.com/blog/2217148

Solr Facet技术的应用与研究  http://tech.meituan.com/solr-facet.html

facet   http://www.cnblogs.com/HD/p/3998321.html

Solr Facet查询: http://eksliang.iteye.com/blog/2165882



0 0
原创粉丝点击