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:
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
-
Start building the query as usual.
-
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.
-
Fetch the results, which will have the expected type.
Alternatively, if you don’t want to use lambdas:
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
Note
|
There are a few constraints regarding |
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.
The documentReference
projection returns a reference to the matched document as a DocumentReference
object.
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.
|
The entityReference
projection returns a reference to the matched entity as an EntityReference
object.
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
|
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].
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
:
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
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. |
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.
|
The field
projection returns the value of a given field for the matched document.
In order for the field
projection to be available on a given field,
you need to mark the field as projectable in the mapping.
By default, the field
projection returns a single value per document,
so the code below will be enough for a single-valued field:
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
:
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
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.
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
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:
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
See [search-dsl-projected-value-type] for more information.
The score
projection returns the score of the matched document.
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. |
The distance
projection returns the distance between a given point
and the geo-point value of a given field for the matched document.
In order for the distance
projection to be available on a given field,
you need to mark the field as projectable in the mapping.
By default, the distance
projection returns a single value per document,
so the code below will be enough for a single-valued field:
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:
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
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>
.
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
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.
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
-
Call
.composite(…)
. -
Use the constructor of a custom object,
MyPair
, as the combining function. -
Define the first projection to combine as a projection on the
title
field, meaning the constructor ofMyPair
will be called for each matched document with the value of thetitle
field as its first argument. -
Define the second projection to combine as a projection on the
genre
field, meaning the constructor ofMyPair
will be called for each matched document with the value of thegenre
field as its second argument. -
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:
List
of projected valueslink:{sourcedir}/org/hibernate/search/documentation/search/projection/ProjectionDslIT.java[role=include]
-
Call
.composite(…)
. -
Define the first projection to combine as a projection on the
title
field, meaning the hits will beList
instances with the value of thetitle
field of the matched document at index0
. -
Define the second projection to combine as a projection on the
genre
field, meaning the hits will beList
instances with the value of thegenre
field of the matched document at index1
. -
The hits will be
List
instances holding the result of the given projections, in the given order, for each matched document.
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. |
The .document()
projection returns the matched document as a native Lucene Document
.
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:
|
The .explanation()
projection returns an explanation
of the match as a native Lucene Explanation
.
org.apache.lucene.search.Explanation
link:{sourcedir}/org/hibernate/search/documentation/search/projection/LuceneProjectionDslIT.java[role=include]
The .source()
projection returns the JSON of the document as it was indexed in Elasticsearch,
as a JsonObject
.
JsonObject
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ElasticsearchProjectionDslIT.java[role=include]
The .explanation()
projection returns an explanation
of the match as a JsonObject
.
JsonObject
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ElasticsearchProjectionDslIT.java[role=include]
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. |
JsonObject
link:{sourcedir}/org/hibernate/search/documentation/search/projection/ElasticsearchProjectionDslIT.java[role=include]