Uploaded image for project: 'Hibernate Search'
  1. HSEARCH-3444

Reduce the verbosity of the predicate/projection/sort DSLs and index schema DSL

    Details

    • Type: Improvement
    • Status: Resolved
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 6.0.0.Alpha3
    • Component/s: engine
    • Labels:
      None

      Description

      The DSLs in Search 6 are relatively verbose, even when using the lambda syntax:

      FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager( entityManager );
      
      FullTextQuery<MyProjectionBean> query = fullTextEntityManager.search( Book.class ).query()
                      .asProjection( f ->
                                       f.composite(
                                                      MyProjectionBean::new,
                                                      f.field( "id_stored", Long.class )
                                                      f.field( "title", String.class )
                                      ).toProjection()
                      )
                      .predicate( f -> f.match()
                              .onFields( "title", "authors.name" )
                              .matching( "Refactoring: Improving the Design of Existing Code" )
                              .toPredicate()
                      )
                      .sort( f -> f.byField( "title" ).asc()
                              .then().byField( "subtitle" ).asc()
                              .toSort()
                      )
                      .build();
      
      List<MyProjectionBean> result = query.getResultList();
      

      We could try to improve on that.
      One idea would be to remove the need to call the toPredicate, toProjection, toSort‌and toIndexFieldType methods even at the top level:

      FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager( entityManager );
      
      FullTextQuery<MyProjectionBean> query = fullTextEntityManager.search( Book.class ).query()
                      .asProjection( f -> f.composite(
                              MyProjectionBean::new,
                              f.field( "id_stored", Long.class )
                              f.field( "title", String.class )
                       ) )
                      .predicate( f -> f.match()
                              .onFields( "title", "authors.name" )
                              .matching( "Refactoring: Improving the Design of Existing Code" )
                      )
                      .sort( f -> f.byField( "title" ).asc()
                              .then().byField( "subtitle" ).asc()
                      )
                      .build();
      
      List<MyProjectionBean> result = query.getResultList();
      

      That would, however, remove the ability to return a cached SearchPredicate/SearchProjection/SearchSort object from the lambda: the lambda would be expected to return a Search(Predicate/Projection/Sort)TerminalContext.

      Maybe we should add a method to the DSL to convert a SearchPredicate/SearchProjection/SearchSort to a Search(Predicate/Projection/Sort)TerminalContext? Something like f.from(searchPredicate)? If we do that, it could make sense to change the naming around SearchPredicate/SearchProjection/SearchSort: we could make it more obvious that these are mainly for re-use, and are not really necessary if you don't cache them. Maybe rename them to ReusablePredicate, or something similar?

      Note that the index schema DSL also suffers from unnecessary verbosity:

      this.accessor = root.field( "geoPoint_1", f -> f.asGeoPoint().toIndexFieldType() )
            .createAccessor();

      With the same solution as above, we would get to something like this:

      this.accessor = root.field( "geoPoint_1", f -> f.asGeoPoint() )
            .createAccessor();

      We could improve this even further by removing the createAccessor call:

      this.accessor = root.field( "geoPoint_1", f -> f.asGeoPoint() );

      … but then there would be an inconsistency with object fields, where the‌createAccessor call is actually necessary, because the user needs a builder to add sub-fields to the object field:

      IndexSchemaObjectField objectField = context.getIndexSchemaElement().objectField( "object" );
      this.objectFieldAccessor = objectField.createAccessor();
      this.subField1Accessor = objectField.field( "subField1", f -> f.asString() );
      this.subField2Accessor = objectField.field( "subField2", f -> f.asInteger() );

      Maybe we should just acknowledge this inconsistency by considering the objectField object above as a builder? Then it would make more sense: when you use a builder, you expect a final call. Something like this maybe:

      IndexSchemaObjectField objectFieldBuilder = context.getIndexSchemaElement().objectFieldBuilder( "object" );
      this.subField1Accessor = objectFieldBuilder.field( "subField1", f -> f.asString() );
      this.subField2Accessor = objectFieldBuilder.field( "subField2", f -> f.asInteger() );
      this.objectFieldAccessor = objectFieldBuilder.build(); // or ".createAccessor()"? It seems odd with a builder...

      Also to be taken into account: we may want one day to add "sub-fields" to non-object fields; see HSEARCH-3465 Open .

        Attachments

          Issue links

            Activity

              People

              • Assignee:
                yrodiere Yoann Rodière
                Reporter:
                yrodiere Yoann Rodière
              • Votes:
                0 Vote for this issue
                Watchers:
                1 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: