将嵌套的 Hibernate Search 5 谓词转换为 Hibernate Search 6

Converting nested Hibernate Search 5 predicates to Hibernate Search 6

在将应用程序从 Hibernate Search 5 转换为 6 的过程中。我通读了很多文档,尤其是 https://docs.jboss.org/hibernate/search/6.0/migration/html_single/#queries-reference 关于如何转换查询 DSL,我在一个简单的场景中理解它,但是你如何将谓词嵌套到其他嵌套谓词中,然后将它们与其他谓词组合在更大的查询中?
在 Hibernate Search 5 中,我们将使用 queryBuilder.bool() 创建一个 BooleanJunction,然后您可以通过调用 bool 上的 createQuery 将其添加到另一个 BooleanJunction 中,并一遍又一遍地创建嵌套谓词查询。

我要转换的代码类型示例:

    BooleanJunction vendorNameBool = queryBuilder.bool();
    BooleanJunction nameBool = queryBuilder.bool();
    
    nameBool.must(
        qb.keyword()
            .onField(CompanyName)
            .matching(nameToken1)
            .createQuery()
    );
    
    nameBool.must(
        qb.keyword()
            .onField(CompanyName)
            .matching(nameToken2)
            .createQuery()
    );

    vendorNameBool.should(nameBool.createQuery);
    
    // do vendorNameBool.should(...) for as many vendor Names that exist, then createQuery 
    probableVendorNamesQuery = vendorNameBool.createQuery();
    
    // creating a number of Queries from various bools and then combining them:
    Query taxIdOrVendorNameOrPhoneNumberQuery = qb.bool()
        .should(probableVendorNamesQuery)
        .should(taxIdQuery)
        .should(phoneNumberQuery)
        .createQuery();
        
    //and add to the final BooleanQuery along with other Query pieces
    
    Query idQuery = getIdQuery();
    Query fileIdQuery = getFileIdQuery();
    
    BooleanQuery.Builder theQuery = new BooleanQuery.Builder();
    theQuery.add(taxIdOrVendorNameOrPhoneNumberQuery, MUST);
    theQuery.add(fileIdQuery, MUST);    
    theQuery.add(idQuery, MUST_NOT);    
    
    BooleanQuery probableQuery = theQuery.build();
     
    // add some projections and execute query
 

大部分 HS6 代码示例都是 lambda 形式。有一个部分 here 提供了一个创建非 lamba 谓词的简单示例,将它们添加到列表中,但是,例如,您如何将该谓词列表添加到外部 should 子句,然后将 should 子句与另一个 should 子句一起添加到外部“必须”子句等.等等?

就我个人而言,我会使用 lambda 语法并嵌套第二个 lambda。改编迁移指南中的示例:

 MySearchParameters searchParameters = ...;
 SearchSession session = Search.session( entityManager );
 List<Book> hits = searchSession.search( Book.class )
         .where( f -> f.bool( b -> {
             b.must( f.matchAll() );
             if ( searchParameters.getSearchTerms() != null ) {
                 b.must( f.simpleQueryString().fields( "title", "description" )
                         .matching( searchParameters.getSearchTerms() )
                         .defaultOperator( BooleanOperator.AND ) );
             }
             // ...
             // BEGIN NEW CODE
             SomeComplexParameter complexParam = searchParameters.getSomeComplexParameter();
             if ( complexParam != null ) {
                 b.must( f.bool( b2 -> {
                     b2.should( f.match().field( "someField1" )
                         .matching( complexParam.getSomeField1() ) );
                     b2.should( f.match().field( "someField2" )
                         .matching( complexParam.getSomeField2() ) );
                 } ) );
             }
             // END NEW CODE
         } ) )
         .fetchHits( params.getPageIndex() * params.getPageSize(), params.getPageSize() );

如果你在编译时知道子句的数量,你甚至不需要第二个 lambda:

 MySearchParameters searchParameters = ...;
 SearchSession session = Search.session( entityManager );
 List<Book> hits = searchSession.search( Book.class )
         .where( f -> f.bool( b -> {
             b.must( f.matchAll() );
             if ( searchParameters.getSearchTerms() != null ) {
                 b.must( f.simpleQueryString().fields( "title", "description" )
                         .matching( searchParameters.getSearchTerms() )
                         .defaultOperator( BooleanOperator.AND ) );
             }
             // ...
             // BEGIN NEW CODE
             SomeComplexParameter complexParam = searchParameters.getSomeComplexParameter();
             if ( complexParam != null ) {
                 b.must( f.bool()
                         .should( f.match().field( "someField1" )
                             .matching( complexParam.getSomeField1() ) )
                         .should( f.match().field( "someField2" )
                             .matching( complexParam.getSomeField2() ) ) );
             }
             // END NEW CODE
         } ) )
         .fetchHits( params.getPageIndex() * params.getPageSize(), params.getPageSize() );

如果您真的不想在顶层使用 lambda(为什么?),您可以将 lambda 用于嵌套谓词,至少:

