[hibernate-issues] [JIRA] (HSEARCH-3325) Search 6 groundwork - Restore support for full-text filters

Yoann Rodière (JIRA) jira at hibernate.atlassian.net
Tue Mar 17 04:15:24 EDT 2020


Yoann Rodière ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%3A58fa1ced-171a-4c00-97e8-5d70d442cc4b ) *commented* on HSEARCH-3325 ( https://hibernate.atlassian.net/browse/HSEARCH-3325?atlOrigin=eyJpIjoiODVlNmNiZGY0NjhmNGYzYzhmYTFmNjdiMDlkY2U1NjkiLCJwIjoiaiJ9 )

Re: Search 6 groundwork - Restore support for full-text filters ( https://hibernate.atlassian.net/browse/HSEARCH-3325?atlOrigin=eyJpIjoiODVlNmNiZGY0NjhmNGYzYzhmYTFmNjdiMDlkY2U1NjkiLCJwIjoiaiJ9 )

I can conceive that a feature that was present in Search 5 would still be useful in Search 6. However, the feature as you implemented it is not the same as the one that was present in Search 5:

* In Search 5, filters were applied *after* the query was built, as some kind of "mask", which was useful in particular for aspect-oriented programming (and incidentally caused the query to be mutable, which is not something we want in 6).
* In Search 5, there was no magic around the filter reference that would associate a filter to a single field ("prices.is-shared" contains both the field name and the filter name)
* In Search 5, the value of filters arguments was defined exclusively when applying a filter, not when defining the filter using the @FullTextFilterDef annotation.
* In Search 5, filters used to be cached, which doesn't seem useful in your case.
* And so on.

My point is, this is not the same feature as in Search 5. Thus, we can't just say "there was this feature in Search 5, we need to merge this patch". You're proposing essentially a new feature, or at least a heavily revamped feature, and I need to understand the use case.

> 
> 
> 
> And whether it is useful for someone should be left to the end user
> 
> 

True... let me put it another way: I need to know there is something you cannot achieve without this feature, or at least that would be complicated or time-consuming to achieve without this feature. I need to know there is value in this feature. Sometimes I can see it clearly, but here I need your help to understand.

You have to understand that even when you provide the code of a new feature, there is a cost for the maintainers. First when merging it to make sure everything is well tested and documented, and fix the code where appropriate, and later to keep the feature in working order when we modify some code it uses.

For the record, I spent more than one week, full-time, just testing, fixing and documenting your other PR about multi-valued sorts. That was time well spent, because I knew the feature is very useful. But still, that's a lot of time, and I cannot spend all my time on features without any idea of their value.

> 
> 
> 
> It's about simplifying your queries
> 
> 

Alright. Then I ask you, honestly: in what ways do you feel the code you gave is simpler than this, which doesn't require any additional API :

   private class BeansDataModel extends SearchDataModel<Assortment> {

       @Override
       public SearchQuery<Assortment> search(List<SortMeta> multiSortMeta, Map< String , Object > filters) {

           try {
               SearchSession session = Search.session(em);
               SearchScope<Assortment> scope = session.scope(Assortment.class);

               SearchSortFactory sorter = scope.sort();
               CompositeSortComponentsStep sort = sorter.composite();

               SearchPredicateFactory spf = scope.predicate();

               BooleanPredicateClausesStep pricesFilter = spf.bool()
                       .filter(isPriceActive(scope))
                       .filter(isPriceShared(scope, identity.getAccount()));

               sort.add(sorter.field( "prices.bruttoPrice_sort" )
                       .asc()
                       .mode(MultiValue.MIN)
                       .filter(pricesFilter));

               for (SortMeta sortMeta : multiSortMeta) {
                   String fieldName = sortMeta.getSortField();
                   switch (sortMeta.getSortOrder()) {
                       case ASCENDING:
                           sort.add(sorter.field(fieldName).asc());
                           break ;
                       case DESCENDING:
                           sort.add(sorter.field(fieldName).desc());
                           break ;
                   }
               }

               PredicateFinalStep select;
               if (searchFullText != null ) {
                   select = scope.predicate().simpleQueryString()
                           .field( "shoppingName" ).boost(0.5f)
                           .matching(searchFullText);
               } else {
                   select = scope.predicate()
                           .matchAll();
               }

               SearchQuery<Assortment> query = session.search(scope)
                       .where((f) -> f.bool().must(select)
                       .filter(f.nested()
                               .objectField( "prices" )
                               .nest(pricesFilter)
                       )).sort(sort.toSort()).toQuery();

               return query;
           } catch (Exception ex) {
               throw new IllegalStateException(ex);
           }
       }

   }

   private static SearchPredicate isPriceActive(SearchScope scope) {
       return PermissionsFilterFactory.create(scope, "prices" );
   }

   private static SearchPredicate isPriceShared(SearchScope scope, Account user) {
       return PricesActiveFilterFactory.create(scope, "prices.sharings" , user, "share" );
   }

@Entity(name = "Assortment" )
public class Assortment {
....

   @IndexedEmbedded(storage = ObjectFieldStorage.NESTED)
   @OneToMany(mappedBy = "assortment" , orphanRemoval = true , fetch = FetchType.EAGER, cascade = CascadeType.ALL)
   private List<AssortmentPrice> prices = new ArrayList<>();
...

@Entity(name = "AssortmentPrice" )
public class AssortmentPrice implements Serializable {
....
   @FullTextField(analyzer = "permission" )
   @Convert(converter = PermissionsConverter.class)
   @Column(name = "Sharings" , length = 2048)
   private Permissions sharings = new Permissions();

   @GenericField
   @Column(name = "Active" )
   private boolean active = true ;
}

public class PermissionsFilterFactory {

   public SearchPredicate create(SearchScope scope, String fieldPath, Account user, String operation) {
       SearchPredicate filter;
       SearchPredicateFactory predicate = scope.predicate();

       if (operation == null ) {
           operation = "read" ;
       }

       PermissionQuery query = new PermissionQuery(fieldPath, operation, user);
       filter = predicate.extension(LuceneExtension.get())
               .fromLuceneQuery(query)
               .toPredicate();

       return filter;
   }

}

public class PricesActiveFilterFactory {

   @Override
   public SearchPredicate create(SearchScope scope, String fieldPath) {
       SearchPredicate filter;

       filter = ctx.predicate()
               .match().field(fieldPath)
               .matching( true )
               .toPredicate();

       return filter;
   }

}

( https://hibernate.atlassian.net/browse/HSEARCH-3325#add-comment?atlOrigin=eyJpIjoiODVlNmNiZGY0NjhmNGYzYzhmYTFmNjdiMDlkY2U1NjkiLCJwIjoiaiJ9 ) Add Comment ( https://hibernate.atlassian.net/browse/HSEARCH-3325#add-comment?atlOrigin=eyJpIjoiODVlNmNiZGY0NjhmNGYzYzhmYTFmNjdiMDlkY2U1NjkiLCJwIjoiaiJ9 )

Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.core&referrer=utm_source%3DNotificationLink%26utm_medium%3DEmail ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailNotificationLink&mt=8 ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100122- sha1:87fe5bb )
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/hibernate-issues/attachments/20200317/125a77e9/attachment.html 


More information about the hibernate-issues mailing list