solrj的facet查询总结
- 格式:docx
- 大小:72.71 KB
- 文档页数:11
SOLR FACET学习笔记修改记录目录一.FACET简介 (3)二.FACET字段 (3)1.适宜被F ACET的字段 (3)2.F ACET字段的要求 (3)3.特殊情况 (3)三.FACET组件 (4)四.FACET查询 (4)1.F IELD F ACET (4)1.1facet.prefix (7)1.2facet.sort (7)1.3facet.limit (7)1.4facet.offset (8)1.5facet.mincount (8)1.6facet.missing (8)1.7facet.method (8)1.8facet.enum.cache.minDf (8)2.D ATE F ACET (8)2.1facet.date (8)2.2facet.date.start (8)2.3facet.date.end (8)2.4facet.date.gap (8)2.5facet.date.hardend (8)2.6facet.date.other (9)3.F ACET Q UERY (10)4.KEY操作符 (12)5.TAG操作符和EX操作符 (14)五.SOLRJ对FACET的支持 (17)一.Facet简介Facet是solr的高级搜索功能之一,可以给用户提供更友好的搜索体验.在搜索关键字的同时,能够按照Facet的字段进行分组并统计.二.Facet字段1.适宜被Facet的字段一般代表了实体的某种公共属性,如商品的分类,商品的制造厂家,书籍的出版商等等.2.Facet字段的要求Facet的字段必须被索引.一般来说该字段无需分词,无需存储.无需分词是因为该字段的值代表了一个整体概念,如电脑的品牌”联想”代表了一个整体概念,如果拆成”联”,”想”两个字都不具有实际意义.另外该字段的值无需进行大小写转换等处理,保持其原貌即可.无需存储是因为一般而言用户所关心的并不是该字段的具体值,而是作为对查询结果进行分组的一种手段,用户一般会沿着这个分组进一步深入搜索.3.特殊情况对于一般查询而言,分词和存储都是必要的.比如CPU类型”Intel 酷睿2双核P7570”,拆分成”Intel”,”酷睿”,”P7570”这样一些关键字并分别索引,可能提供更好的搜索体验.但是如果将CPU作为Facet字段,最好不进行分词.这样就造成了矛盾,解决方法为,将CPU字段设置为不分词不存储,然后建立另外一个字段为它的COPY,对这个COPY的字段进行分词和存储.schema.xml三.Facet组件Solr的默认requestHandler(ponent.SearchHandler)已经包含了Facet组件(ponent.FacetComponent).如果自定义requestHandler或者对默认的requestHandler自定义组件列表,那么需要将Facet加入到组件列表中去.solrconfig.xml四.Facet查询进行Facet查询需要在请求参数中加入”facet=on”或者”facet=true”只有这样Facet组件才起作用.1.Field FacetFacet字段通过在请求中加入”facet.field”参数加以声明,如果需要对多个字段进行Facet查询,那么将该参数声明多次.比如返回结果:各个Facet字段互不影响,且可以针对每个Facet字段设置查询参数.以下介绍的参数既可以应用于所有的Facet字段,也可以应用于每个单独的Facet字段.应用于单独的字段时通过这种方式调用.比如facet.prefix参数应用于cpu字段,可以采用如下形式1.1facet.prefix表示Facet字段值的前缀.比如”facet.field=cpu&facet.prefix=Intel”,那么对cpu字段进行Facet查询,返回的cpu都是以”Intel”开头的,”AMD”开头的cpu型号将不会被统计在内.1.2facet.sort表示Facet字段值以哪种顺序返回.可接受的值为true(count)|false(index,lex).true(count)表示按照count值从大到小排列. false(index,lex)表示按照字段值的自然顺序(字母,数字的顺序)排列.默认情况下为true(count).当facet.limit值为负数时,默认facet.sort= false(index,lex).1.3facet.limit限制Facet字段返回的结果条数.默认值为100.如果此值为负数,表示不限制.1.4facet.offset返回结果集的偏移量,默认为0.它与facet.limit配合使用可以达到分页的效果.1.5facet.mincount限制了Facet字段值的最小count,默认为0.合理设置该参数可以将用户的关注点集中在少数比较热门的领域.1.6facet.missing默认为””,如果设置为true或者on,那么将统计那些该Facet字段值为null的记录.1.7facet.method取值为enum或fc,默认为fc.该字段表示了两种Facet的算法,与执行效率相关.enum适用于字段值比较少的情况,比如字段类型为布尔型,或者字段表示中国的所有省份.Solr会遍历该字段的所有取值,并从filterCache里为每个值分配一个filter(这里要求solrconfig.xml里对filterCache的设置足够大).然后计算每个filter 与主查询的交集.fc(表示Field Cache)适用于字段取值比较多,但在每个文档里出现次数比较少的情况.Solr会遍历所有的文档,在每个文档内搜索Cache内的值,如果找到就将Cache 内该值的count加1.1.8facet.enum.cache.minDf当facet.method=enum时,此参数其作用,minDf表示minimum document frequency.也就是文档内出现某个关键字的最少次数.该参数默认值为0.设置该参数可以减少filterCache的内存消耗,但会增加总的查询时间(计算交集的时间增加了).如果设置该值的话,官方文档建议优先尝试25-50内的值.2.Date Facet日期类型的字段在文档中很常见,如商品上市时间,货物出仓时间,书籍上架时间等等.某些情况下需要针对这些字段进行Facet.不过时间字段的取值有无限性,用户往往关心的不是某个时间点而是某个时间段内的查询统计结果. Solr为日期字段提供了更为方便的查询统计方式.当然,字段的类型必须是DateField(或其子类型).需要注意的是,使用Date Facet时,字段名,起始时间,结束时间,时间间隔这4个参数都必须提供.与Field Facet类似,Date Facet也可以对多个字段进行Facet.并且针对每个字段都可以单独设置参数.2.1facet.date该参数表示需要进行Date Facet的字段名,与facet.field一样,该参数可以被设置多次,表示对多个字段进行Date Facet.2.2facet.date.start起始时间,时间的一般格式为”1995-12-31T23:59:59Z”,另外可以使用”NOW”,”YEAR”,”MONTH”等等,具体格式可以参考org.apache.solr.schema.DateField的java doc.2.3facet.date.end结束时间.2.4facet.date.gap时间间隔.如果start为2009-1-1,end为2010-1-1.gap设置为”+1MONTH”表示间隔1个月,那么将会把这段时间划分为12个间隔段.注意”+”因为是特殊字符所以应该用”%2B”代替.2.5facet.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.2.6facet.date.other取值范围为before|after|between|none|all,默认为none.before会对start之前的值做统计.after会对end之后的值做统计.between会对start至end之间所有值做统计.如果hardend为true的话,那么该值就是各个时间段统计值的和.none表示该项禁用.all表示before,after,all都会统计.举例:返回结果:3.Facet QueryFacet Query利用类似于filter query的语法提供了更为灵活的Facet.通过facet.query 参数,可以对任意字段进行筛选.例1:返回结果:例2:返回结果:例3:返回结果:4.key操作符可以用key操作符为Facet字段取一个别名.例:返回结果:5.tag操作符和ex操作符当查询使用filter query的时候,如果filter query的字段正好是Facet字段,那么查询结果往往被限制在某一个值内.例:返回结果:可以看到,屏幕尺寸(screenSize)为14寸的产品共有107件,其它尺寸的产品的数目都是0,这是因为在filter里已经限制了screenSize:14.这样,查询结果中,除了screenSize=14的这一项之外,其它项目没有实际的意义.有些时候,用户希望把结果限制在某一范围内,又希望查看该范围外的概况.比如上述情况,既要把查询结果限制在14寸屏的笔记本,又想查看一下其它屏幕尺寸的笔记本有多少产品.这个时候需要用到tag和ex操作符.tag就是把一个filter标记起来,ex(exclude)是在Facet的时候把标记过的filter排除在外.例:返回结果:这样其它屏幕尺寸的统计信息就有意义了.五.SolrJ对Facet的支持。
solrj的使用solrj是一个java客户端访问solr,它定义了一个java接口来添加、修改、查询solr的索引。
本页描述的solrj使用Solr释放包括1.4 x版本,solrj/solr兼容性solrj一般保持向后兼容性,你可以使用一个新的solrj调用旧的solr,或一个老solrj调用新的solr。
这里有一些小的例外情况:如果你把1。
x和以后的版本,你必须设置响应解析器,XML,因为这两个版本使用javabin不兼容的版本。
server.setParser(new XMLResponseParser());混合3。
x 和4。
x或之后的版本工作正常,只要你没有改变请求writer二进制。
这是关于改变默认请求writer二进制solr-3038讨论。
如果这样做了,然后之间的兼容性3。
x和以后的版本将需要设置请求writer明确的XML版本。
如果更改了,还有另一个讨论solr-4820加设自动交通检测。
这是一个javabin形式例外,这改变了主要版本间不兼容的方式。
你不能在1.4.1以上和3.1或更高版本之间使用javabin之间的沟通设置classpathSolrj使用有几个文件夹需要包含,/dist, /dist/solrj-lib 和 /lib,最小的一组jar (你会发现这取决于你的使用场景别人需要)使用solrj如下:From /dist:∙apache-solr-solrj-*.jarFrom /dist/solrj-lib∙commons-codec-1.3.jar∙commons-httpclient-3.1.jar∙commons-io-1.4.jar∙jcl-over-slf4j-1.5.5.jar∙slf4j-api-1.5.5.jarFrom /lib∙slf4j-jdk14-1.5.5.jarMavenSolrj支持使用Maven repository,使用下面的文件添加到你的pom.xml,使用solrj<dependency><artifactId>solr-solrj</artifactId><groupId>org.apache.solr</groupId><version>1.4.0</version><type>jar</type><scope>compile</scope></dependency>如果你使用EmbeddedSolrServer,你也需要添加solr-core的依赖。
Solr系列六:solr搜索详解优化查询结果(分⾯搜索、搜索结果⾼亮、查询建议、折叠展开结。
⼀、分⾯搜索1. 什么是分⾯搜索?分⾯搜索:在搜索结果的基础上进⾏按指定维度的统计,以展⽰搜索结果的另⼀⾯信息。
类似于SQL语句的group by分⾯搜索的⽰例:2. Solr中⽀持的分⾯查询字段分⾯、区间分⾯、决策树分⾯、查询分⾯2.1 字段分⾯执⾏搜索时,根据查询请求返回特定分⾯字段中找到的唯⼀值以及找到的⽂档数。
通⽤查询参数:facet:true/false 对当前搜索是否启⽤分⾯facet.query:指定⼀个额外的分⾯查询语句字段分⾯查询参数:facet.field:指定对哪个字段进⾏分⾯计算。
该参数可以多次指定以返回多个字段⽅⾯。
字段需是索引字段。
facet.sort:分⾯结果的排序⽅式:count:根据统计数量排,index:索引的词典顺序facet.limit:确定每个分⾯返回多少个唯⼀分⾯值。
可取值:整数>=-1,-1表⽰不限制,默认100。
facet.offset:对分⾯值进⾏分页,指定页偏移。
>=0 默认0。
facet.prefix:指定限制字段分⾯值必须以xxx开头,⽤以筛选分⾯值。
facet.missing:true/false,是否在分⾯字段中返回所有不包含值(值为缺失)的⽂档计数。
facet.mincount:指定分⾯结果中的分⾯值的统计数量>=mincount的才返回⽰例:sort、limit、offset、prefix、missing、mincount 可根据字段指定: f.filedname.facet.sort=count2.2 区间分⾯区间分⾯将数值或时间字段值分成⼀些区间段,按区间段进⾏统计。
区间分⾯查询参数:facet.range:指定对哪个字段计算区间分⾯。
可多次⽤该参数指定多个字段。
facet.range=price&facet.range=agefacet.range.start:起始值f.price.facet.range.start=0.0&f.age.facet.range.start=10stModified_dt.facet.range.start=NOW/DAY-30DAYSfacet.range.end:结束值f.price.facet.range.end=1000.0&f.age.facet.range.start=99stModified_dt.facet.range.end=NOW/DAY+30DAYSfacet.range.gap:间隔值,创建⼦区间。
一步一步跟我学习lucene(14)---lucene搜索之facet查询原理和facet查询实例Facet说明我们在浏览网站的时候,经常会遇到按某一类条件查询的情况,这种情况尤以电商网站最多,以天猫商城为例,我们选择某一个品牌,系统会将该品牌对应的商品展示出来,效果图如下:如上图,我们关注的是品牌,选购热点等方面,对于类似的功能我们用lucene的term查询当然可以,但是在数据量特别大的情况下还用普通查询来实现显然会因为FSDirectory.open等耗时的操作造成查询效率的低下,同时普通查询是全部document都扫描一遍,这样显然造成了查询效率低;lucene提供了facet查询用于对同一类的document进行聚类化,这样在查询的时候先关注某一个方面,这种显然缩小了查询范围,进而提升了查询效率;facet模块提供了多个用于处理facet的统计和值处理的方法;要实现facet的功能,我们需要了解facetField,FacetField定义了dim和此field对应的path,需要特别注意的是我们在做facetField索引的时候,需要事先调用FacetsConfig.build(Document);FacetField的indexOptions设置为了DOCS_AND_FREQS_AND_POSITIONS的,即既索引又统计出现的频次和出现的位置,这样做主要是为了方便查询和统计;相应的在存储的时候我们需要利用FacetsConfig和DirectoryTaxonomyWriter;DirectoryTaxonomyWriter用来利用Directory来存储T axono 信息到硬盘;DirectoryTaxonomyWriter的构造器如下:[java] view plain copy1.public DirectoryTaxonomyWriter(Directory directory, Ope nMode openMode,2.TaxonomyWriterCache cache) throws IOException {3.4.dir = directory;5.IndexWriterConfig config = createIndexWriterConfig(ope nMode);6.indexWriter = openIndexWriter(dir, config);7.8.// verify (to some extent) that merge policy in effect would preserve category docids9.assert !(indexWriter.getConfig().getMergePolicy() instanc eof TieredMergePolicy) :10."for preserving category docids, merging none-adjacent segments is not allowed";11.12.// after we opened the writer, and the index is locked, it's safe to check13.// the commit data and read the index epoch14.openMode = config.getOpenMode();15.if (!DirectoryReader.indexExists(directory)) {16.indexEpoch = 1;17.} else {18.String epochStr = null;19.Map<String, String> commitData = readCommitData( directory);20.if (commitData != null) {21.epochStr = commitData.get(INDEX_EPOCH);22.}23.// no commit data, or no epoch in it means an old tax onomy, so set its epoch to 1, for lack24.// of a better value.25.indexEpoch = epochStr == null ? 1 : Long.parseLong( epochStr, 16);26.}27.28.if (openMode == OpenMode.CREATE) {29.++indexEpoch;30.}31.32.FieldType ft = new FieldType(TextField.TYPE_NOT_STO RED);33.ft.setOmitNorms(true);34.parentStreamField = new Field(Consts.FIELD_PAYLOAD S, parentStream, ft);35.fullPathField = new StringField(Consts.FULL, "", Field.S tore.YES);36.37.nextID = indexWriter.maxDoc();38.39.if (cache == null) {40.cache = defaultTaxonomyWriterCache();41.}42.this.cache = cache;43.44.if (nextID == 0) {45.cacheIsComplete = true;46.// Make sure that the taxonomy always contain the ro ot category47.// with category id 0.48.addCategory(new FacetLabel());49.} else {50.// There are some categories on the disk, which we ha ve not yet51.// read into the cache, and therefore the cache is inco mplete.52.// We choose not to read all the categories into the ca che now,53.// to avoid terrible performance when a taxonomy ind ex is opened54.// to add just a single category. We will do it later, afte r we55.// notice a few cache misses.56.cacheIsComplete = false;57.}58.}由上述代码可知,DirectoryTaxonomyWriter先打开一个IndexWriter,在确保indexWriter打开和locked的前提下,读取directory对应的segments中需要提交的内容,如果读取到的内容为空,说明是上次的内容,设置indexEpoch为1,接着对cache进行设置;判断directory中是否还包含有document,如果有设置cacheIsComplete为false,反之为true;时候不早了,今天先写到这里,明天会在此基础上补充,大家见谅编程实践我对之前的读取文件夹内容的做了个facet索引的例子对BaseIndex修改了facet的设置,相关代码如下[java] view plain copy1.package com.lucene.index;2.3.4.5.import java.io.File;6.import java.io.IOException;7.import java.nio.file.Paths;8.import java.text.ParseException;9.import java.util.List;10.import java.util.concurrent.CountDownLatch;11.12.import org.apache.lucene.document.Document;13.import org.apache.lucene.document.Field;14.import org.apache.lucene.facet.FacetResult;15.import org.apache.lucene.facet.Facets;16.import org.apache.lucene.facet.FacetsCollector;17.import org.apache.lucene.facet.FacetsConfig;18.import org.apache.lucene.facet.taxonomy.FastTaxono myFacetCounts;19.import org.apache.lucene.facet.taxonomy.directory.Dir ectoryTaxonomyReader;20.import org.apache.lucene.facet.taxonomy.directory.Dir ectoryTaxonomyWriter;21.import org.apache.lucene.index.IndexOptions;22.import org.apache.lucene.index.IndexWriter;23.import org.apache.lucene.index.Term;24.import org.apache.lucene.search.IndexSearcher;25.import org.apache.lucene.search.MatchAllDocsQuery;26.import org.apache.lucene.search.Query;27.import org.apache.lucene.store.Directory;28.import org.apache.lucene.store.FSDirectory;29.import org.apache.lucene.store.RAMDirectory;30.31.import com.lucene.search.SearchUtil;32.33.34.public abstract class BaseIndex<T> implements Run nable{35./**36.* 父级索引路径37.*/38.private String parentIndexPath;39./**40.* 索引编写器41.*/42.private IndexWriter writer;43.private int subIndex;44./**45.* 主线程46.*/47.private final CountDownLatch countDownLatch1;48./**49.*工作线程50.*/51.private final CountDownLatch countDownLatch2;52./**53.* 对象列表54.*/55.private List<T> list;56./**57.* facet查询58.*/59.private String facet;60.protected final FacetsConfig config = new FacetsConf ig();61.protected final static String indexPath = "index1";62.protected final static DirectoryTaxonomyWriter taxo Writer;63.static{64.try {65.Directory directory = FSDirectory.open(Paths.get(inde xPath, new String[0]));66.taxoWriter = new DirectoryTaxonomyWriter(directory) ;67.} catch (IOException e) {68.throw new ExceptionInInitializerError("BaseIndex initi alizing error");69.}70.}71.public BaseIndex(IndexWriter writer,CountDownLatch countDownLatch1, CountDownLatch countDownLatch2,72.List<T> list, String facet){73.super();74.this.writer = writer;75.this.countDownLatch1 = countDownLatch1;76.this.countDownLatch2 = countDownLatch2;77.this.list = list;78.this.facet = facet;79.}80.public BaseIndex(String parentIndexPath, int subIndex ,81.CountDownLatch countDownLatch1, CountDownLatch countDownLatch2,82.List<T> list) {83.super();84.this.parentIndexPath = parentIndexPath;85.this.subIndex = subIndex;86.try {87.//多目录索引创建88.File file = new File(parentIndexPath+"/index"+subInd ex);89.if(!file.exists()){90.file.mkdir();91.}92.this.writer = IndexUtil.getIndexWriter(parentIndexPath +"/index"+subIndex, true);93.} catch (IOException e) {94.// TODO Auto-generated catch block95. e.printStackTrace();96.};97.this.subIndex = subIndex;98.this.countDownLatch1 = countDownLatch1;99.this.countDownLatch2 = countDownLatch2;100.this.list = list;101.}102.public BaseIndex(String path,CountDownLatch count DownLatch1, CountDownLatch countDownLatch2,103.List<T> list) {104.super();105.try {106.//单目录索引创建107.File file = new File(path);108.if(!file.exists()){109.file.mkdir();110.}111.this.writer = IndexUtil.getIndexWriter(path,true);112.} catch (IOException e) {113.// TODO Auto-generated catch block114. e.printStackTrace();115.};116.this.countDownLatch1 = countDownLatch1;117.this.countDownLatch2 = countDownLatch2;118.this.list = list;119.}120.121./**创建索引122.* @param writer123.* @param carSource124.* @param create125.* @throws IOException126.* @throws ParseException127.*/128.public abstract void indexDoc(IndexWriter writer,T t) throws Exception;129./**批量索引创建130.* @param writer131.* @param t132.* @throws Exception133.*/134.public void indexDocs(IndexWriter writer,List<T> t) th rows Exception{135.for (T t2 : t) {136.indexDoc(writer,t2);137.}138.}139./**带group的索引创建140.* @param writer141.* @param docs142.* @throws IOException143.*/144.public void indexDocsWithGroup(IndexWriter writer,S tring groupFieldName,String groupFieldValue,List<Document> docs) throws IOException{145.Field groupEndField = new Field(groupFieldName, gro upFieldValue, Field.Store.NO, Field.Index.NOT_ANALYZED);146.docs.get(docs.size()-1).add(groupEndField);147.//148.writer.updateDocuments(new Term(groupFieldName, groupFieldValue),docs);mit();150.writer.close();151.}152.@Override153.public void run() {154.try {155.countDownLatch1.await();156.System.out.println(writer);157.indexDocs(writer,list);158.} catch (InterruptedException e) {159.// TODO Auto-generated catch block160. e.printStackTrace();161.} catch (Exception e) {162.// TODO Auto-generated catch block163. e.printStackTrace();164.}finally{165.countDownLatch2.countDown();166.try {mit();168.writer.close();169.} catch (IOException e) {170.// TODO Auto-generated catch block171. e.printStackTrace();172.}173.}174.}175.}相应得,document的索引需要利用DirectoryTaxonomyWriter 来进行原有document的处理[java] view plain copy1.package com.lucene.index;2.3.import java.util.List;4.import java.util.concurrent.CountDownLatch;5.6.import org.apache.lucene.document.Document;7.import org.apache.lucene.document.Field;8.import org.apache.lucene.document.LongField;9.import org.apache.lucene.document.StringField;10.import org.apache.lucene.document.TextField;11.import org.apache.lucene.facet.FacetField;12.import org.apache.lucene.index.IndexWriter;13.import org.apache.lucene.index.IndexWriterConfig;14.import org.apache.lucene.index.Term;15.16.import com.lucene.bean.FileBean;17.18.public class FileBeanIndex extends BaseIndex<FileBe an>{19.private static String facet;20.21.public FileBeanIndex(IndexWriter writer, CountDownL atch countDownLatch12, CountDownLatch countDownLatch1,22.List<FileBean> fileBeans, String facet1) {23.super(writer, countDownLatch12, countDownLatch1, fi leBeans, facet);24.facet = facet1;25.}26.@Override27.public void indexDoc(IndexWriter writer, FileBean t) t hrows Exception {28.Document doc = new Document();29.String path = t.getPath();30.System.out.println(t.getPath());31.doc.add(new StringField("path", path, Field.Store.YES)) ;32.doc.add(new LongField("modified", t.getModified(), Fi eld.Store.YES));33.doc.add(new TextField("content", t.getContent(), Field. Store.YES));34.doc.add(new FacetField("filePath", new String[]{facet}) );35.//doc = config.build(taxoWriter,doc);36.if (writer.getConfig().getOpenMode() == IndexWriterC onfig.OpenMode.CREATE){37.//writer.addDocument(doc);38.writer.addDocument(this.config.build(taxoWriter, doc) );39.}else{40.writer.updateDocument(new Term("path", t.getPath()), this.config.build(taxoWriter, doc));41.}mit();43.}44.45.46.}测试facet功能的测试类:[java] view plain copy1.package com.lucene.search;2.3.import java.io.IOException;4.import java.nio.file.Paths;5.import java.util.ArrayList;6.import java.util.List;7.8.import org.apache.lucene.facet.FacetResult;9.import org.apache.lucene.facet.Facets;10.import org.apache.lucene.facet.FacetsCollector;11.import org.apache.lucene.facet.FacetsConfig;12.import belAndValue;13.import org.apache.lucene.facet.taxonomy.FastTaxono myFacetCounts;14.import org.apache.lucene.facet.taxonomy.TaxonomyR eader;15.import org.apache.lucene.facet.taxonomy.directory.Dir ectoryTaxonomyReader;16.import org.apache.lucene.index.DirectoryReader;17.import org.apache.lucene.search.IndexSearcher;18.import org.apache.lucene.search.MatchAllDocsQuery;19.import org.apache.lucene.search.Query;20.import org.apache.lucene.store.Directory;21.import org.apache.lucene.store.FSDirectory;22.import org.junit.Test;23.24.public class TestSearchFacet {25.public static Directory directory;26.public static Directory taxoDirectory;27.public static TaxonomyReader taxoReader;28.protected final static FacetsConfig config = new Face tsConfig();29.static {30.try {31.directory = FSDirectory.open(Paths.get("index", new S tring[0]));32.taxoDirectory = FSDirectory.open(Paths.get("index1", new String[0]));33.taxoReader = new DirectoryTaxonomyReader(taxoDire ctory);34.} catch (IOException e) {35.// TODO Auto-generated catch block36. e.printStackTrace();37.}38.}39.40.public static void testSearchFacet() {41.try {42.DirectoryReader indexReader = DirectoryReader.open( directory);43.IndexSearcher searcher = new IndexSearcher(indexRe ader);44.FacetsCollector fc = new FacetsCollector();45.FacetsCollector.search(searcher, new MatchAllDocsQu ery(), indexReader.maxDoc(), fc);46.Facets facets = new FastTaxonomyFacetCounts(taxoRe ader, config, fc);47.List<FacetResult> results =facets.getAllDims(100);48.for (FacetResult facetResult : results) {49.System.out.println(facetResult.dim);belAndValue[] values = belValues;51.for (LabelAndValue labelAndValue : values) {52.System.out.println("\t"+bel +" "+l abelAndValue.value);53.}54.55.}56.57.indexReader.close();58.taxoReader.close();59.} catch (IOException e) {60.// TODO Auto-generated catch block61. e.printStackTrace();62.}63.}64.65.public static void main(String[] args) {66.testSearchFacet();67.}68.69.}。
q 关键字模糊查询q.op=OR连接连接符,AND OR NOThl=true开启高亮hl.fl=name,features设置高亮字段,默认用<em></em>包裹facet true/false 开启分组facet.field指定排序字段fqmanu:Belkin指定字段精确查找sort price asc按指定字段排序start 0 当前页开始记录索引编号,从0开始计算rows 10 每页显示几条flname,price, 查询返回结果集包含哪些字段,score字段是内置字段df text 查询字段类型wt xml 返回数据类型get方式请求:http://localhost:8983/solr/collection1/select?q=iPod&fq=manu%3ABelkin&sort=price+asc&fl=na me%2Cprice%2Cfeatures%2Cscore&df=text&wt=xml&start=0&rows=10返回的XML数据格式如下:<response><lst name="responseHeader"><int name="status">0</int><int name="QTime">47</int><lst name="params"><str name="df">text</str><str name="fl">name,price,features,score</str><str name="sort">price asc</str><str name="start">0</str><str name="q">iPod</str><str name="wt">xml</str><str name="fq">manu:Belkin</str><str name="rows">10</str></lst></lst><result name="response" numFound="0" start="0" maxScore="0.0"/></response>返回的JSON数据格式如下:{"responseHeader":{"status":0,"QTime":0,"params":{"df":"text","fl":"n ame,price,features,score","sort":"priceasc","start":"0","q":"iPod","wt":"json","fq":"manu:Belkin","rows":"10 "}},"response":{"numFound":0,"start":0,"maxScore":0.0,"docs":[]}}Query: title:(solr in action)^2.5 description:(solr in action)Solr在Tomcat下部署:Solr.war解压开,把E:\solr-4.7.2\example\lib\ext下的所有jar包copy到E:\apache-tomcat-7.0.47\webapps\solr\WEB-INF\lib下在E:\apache-tomcat-7.0.47\webapps\solr\WEB-INF\下新建classes目录,把E:\solr-4.7.2\example\resources目录下的log4j.properties配置文件copy到E:\apache-tomcat-7.0.47\webapps\solr\WEB-INF\classes下,修改E:\apache-tomcat-7.0.47\webapps\solr\WEB-INF下的web.xml配置文件,添加如下配置:<env-entry><env-entry-name>solr/home</env-entry-name><env-entry-value>C:\solrHome</env-entry-value><env-entry-type>ng.String</env-entry-type></env-entry>C:\solrHome是我配置的Solr主目录,把E:\solr-4.7.2\example下的示例copy到solrHome目录下,如上图:conf用来存放solr配置文件,data是solr用来存放索引文件和日志文件的lib是依赖的第三方jar包Schema.xml配置示例:下面这是ansj分词器和MMSeg分词器的配置:solrconfig.xml配置示例:可以从solr的example下的示例工程里copy一份,然后如图添加数据导入配置,数据导入是在data-config.xml中配置的data-config.xml配置示例:配置数据库的JDBC链接url驱动类数据库帐号密码什么的,一看就懂的,就不解释了,我的测试表结构截图如下:别忘记添加mysql 驱动包,如图:把solr的war包copy到tomcat的webapps目录下,如果需要使用IK分词器的自定义词典和停用词词典,还需要把IK的配置文件以及相关词典文件copy到web-inf/classes文件夹下,如图:IK分词器配置文件IKAnalyzer.cfg.xml配置如下:启动Tomcat,访问如下url进入solr后台: http://localhost:8080/solr/admin选择运行哪个solr实例,查询测试:deletedpkquery:增量删除,数据库标记isdeleted=1后,更新solr删除文档批量导入(full-import):http://localhost:8080/solr/dataimport?command=full-import&commit=true增量导入(delta-import):http://localhost:8080/solr/dataimport?command=delta-import&commit=true导入状态查询(status):http://localhost:8080/solr/dataimport重新装载配置文件(reload-config):http://localhost:8080/solr/dataimport?command=reload-config终止导入(abort):http://localhost:8080/solr/dataimport?command=abortclean=true 表示创建索引之前先删除原有索引。
facet用法-回复Facet用法—探索数据的多个方面引言:在数据分析和数据挖掘领域,我们经常需要从大量的数据中获取有关不同方面的信息。
Facet(分面)是一种研究数据的方法,它可以帮助我们以多个角度和维度深入理解数据。
本文将探讨Facet的用法,并通过实例来说明如何逐步使用Facet来回答特定的问题。
第一部分:什么是Facet?Facet是一种将数据集拆分成多个子集的方法,这些子集通常基于特定的属性或维度。
每个子集被称为一个facet。
通过对每个facet进行独立的分析,我们可以更全面地了解数据,并发现不同facet之间的关系。
第二部分:Facet的应用场景Facet可以在各种数据分析领域中应用,下面是一些常见的应用场景:1. 网站分析:通过将用户行为数据按照用户属性、访问时间等维度进行facet化,可以深入了解用户的偏好和行为模式。
2. 产品分析:通过将销售数据按类别、地区、时间等维度进行facet化,可以了解产品的受欢迎程度和销售趋势,从而做出更准确的决策。
3. 社交媒体分析:通过将用户生成内容按照话题、情绪、跟帖数量等维度进行facet化,可以洞察用户的兴趣爱好和情感倾向。
第三部分:Facet的具体用法Facet可以通过以下步骤来完成:1. 选择facet的属性或维度:根据你的具体问题,选择一个或多个合适的属性或维度作为facet的基础。
2. 对数据进行划分:根据选择的属性或维度,将数据集划分为多个子集,每个子集代表一个facet。
3. 分析每个facet:对每个facet进行独立的分析,包括统计描述、可视化等方法。
这将帮助你深入了解每个facet的特征和趋势。
4. 比较不同facet之间的关系:通过对比不同facet的分析结果,你可以发现不同facet之间的联系和相互影响。
这可以帮助你进一步挖掘数据中的有趣模式。
5. 提取结论和洞察:根据对不同facet的分析和比较,你可以得出结论和洞察,为问题的解决提供指导。
Solr基础理论【排名检索、查准率、查全率】⼀.排名检索 搜索引擎代表了基于查询,返回优先⽂档的⼀种⽅法。
在关系型数据库的SQL查询中,表的⼀⾏要么匹配⼀个查询,要么不匹配,查询结果基于⼀列或多列排序。
搜索引擎根据⽂档与查询匹配的程度为⽂档打分,并按降序返回结果。
匹配程度的计算取决于多个因素,⼀般⽽⾔,⽂档得分越⾼意味着该⽂档与查询的相关性越强。
在Solr中会存在单独的字段保存相关性,那就是score字段。
在score字段的得分数值并⾮具有绝对意义,它在lucene内部仅作为相对排名,⽽且同⼀⽂档在不同的查询中的得分并⽆关系。
也就是说,与查询匹配的⽂档相关度得分仅适⽤于该查询。
返回的结果按照得分从低到⾼排序,⽂档得分越⾼。
说明与该查询越相关。
优点: 1.提供清晰的导航⽅式,把⽤户越需要的放在越靠前的位置。
2.智能化程度⾼,⽤户⽆需考虑排序问题。
⼆.查准率 查准率【Precision,精度性的度量】的具体定义如下:正确匹配的⽂档数量/返回的⽂档数量【介于0.0~1.0之间】,例如: 假设某次搜索返回的⽂档集合为⽂档1、2、3,那么该查询的查准率为1.0【3个正确匹配/3个全部匹配】,这是理想状态。
但是,如果返回全部6个结果,那么查准率仅为0.5,因为返回的结果中有⼀半是不相关的。
同样地,如果只返回⼀个相关⽂档【⽐如⽂档1】,那么查准率依然是1.0,因为返回的结果都是匹配的。
可见,查准率是衡量结果与查询是否相关的⼀个指标,但是它并不关注全⾯性,即使只返回了100个匹配⽂档中的1个,查准率依然为1.0【返回的⽂档中没有不匹配⽂档】。
由于查准率仅考虑了返回结果的整体精确性,⽽未考虑结果集的全⾯性,因此需要将查全率与查准率配合使⽤。
三.查全率 查全率【Recall,全⾯性的度量,也称召回率】衡量的是搜索结果的全⾯性。
定义如下:返回的正确匹配的⽂档数/全部正确匹配的⽂档数。
如果仅返回⼀个匹配⽂档,则查全率为1/3,因为没有找到其它两个应该匹配的⽂档【此时查准率为1.0】。
solr模糊查询今天重点讲模糊检索中文名字solr对中文进行分词,如果不是一个词,可能会丢失字,然后在查询名字的,会感觉不太准确。
solr里面提供了很多查询匹配的方法例如:1.使用*:resume_keyword:*王*2.使用AND 拼接查询 keywords:*建筑* AND personName:*王*3.OR keyword:建筑 OR personName:王4. 否: AND resume_keyword:(* NOT *李*) 上面的都是用拼接的,也可以用过滤query.addFilterQuery(" per_name:(*李* )");5.名字匹配法:之前做搜索的时候,名字设置成了分词,然后分词结果不太准确,比如:ABC,它可能分成了AB 然后没有C,所以你在查询的时候,就查不到ABC。
之前的分词是IK<fieldType name="text_ik" class="solr.TextField"><analyzer type="index" class="org.wltea.analyzer.lucene.IK Analyzer"/><analyzer type="query" class="org.wltea.analyzer.lucene.IK Analyzer"/></fieldType>出现了上述问题,然后我们研究了一下schema.xml里面的属性类型,之前用text_ik,改成了text_general,这样它就可以把ABC 分成了A,B,C 通过一个其中的一个字或者ABC都可以检索了,就不用上面的模糊匹配了,就算匹配了,也不太准,可能把自己需要的那一行数据放到了第七八行,而不是第一行。
solr的facet层面查询Solr FacetComponent是实现对词Term的层面统计。
FacetComponent给客户端返回四种类型的结果,分别是:facet_queries,facet_fields,facet_dates,facet_ranges,我们用的最多是facet_fields,即对field的词出现的个数的一个统计。
solr实现的原理因为我们在用到FacetComponent时,查询请求的url里定要加上facet=true. FacetComponet是发挥作用前,QueryComponent会先处理q参数里的查询,查询的结果的DocID保存在DocSet里,这里是一个无序的document ID 的集合。
QueryComponent处理完后,把docSet封装到SimpleFacets里,在FacetComponent 会用到。
FacetComponent 在根据某个field的词时,会用到fieldValueCache,key是facet.field 的值,value是UnInvertedField,UnInvertedField这个类主要负责完成把field域每个词Term,以及词Term在所有文档field域的频率,即出现的次数。
保存在一个数组中,创建的UnInvertedField保存在fieldValueCache缓存中,得到UnInvertedField后,调用UnInvertedField的getCounts方法,跟查询到的document ID 做交集,如果不是查询结果的document ID,,则该Field的词的个数为0,除此之外,还对field出现的词做一个排序,solr的FacetComponet有两种排序选择,分别是count和index,count是按每个词出现的次数,index是按词的字典顺序。
如果查询参数不指定facet.sort,solr默认是按count排序。
solr查询参数使用说明q –查询字符串,必须的。
Solr 中用来搜索的查询。
有关该语法的完整描述,请参阅参考资料中的“Lucene QueryParser Syntax”。
可以通过追加一个分号和已索引且未进行断词的字段的名称来包含排序信息。
默认的排序是score desc,指按记分降序排序。
q=myField:Java AND otherField:developerWorks; date asc此查询搜索指定的两个字段并根据一个日期字段对结果进行排序。
start –返回第一条记录在完整找到结果中的偏移位置,0开始,一般分页用。
rows –指定返回结果最多有多少条记录,配合start 来实现分页。
sort –排序,格式:sort=<fieldname>+<desc|asc>[,<fieldname>+<desc|asc>]…。
示例:(inStock desc, price asc)表示先“inStock”降序, 再“price”升序,默认是相关性降序。
wt –(writer type)指定输出格式,可以有xml, json, php, phps, 后面solr 1.3增加的,要用通知我们,因为默认没有打开。
fq –(filter query)过虑查询,作用:在q 查询符合结果中同时是fq查询符合的,fl- field作为逗号分隔的列表指定文档结果中应返回的Field 集。
默认为“*”,指所有的字段。
“score”指还应返回记分。
例如*,score将返回所有字段及得分。
用solrj的bean时,得在query中指定query.set("fl", "*,score");q.op –覆盖schema.xml的defaultOperator(有空格时用"AND"还是用"OR"操作逻辑),一般默认指定df –默认的查询字段,一般默认指定qt –(query type)指定那个类型来处理查询请求,一般不用指定,默认是standard。
solrj的facet查询总结
1. Facet简介
Facet是solr的高级搜索功能之一,可以给用户提供更友好的搜索体验。
在搜索关键字的同时,能够按照Facet的字段进行分组并统计。
2. Facet字段
2.1. 适宜被Facet的字段
一般代表了实体的某种公共属性。
如商品的分类,商品的制造厂家,书籍的出版商等等。
2.2. Facet字段的要求
Facet的字段必须被索引。
一般来说该字段无需分词、无需存储。
无需分词是因为该字段的值代表了一个整体概念,如电脑的品牌”联想”代表了一个整体概念。
如果拆成”联”,”想”两个字都不具有实际意义。
另外该字段的值无需进行大小写转换等处理,保持其原貌即可。
无需存储是因为一般而言用户所关心的并不是该字段的具体值,
而是作为对查询结果进行分组的一种手段,
用户一般会沿着这个分组进一步深入搜索。
各个Facet字段互不影响,且可以针对每个Facet字段设置查询参数。
以下介绍的参数既可以应用于所有的Facet字段,也可以应用于每个单独的Facet字段。
应用于单独的字段时通过
f.字段名.参数名=参数值
这种方式调用,比如facet.prefix参数应用于cpu字段,可以采用如下形式:
f.cpu.facet.prefix=Intel
4.1.1. facet.prefix
表示Facet字段值的前缀。
比如”facet.field=cpu&facet.prefix=Intel”,那么对cpu字段进行Facet查询,返回的cpu都是以”Intel”开头的,”AMD”开头的cpu型号将不会被统计在内。
4.1.2. facet.sort
表示Facet字段值以哪种顺序返回,可接受的值为true(count)|false(index,lex)。
true(count)表示按照count值从大到小排列。
false(index,lex)表示按照字段值的自然顺序(字母,数字的顺序)排列。
默认情况下为true(count)。
当facet.limit值为负数时,默认facet.sort= false(index,lex)。
4.1.3. facet.limit
限制Facet字段返回的结果条数,默认值为100。
如果此值为负数,表示不限制。
4.1.4. facet.offset
返回结果集的偏移量,默认为0。
它与facet.limit配合使用可以达到分页的效果.
4.1.
5. facet.mincount
限制了Facet字段值的最小count,默认为0。
合理设置该参数可以将用户的关注点集中在少数比较热门的领域。
4.1.6. facet.missing
默认为””,如果设置为true或者on,那么将统计那些Facet字段值为null的记录。
4.1.7. facet.method
取值为enum或fc,默认为fc。
该字段表示了两种Facet的算法,与执行效率相关。
enum适用于字段值比较少的情况,比如字段类型为布尔型,或者字段表示中国的所有省份。
Solr会遍历该字段的所有取值,并从filterCache里为每个值分配一个filter(这里要求solrconfig.xml 里对filterCache的设置足够大).然后计算每个filter与主查询的交集。
fc(表示Field Cache)适用于字段取值比较多,但在每个文档里出现次数比较少的情况。
Solr会遍历所有的文档,在每个文档内搜索Cache内的值,如果找到就将Cache内该值的count加1。
4.1.8. facet.enum.cache.minDf
当facet.method=enum时,此参数其作用,minDf表示minimum document frequency。
也就是文档内出现某个关键字的最少次数,该参数默认值为0。
设置该参数可以减少filterCache的内存消耗,但会增加总的查询时间(计算交集的时间增加了)。
如果设置该值的话,官方文档建议优先尝试25-50内的值。
4.2. Date Facet
日期类型的字段在文档中很常见,如商品上市时间,货物出仓时间,书籍上架时间等等。
某些情况下需要针对这些字段进行Facet。
不过时间字段的取值有无限性,用户往往关心的不是某个时间点而是某个时间段内的查询统计结果。
Solr为日期字段提供了更为方便的查询统计方式。
当然,字段的类型必须是DateField(或其子类型)。
需要注意的是,使用Date Facet时,字段名、起始时间、结束时间、时间间隔这4个参数都必须提供。
与Field Facet类似,Date Facet也可以对多个字段进行Facet,并且针对每个字段都可以单独设置参数。
4.2.1. facet.date
该参数表示需要进行Date Facet的字段名,与facet.field一样,该参数可以被设置多次,表示对多个字段进行Date Facet。
4.2.2. facet.date.start
起始时间,时间的一般格式为” 1995-12-31T23:59:59Z”,另外可以使用”NOW”、”YEAR”、”MONTH”等等,具体格式可以参考org.apache.solr.schema. DateField的java doc。
4.2.3. facet.date.end
结束时间。
4.2.4. facet.date.gap
时间间隔。
如果start为2009-1-1,end为2010-1-1。
gap设置为”+1MONTH”表示间隔1个月,那么将会把这段时间划分为12个间隔段。
注意”+”因为是特殊字符所以应该用”%2B”代替。
4.2.
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。
4.2.6. facet.date.other
取值范围为before|after|between|none|all,默认为none。
before会对start之前的值做统计。
after会对end之后的值做统计。
between会对start至end之间所有值做统计。
如果hardend为true的话,那么该值就是各个时间段统计值的和。
none表示该项禁用。
all表示before,after,all都会统计。
举例:
&facet=on
&facet.date=date
&facet.date.start=2009-1-1T0:0:0Z
&facet.date.end=2010-1-1T0:0:0Z
&facet.date.gap=%2B1MONTH
&facet.date.other=all
返回结果:
4.5. tag操作符和ex操作符【非常有用】
当查询使用filter query的时候,如果filter query的字段正好是Facet字段,那么查询结果往往被限制在某一个值内。
例:
可以看到,屏幕尺寸(screenSize)为14寸的产品共有107件,其它尺寸的产品的数目都是0。
这是因为在filter里已经限制了screenSize:14。
这样,查询结果中,,除了screenSize=14的这一项之外,其它项目没有实际的意义。
应用场景:
有些时候,用户希望把结果限制在某一范围内,又希望查看该范围外的概况。
比如上述情况,既要把查询结果限制在14寸屏的笔记本,又想查看一下其它屏幕尺寸的笔记本有多少产品。
这个时候需要用到tag和ex操作符。
tag就是把一个filter标记起来,ex(exclude)是在Facet的时候把标记过的filter排除在外。
例:
这样其它屏幕尺寸的统计信息就有意义了。