The DSLs in Search 6 are relatively verbose, even when using the lambda syntax:
{code}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(); {code} 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:
{code}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(); {code} 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:
{code}this.accessor = root.field( "geoPoint_1", f -> f.asGeoPoint().toIndexFieldType() ) .createAccessor();{code} With the same solution as above, we would get to something like this:
{code}this.accessor = root.field( "geoPoint_1", f -> f.asGeoPoint() ) .createAccessor();{code} We could improve this even further by removing the {{createAccessor}} call:
{code}this.accessor = root.field( "geoPoint_1", f -> f.asGeoPoint() );{code} … 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:
{code}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() );{code} 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:
{code}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...{code}
Also to be taken into account: we may want one day to add "sub-fields" to non-object fields; see HSEARCH-3465. |
|