5. Querying

简介: <div> <h2 style="orphans:2; text-align:justify; widows:2; font-family:'Lucida Grande',Geneva,Verdana,Arial,sans-serif; color:rgb(74,93,117); line-height:24px; margin-top:0em; font-size:1.6em"> <

 Chapter 5. Querying

 

Hibernate Search第二个最重要的能力就是执行lucene查询和检索Hibernate session中的实体.
准备和执行查询包括以下步骤:
  • 创建FullTextSession

  • 创建Lucene query,通过Hibernate Search query DSL (recommended)或者使用Lucene query API

  • Wrapping the Lucene query using an org.hibernate.Query

  • 执行查询--> list() or scroll()
我们使用FullTextSession进行查询,通过传递一个Hibernate的session

Example 5.1. Creating a FullTextSession

Session session = sessionFactory.openSession();
...
FullTextSession fullTextSession =Search.getFullTextSession(session);
一旦你拥有了FullTextSession,你可以使用2种查询方法: Hibernate Search query DSL 或者  Lucene query.
DSL查询方法:
final QueryBuilder b = fullTextSession.getSearchFactory()
    .buildQueryBuilder().forEntity( Myth.class ).get();

org.apache.lucene.search.Query luceneQuery =
    b.keyword()
        .onField("history").boostedTo(3)
        .matching("storm")
        .createQuery();

org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery );List result = fullTextQuery.list();//return a list of managed objects    
二选一,你可以选择一种方法进行查询操作。下面的例子是lucene api查询.

Example 5.2. Creating a Lucene query via the QueryParser

SearchFactory searchFactory = fullTextSession.getSearchFactory();
org.apache.lucene.queryParser.QueryParser parser = 
    new QueryParser("title", searchFactory.getAnalyzer(Myth.class) );
try {
    org.apache.lucene.search.Query luceneQuery = parser.parse( "history:storm^3" );
}
catch (ParseException e) {
    //handle parsing failure
}

org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery(luceneQuery);List result = fullTextQuery.list();//return a list of managed objects    

Note

Hibernate query方法是基于lucene query的:org.hibernate.Query, 这意味着Hibernate query也支持HQL, Native or Criteria). The regular list() , uniqueResult()iterate() and scroll()等平常我们使用的方法

你也可以使用JPA查询:

Example 5.3. Creating a Search query using the JPA API

EntityManager em = entityManagerFactory.createEntityManager();

FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(em);

...finalQueryBuilder b = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Myth.class).get();

org.apache.lucene.search.Query luceneQuery =  b.keyword().onField("history").boostedTo(3).matching("storm").createQuery();
javax.persistence.Query fullTextQuery = fullTextEntityManager.createFullTextQuery( luceneQuery );

List result = fullTextQuery.getResultList();//return a list of managed objects  

Note

接下来的例子都是介绍hibernate apis,但是可以很方便的转换到jpa方式、

使用lucene api,你可以有几个选项,使用query parser(简单查询),或者lucene programmatic api(复杂查询)
这个超出我们文档范围。请出门右拐找lucene文档。
 
使用lucene programmatic api进行全文检索挺麻烦的...balabala....
 
Hibernate Search 的DSL查询方法的api可以称作流畅的api(无耻 - -),有几个特性:
现在我们来看如何使用API,首先需要一个QueryBuilder,绑定一个要查询的类。QueryBuilder知道用什么分析器,
使用什么桥。
你也可以重写域要使用的分析器,但是很少这么做。除非你知道你在做什么、
使用query builder,要注意的是最终结果都是来自lucene query.因为这个原因,我们可以很容易的将lucene's query 
parser或者lucene programmatic api的查询通hibernate search DSL结合在一起,以防DSL不支持一些功能
 
我们先来查询特定单词
keyword()的意思是,查找一个特定的单词。OnField()指明查找哪个域。matching()为要查询的单词。
  • storm这个值通过history桥

  • 桥的值之后会传递给分析器,分析器对索引进行匹配。

