Skip to content

Latest commit

 

History

History
447 lines (348 loc) · 16.7 KB

search-dsl-projection.asciidoc

File metadata and controls

447 lines (348 loc) · 16.7 KB

Projection DSL

Basics

For some use cases, you only need the query to return a small subset of the data contained in your domain object. In these cases, returning managed entities and extracting data from these entities may be overkill: extracting the data from the index itself would avoid the database round-trip.

Projections do just that: they allow the query to return something more precise than just "the matching entities". Projections can be configured when building the search query:

Example 1. Using projections to extract data from the index
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
  1. Start building the query as usual.

  2. Mention that the expected result of the query is a projection on field "title", of type String. If that type is not appropriate or if the field does not exist, an exception will be thrown.

  3. Fetch the results, which will have the expected type.

Alternatively, if you don’t want to use lambdas:

Example 2. Using projections to extract data from the index — object-based syntax
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
Note

There are a few constraints regarding field projections. In particular, in order for a field to be "projectable", it must be marked as such in the mapping, so that it is correctly stored in the index.

While field projections are certainly the most common, they are not the only type of projection. Other projections allow to compose custom beans containing extracted data, get references to the extracted documents or the corresponding entities, or get information related to the search query itself (score, …​).

To learn more about the field projection, and all the other types of projection, refer to the following sections.

documentReference: return references to matched documents

The documentReference projection returns a reference to the matched document as a DocumentReference object.

Example 3. Returning references to matched documents
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
Note
Since it’s a reference to the document, not the entity, DocumentReference only exposes low-level concepts such as the type name and the document identifier (a String). Use the entityReference projection to get a reference to the entity.

entityReference: return references to matched entities

The entityReference projection returns a reference to the matched entity as an EntityReference object.

Example 4. Returning references to matched entities
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
Note
The entity does not get loaded as part of the projection. If you want the actual entity instance, use the entity projection

id: return identifiers of matched entities

The identifier projection returns the identifier of the matched entity. NOTE: If the provided identifier type does not match the type of identifiers for targeted entity types, an exception will be thrown. See also [search-dsl-projected-value-type].

Example 5. Returning ids to matched entities, providing the identity type.
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]

You can omit the "identifier type" argument, but then you will get projections of type Object:

Example 6. Returning ids to matched entities, without providing the identity type.
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]

entity: return matched entities

The entityReference projection returns the entity corresponding to the document that matched.

Note
With the Hibernate ORM integration, returned objects are managed entities loaded from the database. You can use them as you would use any entity returned from traditional Hibernate ORM queries.
Example 7. Returning matched entities loaded from the database
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
Note
If an entity cannot be loaded (e.g. it was deleted and the index wasn’t updated yet), the hit will be omitted and won’t appear in the returned List at all. The total hit count, however, will not take this omission into account.

field: return field values from matched documents

The field projection returns the value of a given field for the matched document.

Prerequisites

In order for the field projection to be available on a given field, you need to mark the field as projectable in the mapping.

Syntax

By default, the field projection returns a single value per document, so the code below will be enough for a single-valued field:

Example 8. Returning field values from matched documents
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
Note
Hibernate Search will throw an exception when building the query if you do this on a multi-valued field. To project on multi-valued fields, see Multi-valued fields.

You can omit the "field type" argument, but then you will get projections of type Object:

Example 9. Returning field values from matched documents, without specifying the field type
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]

Multi-valued fields

To return multiple values, and thus allow projection on multi-valued fields, use .multi(). This will change the return type of the projection to List<T> where T is what the single-valued projection would have returned.

Example 10. Returning field values from matched documents, for multi-valued fields
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]

Skipping conversion

By default, the values returned by the field projection have the same type as the entity property corresponding to the target field.

For example, if an entity property if of an enum type, the corresponding field may be of type String; the values returned by the field projection will be of the enum type regardless.

This should generally be what you want, but if you ever need to bypass conversion and have unconverted values returned to you instead (of type String in the example above), you can do it this way:

Example 11. Returning field values from matched documents, without converting the field value
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]

See [search-dsl-projected-value-type] for more information.

score: return the score of matched documents

The score projection returns the score of the matched document.

Example 12. Returning the score of matched documents
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
Warning

Two scores can only be reliably compared if they were computed during the very same query execution. Trying to compare scores from two separate query executions will only lead to confusing results, in particular if the predicates are different or if the content of the index changed enough to alter the frequency of some terms significantly.

