Skip to content

Commit

Permalink
HSEARCH-3628 Document fetch graphs and load graphs in search queries
Browse files Browse the repository at this point in the history
  • Loading branch information
yrodiere committed May 26, 2020
1 parent 71c4cb9 commit 6fa684d
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 2 deletions.
45 changes: 43 additions & 2 deletions documentation/src/main/asciidoc/search-dsl-query.asciidoc
Expand Up @@ -391,9 +391,50 @@ etc.
// Search 5 anchors backward compatibility
[[_fetching_strategy]]

include::todo-placeholder.asciidoc[]
By default, Hibernate Search will load associations according to the defaults of your mappings:
associations marked as lazy won't be loaded,
while associations marked as eager will be loaded before returning the entities.

It is possible to force the loading of a lazy association, or to prevent the loading of an eager association,
by referencing an entity graph in the query.
See below for an example, and
link:{hibernateDocUrl}#fetching-strategies-dynamic-fetching-entity-graph[this section of the Hibernate ORM documentation]
for more information about entity graphs.

.Applying an entity graph to a search query
====
[source, JAVA, indent=0, subs="+callouts"]
----
include::{sourcedir}/org/hibernate/search/documentation/search/query/QueryDslIT.java[tags=graph-byReference]
----
<1> Build an entity graph.
<2> Start building the query.
<3> Access the loading options of the query,
then set the entity graph to the graph built above.
You must also pass a semantic: `GraphSemantic.FETCH` means only associations referenced in the graph will be loaded;
`GraphSemantic.LOAD` means associations referenced in the graph *and* associations marked as `EAGER` in the mapping will be loaded.
<4> Fetch the results.
All managers loaded by this search query will have their `associates` association already populated.
====

Instead of building the entity graph on the spot,
you can also define the entity graph statically using the `@NamedEntityGraph` annotation,
and pass the name of your graph to Hibernate Search, as shown below.
See link:{hibernateDocUrl}#fetching-strategies-dynamic-fetching-entity-graph[this section of the Hibernate ORM documentation]
for more information about `@NamedEntityGraph`.

// TODO HSEARCH-3628 https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_fetching_strategy
.Applying a named entity graph to a search query
====
[source, JAVA, indent=0, subs="+callouts"]
----
include::{sourcedir}/org/hibernate/search/documentation/search/query/QueryDslIT.java[tags=graph-byName]
----
<1> Start building the query.
<2> Access the loading options of the query,
then set the entity graph to "preload-associates", which was defined elsewhere using the `@NamedEntityGraph` annotation.
<3> Fetch the results.
All managers loaded by this search query will have their `associates` association already populated.
====

[[search-dsl-query-timeout]]
== Timeout
Expand Down
Expand Up @@ -10,6 +10,8 @@
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.OneToMany;
import javax.persistence.OrderColumn;

Expand All @@ -19,6 +21,10 @@

@Entity
@Indexed
@NamedEntityGraph(
name = "preload-associates",
attributeNodes = @NamedAttributeNode("associates")
)
public class Manager implements Person {

@Id
Expand Down
Expand Up @@ -7,6 +7,7 @@
package org.hibernate.search.documentation.search.query;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.search.util.impl.integrationtest.mapper.orm.ManagedAssert.assertThatManaged;

import java.time.Duration;
import java.util.Arrays;
Expand All @@ -15,11 +16,14 @@
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.SharedCacheMode;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.graph.GraphSemantic;
import org.hibernate.search.backend.elasticsearch.ElasticsearchExtension;
import org.hibernate.search.backend.elasticsearch.search.query.ElasticsearchSearchQuery;
import org.hibernate.search.backend.elasticsearch.search.query.ElasticsearchSearchResult;
Expand Down Expand Up @@ -478,6 +482,62 @@ public void fetchSize() {
} );
}

@Test
public void graph() {
// By default associates are not loaded
OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> {
SearchSession searchSession = Search.session( entityManager );

SearchResult<Manager> result = searchSession.search( Manager.class ) // <1>
.where( f -> f.match()
.field( "name" )
.matching( "james" ) )
.fetch( 20 ); // <3>

assertThat( result.hits() )
.isNotEmpty()
.allSatisfy( manager -> assertThatManaged( manager.getAssociates() ).isNotInitialized() );
} );

OrmUtils.withinJPATransaction( entityManagerFactory, theEntityManager -> {
// tag::graph-byReference[]
EntityManager entityManager = /* ... */
// end::graph-byReference[]
theEntityManager;
// tag::graph-byReference[]

EntityGraph<Manager> graph = entityManager.createEntityGraph( Manager.class ); // <1>
graph.addAttributeNodes( "associates" );

SearchResult<Manager> result = Search.session( entityManager ).search( Manager.class ) // <2>
.where( f -> f.match()
.field( "name" )
.matching( "james" ) )
.loading( o -> o.graph( graph, GraphSemantic.FETCH ) ) // <3>
.fetch( 20 ); // <4>
// end::graph-byReference[]

assertThat( result.hits() )
.isNotEmpty()
.allSatisfy( manager -> assertThatManaged( manager.getAssociates() ).isInitialized() );
} );

OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> {
// tag::graph-byName[]
SearchResult<Manager> result = Search.session( entityManager ).search( Manager.class ) // <1>
.where( f -> f.match()
.field( "name" )
.matching( "james" ) )
.loading( o -> o.graph( "preload-associates", GraphSemantic.FETCH ) ) // <2>
.fetch( 20 ); // <3>
// end::graph-byName[]

assertThat( result.hits() )
.isNotEmpty()
.allSatisfy( manager -> assertThatManaged( manager.getAssociates() ).isInitialized() );
} );
}

@Test
public void json_elasticsearch() {
Assume.assumeTrue( backendConfiguration instanceof ElasticsearchBackendConfiguration );
Expand Down

0 comments on commit 6fa684d

Please sign in to comment.