`
lxwt909
  • 浏览: 570796 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Lucene5学习之Facet简单入门

阅读更多

       说到Facet,我还真找不到一个合适的中文词汇来描述它,英文翻译是方面,感觉不够贴切,大家也不必纠结它的中文叫法是啥,你只需要知道使用Facet能解决什么类型的问题就行了,来看几个典型的应用案例:


 

 

        看了上面几张图,大家应该知道Facet是用来干嘛的了,如果非要用语言描述Facet的用途,那Facet的用途就是根据域的域值进行分组统计,注意这里的域必须是FacetField,你Facet域的域值有几个就会分几组,并统计在Query查询条件下各组的命中结果数量。但通常不需要显示所有分组,就如图上面3张图,一般都是显示Top N 个分组即可。是不是觉得Facet和Group有点相似,对,看起来是有那么一点相似,那两者到底有什么区别呢?

 

They are two different lucene features:

Grouping was first released with Lucene 3.2, its related jira issue is LUCENE-1421: it allows to group search results by specified field. For example, if you group by the author field, then all documents with the same value in the author field fall into a single group. You will have a kind of tree as output. If you want to go deeper into using this lucene feature, this blog post should be useful.
Faceting was first released with Lucene 3.4, its related jira issue is LUCENE-3079: this feature doesn't group documents, it just tells you how many documents fall in a specific value of a facet. For example, if you have a facet based on the author field, you will receive a list of all your authors, and for each author you will know how many documents belong to that specific author. After, if you want to see those documents, you have to query one more time adding a specific filter (author=whatever). The faceted search is in fact based on browsing documents applying multiple filters to progressively reach the documents you're really interested in.

    对不起,只有英文的说明,大意就是:Grouping分组功能是在跟随Lucene 3.2稳定版首次发布的,它允许你根据一个指定的域进行分组,举个例子,如果你根据一个author域进行分组,那么这个域的所有域值相同的索引文档进行落入到这个分组中。Facet是在跟随Lucene3.4稳定版首次发布的,facet并不对文档进行分组,Facet只是告诉你某个Facet下每个域值的命中数量,举个例子,如果你有个facet是基于author域的,那么facet会返回author域下的每个域值,以及每个author域值下的命中结果总数。如果你想查看每个author域值下的命中结果,那么你可能需要再发起一次请求,通过添加一个filter如author=xxxx.  其实Facet搜索就是通过应用多个filter来让用户浏览索引文档,使用户逐步找到自己感兴趣的索引文档,一句话:Facet分组统计的目的是通过统计的数量诱发你点击的欲望,一般你看到数量多的,你会有点击欲望,点击进去了你自己会判断是不是你感兴趣的内容,如果不是,那么你会点击数据量次之的,如此下去,逐步诱导你找到你感兴趣的内容,这就是Facet功能设计的目的。说白了就是利用羊群效应诱发你去点击。

 

       首先来你需要创建FacetField域,在创建之前你需要了解FacetField的是否分词,存储,位置信息等。看看FacetField源码一切就知晓了。


    FacetField的域名称都是
dummy,域类型都是默认的DOCS_AND_FREQS_AND_POSITIONS即需要记录Term频率和Document频率(即项向量)和位置信息。而FieldType对于默认是Stored=false,而tokenized=true(即会进行分词处理转化为多个Term),了解这些很有必要。

       然后FacetField跟普通的Field一样,需要添加到document中,然后document需要通过IndexWriter对象a调用addDocument写入索引,但此时document需要做一个转换过程,即

FacetsConfig.build(DirectoryTaxonomyWriter writer,Document document);

    我们来看看FacetsConfig的build方法背地里都干了些什么?

 


     首先定义了3个Map分别对应了3种类型的FacetField:FacetField,SortedSetDocValuesFacetField,AssociationFacetField, FacetField就是普通的Facet域,SortedSetDocValuesFacetField就是可以用来排序的DocValuesField域,AssociationFacetField是用来自定义Facets的域,它可以关联任意的byte[]字节数组.把用户添加的域用3个map分开后,分别用了3个函数进行处理,如图:

     processFacetFields内部关键点代码就是:

     pathToString就是把多个域值拼在一起,比如:

new FacetField("Author", new String[] { "Bob" ,"Jack","Tom"})

    那拼一起后就是BobJackTom,然后创建了一个StringField且Store.NO,意思就是我们add一个FacetField其实就是add了一个StringField,当然两者不能完全等同。注意是if里的条件:

ft.multiValued && (ft.hierarchical || ft.requireDimCount)即如果是多值域且(path有多个值或者需要统计facet总数),如果不是多值域,则会add一个BinaryDocValuesField域:

doc.add(new BinaryDocValuesField(indexFieldName, dedupAndEncode(ordinals.get())));

    然后我们通过IndexSearcher查询的时候需要传入FacetsCollector结果收集器,剩下的套路基本都是固定的,没什么好说的,如下:

FacetsCollector fc = new FacetsCollector();

		searcher.search(new MatchAllDocsQuery(), null, fc);

		List<FacetResult> results = new ArrayList<FacetResult>();

		Facets facets = new FastTaxonomyFacetCounts(taxoReader, this.config, fc);

		results.add(facets.getTopChildren(10, "Author"));
		results.add(facets.getTopChildren(10, "Publish Date"));

		indexReader.close();
		taxoReader.close();

     至于DrillDownQuery,他其实就是根据用户传入的path数组用BooleanQuery进行链接的:

 

     先用BooleanQuery把多个TermQuery用Or链接起来,再用ConstantScoreQuery包装下,主要是为了禁用查询权重的。

    至于DrillSideways更不需要被它的外表迷惑了,其实他内部其实还是根据传入的IndexSearch和Facet结果收集器去查询的:

       内部就是为了包装得到一个DrillSidewaysQuery对象,最后还是调用的IndexSearcher的search方法。

      

 

 

       下面是一个Facet使用简单示例:

package com.yida.framework.lucene5.facet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.facet.DrillDownQuery;
import org.apache.lucene.facet.DrillSideways;
import org.apache.lucene.facet.FacetField;
import org.apache.lucene.facet.FacetResult;
import org.apache.lucene.facet.Facets;
import org.apache.lucene.facet.FacetsCollector;
import org.apache.lucene.facet.FacetsConfig;
import org.apache.lucene.facet.taxonomy.FastTaxonomyFacetCounts;
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;

/**
 * Facet简单示例
 * 
 * @author Lanxiaowei
 * 
 */
public class SimpleFacetsExample {
	private final Directory indexDir = new RAMDirectory();
	private final Directory taxoDir = new RAMDirectory();
	private final FacetsConfig config = new FacetsConfig();

	public SimpleFacetsExample() {
		this.config.setHierarchical("Author", true);
		this.config.setHierarchical("Publish Date", true);
	}

	/**
	 * 创建测试索引
	 * 
	 * @throws IOException
	 */
	private void index() throws IOException {
		IndexWriter indexWriter = new IndexWriter(this.indexDir,
				new IndexWriterConfig(new WhitespaceAnalyzer())
						.setOpenMode(IndexWriterConfig.OpenMode.CREATE));

		DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(
				this.taxoDir);

		Document doc = new Document();
		doc.add(new FacetField("Author", new String[] { "Bob" }));
		doc.add(new FacetField("Publish Date", new String[] { "2010", "10",
				"15" }));
		indexWriter.addDocument(this.config.build(taxoWriter, doc));

		doc = new Document();
		doc.add(new FacetField("Author", new String[] { "Lisa" }));
		doc.add(new FacetField("Publish Date", new String[] { "2010", "10",
				"20" }));
		indexWriter.addDocument(this.config.build(taxoWriter, doc));

		doc = new Document();
		doc.add(new FacetField("Author", new String[] { "Lisa" }));
		doc.add(new FacetField("Publish Date",
				new String[] { "2012", "1", "1" }));
		indexWriter.addDocument(this.config.build(taxoWriter, doc));

		doc = new Document();
		doc.add(new FacetField("Author", new String[] { "Susan" }));
		doc.add(new FacetField("Publish Date",
				new String[] { "2012", "1", "7" }));
		indexWriter.addDocument(this.config.build(taxoWriter, doc));

		doc = new Document();
		doc.add(new FacetField("Author", new String[] { "Frank" }));
		doc.add(new FacetField("Publish Date",
				new String[] { "1999", "5", "5" }));
		indexWriter.addDocument(this.config.build(taxoWriter, doc));

		indexWriter.close();
		taxoWriter.close();
	}

	private List<FacetResult> facetsWithSearch() throws IOException {
		DirectoryReader indexReader = DirectoryReader.open(this.indexDir);
		IndexSearcher searcher = new IndexSearcher(indexReader);
		TaxonomyReader taxoReader = new DirectoryTaxonomyReader(this.taxoDir);

		FacetsCollector fc = new FacetsCollector();

		FacetsCollector.search(searcher, new MatchAllDocsQuery(), 10, fc);

		List<FacetResult> results = new ArrayList<FacetResult>();

		Facets facets = new FastTaxonomyFacetCounts(taxoReader, this.config, fc);
		results.add(facets.getTopChildren(10, "Author", new String[0]));
		results.add(facets.getTopChildren(10, "Publish Date", new String[0]));

		indexReader.close();
		taxoReader.close();

		return results;
	}

	private List<FacetResult> facetsOnly() throws IOException {
		DirectoryReader indexReader = DirectoryReader.open(this.indexDir);
		IndexSearcher searcher = new IndexSearcher(indexReader);
		TaxonomyReader taxoReader = new DirectoryTaxonomyReader(this.taxoDir);

		FacetsCollector fc = new FacetsCollector();

		searcher.search(new MatchAllDocsQuery(), null, fc);

		List<FacetResult> results = new ArrayList<FacetResult>();

		Facets facets = new FastTaxonomyFacetCounts(taxoReader, this.config, fc);

		results.add(facets.getTopChildren(10, "Author"));
		results.add(facets.getTopChildren(10, "Publish Date"));

		indexReader.close();
		taxoReader.close();

		return results;
	}

	private FacetResult drillDown() throws IOException {
		DirectoryReader indexReader = DirectoryReader.open(this.indexDir);
		IndexSearcher searcher = new IndexSearcher(indexReader);
		TaxonomyReader taxoReader = new DirectoryTaxonomyReader(this.taxoDir);

		
		DrillDownQuery q = new DrillDownQuery(this.config);
		q.add("Publish Date", new String[] { "2010" });
		
		FacetsCollector fc = new FacetsCollector();
		FacetsCollector.search(searcher, q, 10, fc);

		Facets facets = new FastTaxonomyFacetCounts(taxoReader, this.config, fc);
		FacetResult result = facets.getTopChildren(10, "Author", new String[0]);

		indexReader.close();
		taxoReader.close();

		return result;
	}

	private List<FacetResult> drillSideways() throws IOException {
		DirectoryReader indexReader = DirectoryReader.open(this.indexDir);
		IndexSearcher searcher = new IndexSearcher(indexReader);
		TaxonomyReader taxoReader = new DirectoryTaxonomyReader(this.taxoDir);

		DrillDownQuery q = new DrillDownQuery(this.config);

		q.add("Publish Date", new String[] { "2010" });

		DrillSideways ds = new DrillSideways(searcher, this.config, taxoReader);
		DrillSideways.DrillSidewaysResult result = ds.search(q, 10);

		List<FacetResult> facets = result.facets.getAllDims(10);

		indexReader.close();
		taxoReader.close();

		return facets;
	}

	public List<FacetResult> runFacetOnly() throws IOException {
		index();
		return facetsOnly();
	}

	public List<FacetResult> runSearch() throws IOException {
		index();
		return facetsWithSearch();
	}

	public FacetResult runDrillDown() throws IOException {
		index();
		return drillDown();
	}

	public List<FacetResult> runDrillSideways() throws IOException {
		index();
		return drillSideways();
	}

	public static void main(String[] args) throws Exception {
		// one
		System.out.println("Facet counting example:");
		System.out.println("-----------------------");
		SimpleFacetsExample example = new SimpleFacetsExample();
		List<FacetResult> results1 = example.runFacetOnly();
		System.out.println("Author: " + results1.get(0));
		System.out.println("Publish Date: " + results1.get(1));
		
		
		// two
		System.out.println("Facet counting example (combined facets and search):");
		System.out.println("-----------------------");
		List<FacetResult> results = example.runSearch();
		System.out.println("Author: " + results.get(0));
		System.out.println("Publish Date: " + results.get(1));
		
		
		// three
		System.out.println("Facet drill-down example (Publish Date/2010):");
		System.out.println("---------------------------------------------");
		System.out.println("Author: " + example.runDrillDown());

		// four
		System.out.println("Facet drill-sideways example (Publish Date/2010):");
		System.out.println("---------------------------------------------");
		for (FacetResult result : example.runDrillSideways()) {
			System.out.println(result);
		}
	}
}

 

    Facet内容有点多,打算Facet内容还要再写一篇做补充,暂时先说这么多,后续再补充,这篇算是一个Facet入门吧。Demo源码哥就不上传了,ITEye上传一个2M多的文件都上传不成功,试了N次了,ITEye管理员,你给我粗来!!!

     

     如果你还有什么问题请加我Q-Q:7-3-6-0-3-1-3-0-5,

或者加裙
一起交流学习!

     

    

     

 

    

      

     

  • 大小: 184.4 KB
  • 大小: 367.2 KB
  • 大小: 459.5 KB
  • 大小: 383.5 KB
  • 大小: 438.1 KB
  • 大小: 440.9 KB
  • 大小: 117.8 KB
  • 大小: 303.8 KB
  • 大小: 294.6 KB
  • 大小: 352.8 KB
  • 大小: 557.8 KB
3
2
分享到:
评论
2 楼 kimibaby1990 2017-08-18  
谢谢分享~
“pathToString就是把多个域值拼在一起” 好像有些问题。
1 楼 axhack 2015-04-17  
demo可以传到百度网盘

相关推荐

    Lucene5学习之Facet(续)

    《Lucene5学习之Facet(续)》 在深入探讨Lucene5的Facet功能之前,我们先来了解一下什么是Faceting。Faceting是搜索引擎提供的一种功能,它允许用户通过分类或属性对搜索结果进行细分,帮助用户更精确地探索和理解...

    Lucene5学习之创建索引入门示例

    **Lucene5学习之创建索引入门示例** 在IT领域,搜索引擎的开发与优化是一项关键技术,而Apache Lucene作为一款高性能、全文本搜索库,是许多开发者进行文本检索的首选工具。本文将深入探讨如何使用Lucene5来创建一...

    lucene分组查询优化facet

    其中,Facet(分面)查询是Lucene提供的一种强大的分类和统计功能,它允许用户根据特定的维度(如作者、类别等)对搜索结果进行分组和计数,从而帮助用户更深入地探索数据。本篇文章将详细探讨Lucene的分组查询优化...

    lucene facet查询示例

    **Lucene Facet查询详解** Lucene是一款强大的全文搜索引擎库,广泛应用于各种信息检索系统。在处理大量数据时,为了帮助用户快速、有效地探索和理解数据,Lucene引入了Facets(方面)功能,它提供了分类浏览和统计...

    Lucene5学习之自定义Collector

    这篇博客“Lucene5学习之自定义Collector”显然聚焦于如何在Lucene 5版本中通过自定义Collector来优化搜索结果的收集过程。Collector是Lucene搜索框架中的一个重要组件,它负责在搜索过程中收集匹配的文档,并根据...

    Lucene5学习之排序-Sort

    “Lucene5学习之排序-Sort”这个标题表明了我们要探讨的是关于Apache Lucene 5版本中的排序功能。Lucene是一个高性能、全文检索库,它提供了强大的文本搜索能力。在这个主题中,我们将深入理解如何在Lucene 5中对...

    Lucene5学习之Highlighte关键字高亮

    《Lucene5学习之Highlighter关键字高亮》 在信息技术领域,搜索引擎的使用已经变得无处不在,而其中的关键技术之一就是如何有效地突出显示搜索结果中的关键字,这就是我们今天要探讨的主题——Lucene5中的...

    Lucene5学习之Group分组统计

    "Lucene5学习之Group分组统计" 这个标题指出我们要讨论的是关于Apache Lucene 5版本中的一个特定功能——Grouping。在信息检索领域,Lucene是一个高性能、全文搜索引擎库,而Grouping是它提供的一种功能,允许用户对...

    Lucene5学习之SpellCheck拼写纠错

    **标题:“Lucene5学习之SpellCheck拼写纠错”** 在深入探讨Lucene5的SpellCheck功能之前,首先需要理解Lucene是什么。Lucene是一个开源的全文检索库,由Apache软件基金会开发,它提供了高性能、可扩展的文本搜索...

    Lucene5学习之拼音搜索

    本文将围绕“Lucene5学习之拼音搜索”这一主题,详细介绍其拼音搜索的实现原理和实际应用。 首先,我们需要理解拼音搜索的重要性。在中文环境中,由于汉字的复杂性,用户往往习惯于通过输入词语的拼音来寻找信息。...

    Lucene5学习之增量索引(Zoie)

    总结起来,Lucene5学习之增量索引(Zoie)涉及到的关键技术点包括: 1. 基于Lucene的增量索引解决方案:Zoie系统。 2. 主从复制架构:Index Provider和Index User的角色。 3. 数据变更追踪:通过变更日志实现增量索引...

    Lucene5学习之自定义排序

    本文将深入探讨“Lucene5学习之自定义排序”这一主题,帮助你理解如何在Lucene5中实现自定义的排序规则。 首先,Lucene的核心功能之一就是提供高效的全文检索能力,但默认的搜索结果排序通常是基于相关度得分...

    Lucene5学习之FunctionQuery功能查询

    **标题解析:** "Lucene5学习之FunctionQuery功能查询" Lucene5是Apache Lucene的一个版本,这是一个高性能、全文本搜索库,广泛应用于搜索引擎和其他需要高效文本检索的系统。FunctionQuery是Lucene中的一种查询...

    Lucene5学习之分页查询

    本文将深入探讨"Lucene5学习之分页查询"这一主题,结合给定的标签"源码"和"工具",我们将讨论如何在Lucene5中实现高效的分页查询,并探讨其背后的源码实现。 首先,理解分页查询的重要性是必要的。在大型数据集的...

    lucene3.6的入门案例

    **Lucene 3.6 入门案例** Lucene 是一个高性能、全文本搜索库,由 Apache 软件基金会开发。它提供了完整的搜索功能,包括索引、查询、评分等,广泛应用于各种项目和产品中。在这个入门案例中,我们将深入理解如何...

    Lucene5学习之Suggest关键字提示

    《深入探索Lucene5:Suggest关键字提示技术》 在信息检索领域,用户输入查询时,提供快速、准确的关键字提示能显著提升用户体验。Lucene,作为Java领域最流行的全文检索库,其5.x版本引入了Suggest组件,用于实现...

    Lucene5学习之多线程创建索引

    《Lucene5学习之多线程创建索引》 在深入了解Lucene5的多线程索引创建之前,我们先来了解一下Lucene的基本概念。Lucene是一个高性能、全文本搜索库,由Apache软件基金会开发。它提供了强大的文本分析、索引和搜索...

    Lucene5学习之Spatial地理位置搜索

    《深入探索Lucene5 Spatial:地理位置搜索》 在信息技术飞速发展的今天,地理位置搜索已经成为许多应用和服务不可或缺的一部分。Apache Lucene作为一个强大的全文搜索引擎库,其在5.x版本中引入了Spatial模块,使得...

    Lucene5学习之自定义同义词分词器简单示例

    本篇将聚焦于"Lucene5学习之自定义同义词分词器简单示例",通过这个主题,我们将深入探讨如何在Lucene5中自定义分词器,特别是实现同义词扩展,以提升搜索质量和用户体验。 首先,理解分词器(Analyzer)在Lucene中...

    lucene学习lucene学习

    2. 创建索引:清单 1 展示了一个简单的 Java 示例,演示如何使用 Lucene 对一个目录中的 .txt 文件创建索引。在这个例子中,`fileDir` 指定包含待索引文本文件的目录,`indexDir` 是存储 Lucene 索引文件的位置。`...

Global site tag (gtag.js) - Google Analytics