我们来看看被搜索的属性不是String的时候:
hibernate search支持各种变换,不单单是Date,也提供其他的桥,提供objectToString方法(太方便啦!lucene只支持
String,而hibernate帮我们封装好啦)
 
 
接下来我们来个有点难度的例子。使用连词分析器(ngram analyzers)。连词分析器可以弥补因为用户打错字,导致
搜索不到结果的情况。比如我们搜索(3-grams,应该是3个字母组合的意思)hibernate可以是: hib, ibe, ber, rna, nat, ate.
在上面的例子中,我们搜索的关键字Sisiphus,会先转换成小写,然后分成3个字母组合(3-grams) sis, isi, sip, phu, hus. 每个
n-gram都将作为查询关键字。
 

Note

如果不想使用桥(field bridge)或者分析器,可以使用ignoreAnalyzer()和ignoreFieldBridge()

 
查询一个域里面可能包含的多个关键字使用:
查询几个域中可能包含关键字使用:
我们可以对域设置权重,name这个域权重为5:
使用模糊字段查询。
threshold 临界值)规定了两个 terms 被认为相同(匹配)的上限,是 0 1 之间的数,默认是 0.5 prefixLength(前缀长度)说明了模糊性(被忽略的前缀长度):如果被设置为0,则任意一个非零的值被推荐(估计是匹配所有)
 
可以执行通配符搜索(查找只知道单词部分内容),“?”代表单个字符,“ * ”代表任意多个字符。 注意 :出于性能的考虑,查询时不要以通配符开头。
可以使用它来搜索确切匹配或者相似的句子,可以使用 phrase ()来完成:
也可以搜索相似的句子,可以通过添加一个 slop factor 来实现。它允许其它单词出现在这个句子中。
现在介绍边界搜索(可以作用在数字、日期、字符串等上)。边界搜索用来在某两个边界之间进行搜索,或者搜索给定值之上或之下的结果,示例代码如下:
最后介绍组合查询,可以创建更复杂的查询语句,有以下组合操作可以供使用:

5.1.2.7. Query options

?     boostedTo:可以用在查询实体或字段中,使用给定的因子提升整个查询或特定字段。

?     withConstantScore (on query):和boost(作用)一样,所有匹配的查询结果有一个常量分数。

?     filteredBy(on query):使用过滤器过滤查询结果。

?     ignoreAnalyzer (on field):处理字段时忽略analyzer

?     ignoreFieldBridge (on field):处理字段时忽略field bridge

来看例子:
Query luceneQuery = mythQB
    .bool()
      .should( mythQB.keyword().onField("description").matching("urban").createQuery() )
      .should( mythQB
        .keyword()
        .onField("name")
          .boostedTo(3)
          .ignoreAnalyzer()
        .matching("urban").createQuery() )
      .must( mythQB
        .range()
          .boostedTo(5).withConstantScore()
        .onField("starred").above(4).createQuery() )
    .createQuery();

目前为止我们只讨论了如何创建 LuceneQuery ,这只是一系列动作中的第一步,现在看一看如果从 Lucene Query 创建 Hibernate Search Query

一旦Lucene Query被创建,他需要被包装成一个Hibernate查询。如果没有特殊说明,它将会对所有的索引实体进行查询,可能返回所有的索引类的类型。

从性能的角度考虑,建议限制返回的实体类型。

Example 5.4. Wrapping a Lucene query into a Hibernate Query

FullTextSession fullTextSession = Search.getFullTextSession( session );
org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery );

Example 5.5. Filtering the search result by entity type

fullTextQuery = fullTextSession    .createFullTextQuery( luceneQuery, Customer.class );

// or

fullTextQuery = fullTextSession    .createFullTextQuery( luceneQuery, Item.class, Actor.class );
在例 5.5 中,第一个例子只返回匹配 Customer 的结果,第二个例子返回匹配 Actor Item 类的机构。结果限制是多态实现的,也就是说如果有两个子类 Salesman Custom 继承自父类 Person ,可以只指定 Person.class 来过滤返回结果。

由于性能的原因,推荐每次查询返回一定数量的查询结果。事实上用户浏览时从一页翻到另一页是非常常见的情况。你定义翻页的方法正是使用 HQL Criteria 定义分页的方法。

Example 5.6. Defining pagination for a search query