On a related note, exposing scores to end users is generally not an easy task. See this article for some insight into what’s wrong with displaying the score as a percentage, specifically.

distance: return the distance to a point

The distance projection returns the distance between a given point and the geo-point value of a given field for the matched document.

Prerequisites

In order for the distance projection to be available on a given field, you need to mark the field as projectable in the mapping.

Syntax

By default, the distance projection returns a single value per document, so the code below will be enough for a single-valued field:

Example 13. Returning the distance to a point
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
Note
Hibernate Search will throw an exception when building the query if you do this on a multi-valued field. To project on multi-valued fields, see Multi-valued fields.

The returned distance is in meters by default, but you can pick a different unit:

Example 14. Returning the distance to a point with a given distance unit
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]

Multi-valued fields

To return multiple values, and thus allow projection on multi-valued fields, use .multi(). This will change the return type of the projection to List<Double>.

Example 15. Returning the distance to a point, for multi-valued fields
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]

composite: combine projections

The composite projection applies multiple projections and combines their results.

To preserve type-safety, you can provide a custom combining function. The combining function can be a Function, a BiFunction, or a org.hibernate.search.util.common.function.TriFunction. It will receive values returned by inner projections and return an object combining these values.

Depending on the type of function, either one, two, or three additional arguments are expected, one for each inner projection.

Example 16. Returning custom objects created from multiple projected values
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
  1. Call .composite(…​).

  2. Use the constructor of a custom object, MyPair, as the combining function.

  3. Define the first projection to combine as a projection on the title field, meaning the constructor of MyPair will be called for each matched document with the value of the title field as its first argument.

  4. Define the second projection to combine as a projection on the genre field, meaning the constructor of MyPair will be called for each matched document with the value of the genre field as its second argument.

  5. The hits will be the result of calling the combining function for each matched document, in this case MyPair instances.

If you need more inner projections, or simply if you don’t mind receiving the result of inner projections as a List<?>, you can use the variant of .composite(…​) that doesn’t expect a function argument:

Example 17. Returning a List of projected values
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
  1. Call .composite(…​).

  2. Define the first projection to combine as a projection on the title field, meaning the hits will be List instances with the value of the title field of the matched document at index 0.

  3. Define the second projection to combine as a projection on the genre field, meaning the hits will be List instances with the value of the genre field of the matched document at index 1.

  4. The hits will be List instances holding the result of the given projections, in the given order, for each matched document.

Backend-specific extensions

By calling .extension(…​) while building a query, it is possible to access backend-specific projections.

Note

As their name suggests, backend-specific projections are not portable from one backend technology to the other.

Lucene: document

The .document() projection returns the matched document as a native Lucene Document.

Example 18. Returning the matched document as a native org.apache.lucene.document.Document
link:{sourcedir}/org/hibernate/search/documentation/search/projection/LuceneProjectionDslIT.java[role=include]
Note

The returned document is not exactly the one that was indexed.

In particular:

  • Only stored fields are present.

  • Even stored fields may not have the same FieldType as they originally had.

  • The document structure flattened, i.e. even fields from nested documents are all added to same returned document.

  • Dynamic fields may be missing.

Lucene: explanation

The .explanation() projection returns an explanation of the match as a native Lucene Explanation.

Example 19. Returning the score explanation as a native org.apache.lucene.search.Explanation
link:{sourcedir}/org/hibernate/search/documentation/search/projection/LuceneProjectionDslIT.java[role=include]

Elasticsearch: source

The .source() projection returns the JSON of the document as it was indexed in Elasticsearch, as a JsonObject.

Example 20. Returning the matched document source as a JsonObject
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ElasticsearchProjectionDslIT.java[role=include]

Elasticsearch: explanation

The .explanation() projection returns an explanation of the match as a JsonObject.

Example 21. Returning the score explanation as a JsonObject
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ElasticsearchProjectionDslIT.java[role=include]

Elasticsearch: jsonHit

The .jsonHit() projection returns the exact JSON returned by Elasticsearch for the hit, as a JsonObject.

Note

This is particularly useful when customizing the request’s JSON to ask for additional data within each hit.

Example 22. Returning the Elasticsearch hit as a JsonObject
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ElasticsearchProjectionDslIT.java[role=include]