Hibernate Search(基于version3.4)--第五章Querying
Querying
?
Hibernate Search的第二个很重要的能力是运行Lucene queries并通过Hibernate session获得受管理的实体。search在提供了Lucene强大的功能之外还保持着Hibernate的编程模式(给Hibernate典型的search机制提供另外的dimension:HQL,Criteria query,native SQL query)
?
预备和运行一个query由4个步骤组成:
?
创建一个FullTextSession通过Hibernate Search query DSL(推荐的)或Lucene query API创建一个Lucene query。使用org.hibernate.Query包装Lucene query通过调用list() 或scroll()执行search你必须使用FullTextSession来访问query功能。这个具体的search session包装了一个一般的org.hibernate.Session来提供query和indexing能力。
?
Example 5.1. Creating a FullTextSession
?
??
Note:建立在Lucene query之上的Hibernate query是一般的org.hibernate.Query实现,这意味着你可以使用与其他Hibernate query功能(HQL,Native or Criteria)相同的模式来编程。org.hibernate.Query中的list(),uniqueResult(),iterate()和scroll()方法都可以使用。
?
你也可以使用Hibernate的Java Persistence API:
?
Example 5.3. Creating a Search query using the JPA API
?
?5.1.2.7. 查询选项(Query options)我们在前面的例子中看到过不少的查询选项,但让我们再一次总结这些选项:boostedTo (on query type and on field): 使用给定的factor来boost整个query或指定的fieldwithConstantScore (on query):所有匹配query的结果都有一个常量的分数等同于boost。filteredBy(Filter)?(on query):使用Filter实例来过滤查询结果ignoreAnalyzer (on field):不用analyzer来处理这个field。ignoreFieldBridge (on field):不用field bridge来处理这个field。?
让我们看看应用了这些选项的例子:
?
?
Hibernate Search从Lucene index中抽离出实体对象的属性并把它们向上转换成Object,最终结果返回Object[]列表。Projection避开了潜在数据库的查询(如果响应时间很重要的话,这就会很有用了)。然而,它还有一些约束条件:
?
?
投影所对应的属性必须保存在index中,即@Field(store=Store.YES)属性投影必须使用org.hibernate.search.bridge.TwoWayFieldBridge或org.hibernate.search.bridge.TwoWayStringBridge的FieldBridge实现,后者是一个简便版本。?
?
Note:所有Hibernate Search内建类型都是two-way的。
?
你只可以投影实体对象或其关联对象中的简单属性。意思是说不能投影内嵌的整个实体。投影不能应用于使用了@IndexedEmbedded的集合或map。Projection还有另外的用途。Lucene能为结果集提供一些元信息。通过使用指定的projection常量,projection机制能获取这样的元信息:?
?
?
Example 5.10. Using projection in order to retrieve meta data
?
?
?
?
?
你可以混合投影field和projection常量。下面列举了可用的projection常量:
?
FullTextQuery.THIS:返回整个受管对象(这不再是使用projected query)FullTextQuery.DOCUMENT:返回实体对象对应的Lucene Document。FullTextQuery.OBJECT_CLASS:返回实体对象的class。FullTextQuery.SCORE:返回对应的document scoreFullTextQuery.ID:投影对象的id值。FullTextQuery.DOCUMENT_ID:投影Lucene document id。小心,Lucene document id会在打开新的IndexReader时变得不一样。(这个功能还在测试中)FullTextQuery.EXPLANATION:返回匹配的Lucene Explanation对象。?
?
?
?
?
5.1.3.6.自定义对象初始化策略(Customizing object initialization strategies)
?
?
?
?
默认地,Hibernate Search使用最合适的策略来初始化匹配的实体对象。它运行一个或多个查询来获取请求的实体对象。当实体对象存储在持久化上下文或二级缓存中时,默认的方法会最小程度地访问数据库,因此也是最好的方法。
?
如果大多数的实体对象缓存在二级缓存中,你可以强迫Hibernate Search先从缓存中获取对象,如果没有再访问数据库。
?
Example 5.11. Check the second-level cache before using a query
?
?
?
?
ResultTransformer的实现例子可以在Hibernate Core codebase中找到。
?
?
5.2.4. Understanding results
有时候你可能会因为某些查询结果而感到迷惑。Luke是一个很好的工具帮助你了解查询的结果。然而,Hibernate Search也能让你从给定的query中访问Lucene Explanation对象。这个对象对于Lucene用户来说是相当高级的,不过能为理解一个结果的分数提供很好的帮助。你有两种方式来访问结果对应的Explanation对象:
?
使用fullTextQuery.explain(int)方法使用projection第一种方法用document id作为参数来返回Explanation对象。document id可以通过projection和FullTextQuery.DOCUMENT_ID常量获取。?
?
?
Warning:document id与实体的id是不同的。不要混淆这两个概念。
?
?
第二种方法让你使用FullTextQuery.EXPLANATION常量来投影Explanation对象。
?
Example 5.18. Retrieving the Lucene Explanation object using projection
?
?
?在这个例子中,我们在query之上使用了两个filter。如果你有这个需要的话,你可以使用任意数量的filter。
?
声明filter是通过@FullTextFilterDef注解完成。该注解标注在带有@Indexed注解的实体上。这暗示了filter声明是全局的和它们的名字必须是唯一的。如果两个不同的@FullTextFilterDef声明两个相同名字的filter,就会抛出SearchException。每个命名的filter必须指定它自己的filter实现。
?
Example 5.20. Defining and implementing a Filter
?
?
BestDriversFilter是一个简单的Lucene filter实现,它把所有'score'不为5的结果过滤掉了。在这个例子中,具体的filter直接实现了org.apache.lucene.search.Filter并包含一个无参的构造器。
?
如果你的Filter创建需要额外的步骤或Filter需要使用有参数的构造器,那么你就要使用factory模式:
?
Example 5.21. Creating a filter using the factory pattern
?
??
当运行这个faceting request,将会为每个离散值(在这里是'label' field的值)创建一个Facet实例。Facet实例会记录下实际的field value,包括这个field value的值在原查询结果中出现的频率。orderedBy,includeZeroCounts和maxFacetCount是任何faceting request的可选参数。orderedBy允许指定返回的facet的顺序,默认是FacetSortOrder.COUNT_DESC,不过你也可能按field value或按指定的范围排序。includeZeroCount定义是否计数为0的facet也包含在结果中(默认是包括的)。maxFacetCount限制了最大的facet返回数。
?
?
?
Tip:应用faceting的indexed field需要满足一些先决条件。被索引的属性域必须是字符串,日期或数值类型。另外属性域必须以Index.UN_TOKENIZED方式索引,数值型的属性域必须标注为@NumericField
?
?
?
range faceting request的创建非常相似,除了我们必须为field指定一个范围值。Example 5.27,“Creating a range faceting request”是一个range faceting request的例子,它指定了三个不同的price范围。below和above只能指定一次,但你可以任意地指定from-to范围。通过excludeLimit方法定义是否包括每个范围的边界。
?
?
Example 5.27. Creating a range faceting request
?
?
??
5.4.2. Applying a faceting request
在5.4.1节'creating a faceting request',我们已经看到怎么样去创建一个faceting request。现在是时候在查询时应用这个faceting request。关键在于从FulltextQuery中获取的FacetManager。(see Example 5.28, “Applying a faceting request”)
?
Example 5.28. Applying a faceting request
?
?
?
只要你有这样的需要,你可以使用添加任何数量的faceting request,并通过getFacets()和faceting request name来获取Facet。同样地,有一个disableFaceting()方法,它可以通过request name来禁用一个faceting request。
?
5.4.3. 限制查询结果(Restricting query results)
?
最后但不重要的是,你可以应用任何返回的Facet作为你原本的query的额外的criteria,这样就可以实现一个"drill-down"功能。为了这个目的,就得利用FacetSelection这个类。可以通过FacetManager来应用FacetSelection,并允许你选择一个facet作为query criteria(selectFacet),移除一个facet restriction(deselectFacets),移除所有facet restrictions(clearSelectedFacets)并获取当前所有的selected facets(getSelectedFacets)。?Example 5.29, “Restricting query results via the application of a?FacetSelection” shows an example.
?
?
Example 5.29. Restricting query results via the application of a FacetSelection
?
?
5.5. Optimizing the query process
查询性能依赖于下面几个准则:
?
Lucene query自身的问题。查看关于这方面更多的文献加载对象的数量。使用pagination或index projection。Hibernate Search与Lucene reader的交互方式:定义合适的Reader strategy。缓存频繁使用的从index中抽离的值。see Section 5.5.1, “Caching index values:FieldCache”?
5.5.1. Caching index values: FieldCache
Lucene index的主要功能是鉴定与查询的匹配关系,然而查询完成后,必须分析结果并抽离有用的信息:典型地,Hibernate Search需要抽出Class type和primary key。
?
从index中抽离需要的值是一种性能消耗,这种消耗可能很低并不易让人知道,但在某些时候caching会是一种很好的实践。
?
缓存的精确需要依赖于使用Projection的类型(see Section 5.1.3.5,“Projection”),有些时候,Class type是不需要缓存的,因为它可以通过query上下文获知。
?
使用@CacheFromIndex注解,你可以试验缓存Hibernate Search所需要的不同的主元数据field。
?
import static org.hibernate.search.annotations.FieldCacheType.CLASS;import static org.hibernate.search.annotations.FieldCacheType.ID;@Indexed@CacheFromIndex( { CLASS, ID } )public class Essay { ...?
?
通过这个注解现在就可以缓存Class type和ID。
CLASS:Hibernate Search将会使用Lucene的FieldCache来改善从index抽离Class type的性能。默认下这个值是可用的,Hibernate Search将应该这个值如果你没有指定@CacheFromIndex注解。ID:缓存主键标识符。这好像能提供最好的查询表现,但同时也会消耗更多的内存(有可能会降低性能)Note:在warmup(运行一些query)后,测量性能和内存消耗之间的影响:使用Field Cache好像能改善性能,但并不总是这样的。
?
?
使用FieldCache有两个缺点:
内存使用:缓存会消耗大量的内存。典型的CLASS缓存比ID缓存要求更低。Index warmup:当使用了field cache,第一次查询会比不用缓存慢。对于某些查询,classtype并不是必需的,在某些时候,即使你使用了CLASS field cache,它可能并不会被使用;例如如果你查询单个class,显然地返回的值将会是这个class类型。
对于使用ID FieldCache,实体的id必须使用TwoWayFieldBridge(比如所有内建的bridge),and all types being loaded in a specific query must use the fieldname forthe id, and have ids of the same type (this is evaluated at each Query execution).
?
?
?
?
?