org.hibernate.Query fullTextQuery =     fullTextSession.createFullTextQuery( luceneQuery, Customer.class );
fullTextQuery.setFirstResult(15); //start from the 15th 
elementfullTextQuery.setMaxResults(10); //return 10 elements

Tip

可以使用fulltextQuery.getResultSize()获取全部匹配元素的个数。

apache lucene提供非常强大方便的排序功能,

Example 5.7. Specifying a Lucene Sort in order to sort the results

org.hibernate.search.FullTextQuery query = s.createFullTextQuery( query, Book.class );
org.apache.lucene.search.Sort sort = new Sort(new SortField("title", SortField.STRING));
query.setSort(sort);
List results = query.list();


Tip



注意需要排序的域是不能被标注为分词的( tokenized )

Example 5.8. Specifying FetchMode on a query

Criteria criteria =     s.createCriteria( Book.class ).setFetchMode( "authors", FetchMode.JOIN );
s.createFullTextQuery( luceneQuery ).setCriteriaQuery( criteria );
上面的例子将返回所有luceneQuery匹配的Books,authors将被作为外部链接加载。

Important

只有设置fetch mode才可以使用criteria的restriction

Important

如果返回多个不同类型实体,则不能使用setCriteriaQuery 

有些时候不需要返回整个实体模型,而仅仅是实体中的部分字段。 Hibernate Search 允许你这样做,即返回几个字段。

Example 5.9. Using projection instead of returning the full domain object

org.hibernate.search.FullTextQuery query =     s.createFullTextQuery( luceneQuery, Book.class );
query.setProjection( "id", "summary", "body", "mainAuthor.name" );
List results = query.list();
Object[] firstResult = (Object[]) results.get(0);
Integer id = firstResult[0];
String summary = firstResult[1];
String body = firstResult[2];
String authorName = firstResult[3];
设置hibernate search先从二级缓存取实体还是先从database中取:

Example 5.11. Check the second-level cache before using a query

FullTextQuery query = session.createFullTextQuery(luceneQuery, User.class);query.initializeObjectWith(    ObjectLookupMethod.SECOND_LEVEL_CACHE,    DatabaseRetrievalMethod.QUERY);
  • ObjectLookupMethod.PERSISTENCE_CONTEXT: useful if most of the matching entities are already in the persistence context (ie loaded in the Session or EntityManager)

  • ObjectLookupMethod.SECOND_LEVEL_CACHE: check first the persistence context and then the second-level cache.

使用Hibernate Search进行全文检索时,你可以使用下面两种方式限制每次查询的时间:

?       当限定时间到时抛出异常