MySearchParameters searchParameters = ...;
SearchSession session = Search.session( entityManager );
SearchPredicateFactory pf = session.scope( Book.class ).predicate();
List<SearchPredicate> predicates = new ArrayList<>();

if ( searchParameters.getSearchTerms() != null ) {
    predicates.add( pf.simpleQueryString().fields( "title", "description" )
            .matching( searchParameters.getSearchTerms() )
            .defaultOperator( BooleanOperator.AND )
            .toPredicate() );
}

// ...

// BEGIN NEW CODE
SomeComplexParameter complexParam = searchParameters.getSomeComplexParameter();
if ( complexParam != null ) {
    predicates.add( pf.bool( b -> {
        b.should( pf.match().field( "someField1" )
            .matching( complexParam.getSomeField1() ) );
        b.should( pf.match().field( "someField2" )
            .matching( complexParam.getSomeField2() ) );
    } )
            .toPredicate() );
}
// END NEW CODE

List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.bool( b -> {
            b.must( f.matchAll() );
            for ( SearchPredicate predicate : predicates ) {
                b.must( predicate );
            }
        } )

同样,只要您事先知道谓词的数量,就不需要 lambda:

MySearchParameters searchParameters = ...;
SearchSession session = Search.session( entityManager );
SearchPredicateFactory pf = session.scope( Book.class ).predicate();
List<SearchPredicate> predicates = new ArrayList<>();

if ( searchParameters.getSearchTerms() != null ) {
    predicates.add( pf.simpleQueryString().fields( "title", "description" )
            .matching( searchParameters.getSearchTerms() )
            .defaultOperator( BooleanOperator.AND )
            .toPredicate() );
}

// ...

// BEGIN NEW CODE
SomeComplexParameter complexParam = searchParameters.getSomeComplexParameter();
if ( complexParam != null ) {
    predicates.add( pf.bool()
            .should( pf.match().field( "someField1" )
                .matching( complexParam.getSomeField1() ) )
            .should( pf.match().field( "someField2" )
                 .matching( complexParam.getSomeField2() ) )
            .toPredicate() );
}
// END NEW CODE

List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.bool( b -> {
            b.must( f.matchAll() );
            for ( SearchPredicate predicate : predicates ) {
                b.must( predicate );
            }
        } )
        .fetchHits( params.getPageIndex() * params.getPageSize(), params.getPageSize() );

最后,如果您真的想完全远离 lambda(但又是为什么?),您或许可以这样做。请注意,BooleanPredicateClausesStep 的泛型类型参数可能会在 Hibernate Search 的次要版本中更改,因此此代码在升级时更有可能中断。

MySearchParameters searchParameters = ...;
SearchSession session = Search.session( entityManager );
SearchPredicateFactory pf = session.scope( Book.class ).predicate();
BooleanPredicateClausesStep<?> boolStep = pf.bool();

boolStep.must( f.matchAll() );

if ( searchParameters.getSearchTerms() != null ) {
    boolStep.must( pf.simpleQueryString().fields( "title", "description" )
            .matching( searchParameters.getSearchTerms() )
            .defaultOperator( BooleanOperator.AND ) );
}

// ...

SomeComplexParameter complexParam = searchParameters.getSomeComplexParameter();
if ( complexParam != null ) {
    BooleanPredicateClausesStep<?> boolStep2 = pf.bool();
    boolStep2.should( f.match().field( "someField1" )
            .matching( complexParam.getSomeField1() ) );
    boolStep2.should( f.match().field( "someField2" )
            .matching( complexParam.getSomeField2() ) );
    boolStep.must( boolStep2 );
}

SearchPredicate boolPredicate = boolStep.toPredicate();

List<Book> hits = searchSession.search( Book.class )
        .where( boolPredicate )
        .fetchHits( params.getPageIndex() * params.getPageSize(), params.getPageSize() );

如果您使用 JDK 11 进行编译,更可靠的解决方案是使用 var 关键字:

MySearchParameters searchParameters = ...;
SearchSession session = Search.session( entityManager );
SearchPredicateFactory pf = session.scope( Book.class ).predicate();
var boolStep = pf.bool();

boolStep.must( f.matchAll() );

if ( searchParameters.getSearchTerms() != null ) {
    boolStep.must( pf.simpleQueryString().fields( "title", "description" )
            .matching( searchParameters.getSearchTerms() )
            .defaultOperator( BooleanOperator.AND ) );
}

// ...

SomeComplexParameter complexParam = searchParameters.getSomeComplexParameter();
if ( complexParam != null ) {
    var boolStep2 = pf.bool();
    boolStep2.should( f.match().field( "someField1" )
            .matching( complexParam.getSomeField1() ) );
    boolStep2.should( f.match().field( "someField2" )
            .matching( complexParam.getSomeField2() ) );
    boolStep.must( boolStep2 );
}

SearchPredicate boolPredicate = boolStep.toPredicate();

List<Book> hits = searchSession.search( Book.class )
        .where( boolPredicate )
        .fetchHits( params.getPageIndex() * params.getPageSize(), params.getPageSize() );