?       当限定时间到时限制查询结果的个数。(EXPERIMENTAL

两种方式不兼容。

一旦建立了Hibernate Search query.执行查询操作就像执行HQL,Criteria查询一样, list()uniqueResult()iterate()scroll()
如果需要返回特定结果,(比如利用分页),并且希望所有查询结果都运用该规则,推荐 list() or uniqueResult()。
list()可以设置batch-size。当使用 list() , uniqueResult() and iterate()时, 注意hibernate search会处理所有Lucene匹配的索引(包括分页)

如果你希望尽量少去加载lucene document,scroll非常适合。别忘了使用完关闭ScrollableResults对象
有时候我们需要知道搜到到的结果集数量
将所有匹配到的lucene document都取出来肯定会损耗很多资源。
hibernate search允许获取所有匹配到的索引document,即使你设置了分页参数,.更有趣的是,支持获取所有索引个数,而不需要加搜索条件

Example 5.16. Determining the result size of a query

org.hibernate.search.FullTextQuery query =     s.createFullTextQuery( luceneQuery, Book.class );
//return the number of matching books without loading a single one
assert 3245 == query.getResultSize(); 

org.hibernate.search.FullTextQuery query =     s.createFullTextQuery( luceneQuery, Book.class );
query.setMaxResult(10);List results = query.list();
//return the total number of matching books regardless of pagination
assert 3245 == query.getResultSize(); 

Note

就像Google,搜索结果数量只是个大概,如果有索引还没有被更新添加

就像 Section 5.1.3.5, “Projection”章节看到的投影结果就是返回成一个Object数组。
但有时候这样的数据结构不是我们想要的,那么我们可以转换:

Example 5.17. Using ResultTransformer in conjunction with projections

org.hibernate.search.FullTextQuery query =     s.createFullTextQuery( luceneQuery, Book.class );
query.setProjection( "title", "mainAuthor.name" );
query.setResultTransformer( 
    new StaticAliasToBeanResultTransformer( 
        BookView.class, 
        "title", 
        "author" ) 
);
List<BookView> results = (List<BookView>) query.list();
for(BookView view : results) {    
log.info( "Book: " + view.getTitle() + ", " + view.getAuthor() );
}
上面的例子,将投影的两个域title,mainAuthor.name,利用ResultTransformaer封装成BookView(tile,author)类.
有时候我们查询得到的结果不是我们想要的,比如返回空结果或者乱七八糟,我们可以利用
luke来调试。但是hibernate search也提供一个操作lucene解释类(  Explanation object )的方法。
第一个方式使用ducument id作为参数、获得Explanation对象。document id 可以通过projection或者
FullTextQuery.DOCUMENT_ID

Warning

Document ID 和 实体类的ID不是同一个东西

第二个方法:利用FullTextQuery.EXPLANATION常量

Example 5.18. Retrieving the Lucene Explanation object using projection

FullTextQuery ftQuery = s.createFullTextQuery( luceneQuery, Dvd.class )
        .setProjection(
              FullTextQuery.DOCUMENT_ID,
              FullTextQuery.EXPLANATION,
              FullTextQuery.THIS );
@SuppressWarnings("unchecked") List<Object[]> results = ftQuery.list();
for (Object[] result : results) {
    Explanation e = (Explanation) result[1];
    display( e.toString() );
}
注意,在使用explanation对象的时候,会粗略、损耗性大的再跑一遍与lucene query。所以必须的
时候再使用这个。

apache lucene允许使用filter过滤器过滤查询结果,也支持自定义的过滤器。应用例子:
Hibernate Search过滤器类似Hibernate过滤器:

Example 5.19. Enabling fulltext filters for a given query

fullTextQuery = s.createFullTextQuery( query, Driver.class );
fullTextQuery.enableFullTextFilter("bestDriver");
fullTextQuery.enableFullTextFilter("security").setParameter( "login", "andre" );
fullTextQuery.list(); //returns only best drivers where andre has credentials
上面的例子中我们启用了两个过滤器。

通过@FullTextFilterDef标注声明过滤器。过滤器可以标注在任何被@Indexed的实体类。
过滤器必须实现filter的函数

Example 5.20. Defining and implementing a Filter

@Entity
@Indexed
@FullTextFilterDefs( {
    @FullTextFilterDef(name = "bestDriver", impl = BestDriversFilter.class), 
    @FullTextFilterDef(name = "security", impl = SecurityFilterFactory.class) 
})
public class Driver { ... }
public class BestDriversFilter extends org.apache.lucene.search.Filter {

    public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
        OpenBitSet bitSet = new OpenBitSet( reader.maxDoc() );
        TermDocs termDocs = reader.termDocs( new Term( "score", "5" ) );
        while ( termDocs.next() ) {
            bitSet.set( termDocs.doc() );        }        return bitSet;    }}
下步意义。
 
 
目录
相关文章
|
资源调度
There appears to be trouble with your network connection.Retrying
There appears to be trouble with your network connection.Retrying
969 0
There appears to be trouble with your network connection.Retrying
|
Java API
解决办法:access restriction is not accessible due to restriction
解决办法:access restriction is not accessible due to restriction
100 0
|
前端开发
Warning: This synthetic event is reused for performance reasons.
Warning: This synthetic event is reused for performance reasons.
456 0
Warning: This synthetic event is reused for performance reasons.
|
容器
Lead creation performance
Lead creation performance
107 0
How to get information of all attachments belonging to a given appointment
How to get information of all attachments belonging to a given appointment
How to get information of all attachments belonging to a given appointment
automatic asynchronous creation if no note exists
Created by Wang, Jerry, last modified on May 12, 2015
104 0
automatic asynchronous creation if no note exists
|
SQL Web App开发 监控