From 468c14640fef80bca7312e7ccdc3d0bd7f9ea090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 2 Sep 2019 14:13:12 +0200 Subject: [PATCH 1/7] HSEARCH-3615 Reorganize the Query DSL section of the documentation slightly --- .../src/main/asciidoc/search-dsl.asciidoc | 77 ++++++++++++++----- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/documentation/src/main/asciidoc/search-dsl.asciidoc b/documentation/src/main/asciidoc/search-dsl.asciidoc index 48c4de8ff1f..c7a15e29adf 100644 --- a/documentation/src/main/asciidoc/search-dsl.asciidoc +++ b/documentation/src/main/asciidoc/search-dsl.asciidoc @@ -67,14 +67,46 @@ to group hits and compute aggregated metrics for each group -- hit count by cate // Search 5 anchors backward compatibility [[_retrieving_the_results]] +[[search-dsl-query-fetching-results-basics]] +==== Basics +// Search 5 anchors backward compatibility +[[_result_size]] + +include::todo-placeholder.asciidoc[] + +// TODO +// 1. Fetching the top hits and other results (total hit count, aggregations, ...) +// 2. Fetching the top hits only +// 3. Fetching the only hit +// 4. Fetching the total hit count only + +[[search-dsl-query-fetching-results-all]] +==== Fetching all hits + +include::todo-placeholder.asciidoc[] + +// TODO fetching all hits (warning) + +[[search-dsl-query-fetching-results-pagination]] +==== Pagination +// Search 5 anchors backward compatibility +[[_pagination]] + include::todo-placeholder.asciidoc[] -// TODO [[_pagination]] https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_pagination -// TODO [[_performance_considerations]] https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_performance_considerations -// TODO [[_result_size]] https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_result_size +// TODO https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_pagination + +[[search-dsl-query-fetching-results-scrolling]] +==== Scrolling +// Search 5 anchors backward compatibility +[[_performance_considerations]] + +include::todo-placeholder.asciidoc[] + +// TODO https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_performance_considerations [[search-dsl-query-routing]] -==== Routing +=== Routing include::components/sharding-intro-note.asciidoc[] @@ -121,16 +153,6 @@ so we can specify the routing key to limit the query to relevant shards. <4> Build the query and fetch the results. ==== -[[search-dsl-query-timeout]] -=== Timeout -// Search 5 anchors backward compatibility -[[_limiting_the_time_of_a_query]] - -include::todo-placeholder.asciidoc[] - -// TODO [[_raise_an_exception_on_time_limit]] https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_raise_an_exception_on_time_limit -// TODO [[_limit_the_number_of_results_when_the_time_limit_is_reached]] https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_limit_the_number_of_results_when_the_time_limit_is_reached - [[search-dsl-query-entity-loading-options]] === Entity loading options @@ -230,24 +252,43 @@ include::todo-placeholder.asciidoc[] // TODO HSEARCH-3628 https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_fetching_strategy -[[search-dsl-query-turning-into-jpa-query]] -=== Turning the `SearchQuery` into a JPA or Hibernate ORM query +[[search-dsl-query-timeout]] +=== Timeout +// Search 5 anchors backward compatibility +[[_limiting_the_time_of_a_query]] + +include::todo-placeholder.asciidoc[] + +// TODO [[_raise_an_exception_on_time_limit]] https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_raise_an_exception_on_time_limit +// TODO [[_limit_the_number_of_results_when_the_time_limit_is_reached]] https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_limit_the_number_of_results_when_the_time_limit_is_reached + +[[search-dsl-query-object]] +=== Obtaining a query object include::todo-placeholder.asciidoc[] +// TODO toQuery() and potential uses (=> ORM query, explain(), ...) + // TODO turning a SearchQuery into a JPA/ORM query // TODO [[_resulttransformer]] https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_resulttransformer [[search-dsl-query-debugging]] === Debugging a query + +[[search-dsl-query-debugging-matches]] +==== Explaining matches + +include::todo-placeholder.asciidoc[] +// TODO toString(), getQueryString(), and logging, too? + +[[search-dsl-query-debugging-scores]] +==== Explaining scores // Search 5 anchors backward compatibility [[_understanding_results]] include::todo-placeholder.asciidoc[] - // TODO https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_understanding_results -// TODO toString(), getQueryString(), and logging, too? [[search-dsl-predicate]] == Predicate DSL From 5683e92bc4c8f90ae75f6bbbfaccc41886617809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 2 Sep 2019 14:28:58 +0200 Subject: [PATCH 2/7] HSEARCH-3615 Use an explicit hit limit in all documentation examples Calling fetch() or fetchHits() without a limit is a bad idea unless you know what you're doing, so we'd better not show that everywhere. --- .../main/asciidoc/getting-started.asciidoc | 4 +- .../src/main/asciidoc/search-dsl.asciidoc | 4 +- .../GettingStartedWithAnalysisIT.java | 4 +- .../GettingStartedWithoutAnalysisIT.java | 8 +- .../HibernateOrmSimpleMappingIT.java | 4 +- .../HibernateOrmIndexedIT.java | 6 +- .../HibernateOrmAutomaticIndexingIT.java | 2 +- .../orm/routing/HibernateOrmRoutingIT.java | 2 +- .../aggregation/AggregationDslIT.java | 26 +++--- .../searchdsl/converter/DslConverterIT.java | 4 +- .../converter/ProjectionConverterIT.java | 4 +- .../searchdsl/predicate/PredicateDslIT.java | 82 +++++++++---------- .../searchdsl/projection/ProjectionDslIT.java | 34 ++++---- .../searchdsl/query/QueryDslIT.java | 6 +- .../searchdsl/sort/SortDslIT.java | 30 +++---- 15 files changed, 110 insertions(+), 110 deletions(-) diff --git a/documentation/src/main/asciidoc/getting-started.asciidoc b/documentation/src/main/asciidoc/getting-started.asciidoc index 7afc5f80e69..f0de21e8728 100644 --- a/documentation/src/main/asciidoc/getting-started.asciidoc +++ b/documentation/src/main/asciidoc/getting-started.asciidoc @@ -290,7 +290,7 @@ include::{sourcedir}/org/hibernate/search/documentation/gettingstarted/withhsear <2> Initiate a search query on the index mapped to the `Book` entity. <3> Define that only documents matching the given predicate should be returned. The predicate is created using a factory `f` passed as an argument to the lambda expression. -<4> Build the query and fetch the results. +<4> Build the query and fetch the results, limiting to the top 20 hits. <5> Retrieve the total number of matching entities. <6> Retrieve matching entities. <7> In case you're not interested in the whole result, but only in the hits, @@ -311,7 +311,7 @@ include::{sourcedir}/org/hibernate/search/documentation/gettingstarted/withhsear <3> Initiate a search query targeting the search scope. <4> Define that only documents matching the given predicate should be returned. The predicate is created using the same search scope as the query. -<5> Build the query and fetch the results. +<5> Build the query and fetch the results, limiting to the top 20 hits. <6> Retrieve the total number of matching entities. <7> Retrieve matching entities. <8> In case you're not interested in the whole result, but only in the hits, diff --git a/documentation/src/main/asciidoc/search-dsl.asciidoc b/documentation/src/main/asciidoc/search-dsl.asciidoc index c7a15e29adf..a4c17453bc7 100644 --- a/documentation/src/main/asciidoc/search-dsl.asciidoc +++ b/documentation/src/main/asciidoc/search-dsl.asciidoc @@ -33,7 +33,7 @@ include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDsl <3> Define that only documents matching the given predicate should be returned. The predicate is created using a factory `f` passed as an argument to the lambda expression. See <> for more information about predicates. -<4> Build the query and fetch the results. +<4> Build the query and fetch the results, limiting to the top 20 hits. <5> Retrieve the total number of matching entities. <6> Retrieve matching entities. ==== @@ -238,7 +238,7 @@ include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDsl ---- <1> Start building the query. <2> Set the fetch size to an arbitrary value (must be `1` or more). -<3> Fetch the results. +<3> Fetch the results, limiting to the top 200 hits. One query will be executed to load the hits if there are less hits than the given fetch size; two queries if there are more hits than the fetch size but less than twice the fetch size, etc. diff --git a/documentation/src/test/java/org/hibernate/search/documentation/gettingstarted/withhsearch/withanalysis/GettingStartedWithAnalysisIT.java b/documentation/src/test/java/org/hibernate/search/documentation/gettingstarted/withhsearch/withanalysis/GettingStartedWithAnalysisIT.java index 3aaea5f0410..95c35446227 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/gettingstarted/withhsearch/withanalysis/GettingStartedWithAnalysisIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/gettingstarted/withhsearch/withanalysis/GettingStartedWithAnalysisIT.java @@ -83,7 +83,7 @@ public void test() { .fields( "title", "authors.name" ) .matching( "refactor" ) ) - .fetch(); + .fetch( 20 ); // Not shown: commit the transaction and close the entity manager // end::searching[] @@ -101,7 +101,7 @@ public void test() { .fields( "title", "authors.name" ) .matching( term ) ) - .fetch(); + .fetch( 20 ); assertThat( result.getHits() ).as( "Result of searching for '" + term + "'" ) .extracting( "id" ) .containsExactlyInAnyOrder( bookIdHolder.get() ); diff --git a/documentation/src/test/java/org/hibernate/search/documentation/gettingstarted/withhsearch/withoutanalysis/GettingStartedWithoutAnalysisIT.java b/documentation/src/test/java/org/hibernate/search/documentation/gettingstarted/withhsearch/withoutanalysis/GettingStartedWithoutAnalysisIT.java index 2a5c7806ba3..1d7a3ee31f9 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/gettingstarted/withhsearch/withoutanalysis/GettingStartedWithoutAnalysisIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/gettingstarted/withhsearch/withoutanalysis/GettingStartedWithoutAnalysisIT.java @@ -110,7 +110,7 @@ public void test() { .matching( "Refactoring: Improving the Design of Existing Code" ) .toPredicate() ) - .fetch(); // <5> + .fetch( 20 ); // <5> long totalHitCount = result.getTotalHitCount(); // <6> List hits = result.getHits(); // <7> @@ -125,7 +125,7 @@ public void test() { .toPredicate() ) // tag::searching-objects[] - .fetchHits(); // <8> + .fetchHits( 20 ); // <8> // Not shown: commit the transaction and close the entity manager // end::searching-objects[] @@ -146,7 +146,7 @@ public void test() { .fields( "title", "authors.name" ) .matching( "Refactoring: Improving the Design of Existing Code" ) ) - .fetch(); // <4> + .fetch( 20 ); // <4> long totalHitCount = result.getTotalHitCount(); // <5> List hits = result.getHits(); // <6> @@ -160,7 +160,7 @@ public void test() { .matching( "Refactoring: Improving the Design of Existing Code" ) ) // tag::searching-lambdas[] - .fetchHits(); // <7> + .fetchHits( 20 ); // <7> // Not shown: commit the transaction and close the entity manager // end::searching-lambdas[] diff --git a/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/directfieldmapping/HibernateOrmSimpleMappingIT.java b/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/directfieldmapping/HibernateOrmSimpleMappingIT.java index 26053a339b8..4bd7e311702 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/directfieldmapping/HibernateOrmSimpleMappingIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/directfieldmapping/HibernateOrmSimpleMappingIT.java @@ -76,7 +76,7 @@ public void sort() { .sort( f -> f.field( "pageCount" ).desc() // <2> .then().field( "title_sort" ) ) - .fetchHits(); // <3> + .fetchHits( 20 ); // <3> assertThat( result ) .extracting( "title" ) @@ -92,7 +92,7 @@ public void projection_simple() { List result = searchSession.search( Book.class ) // <1> .asProjection( f -> f.field( "title", String.class ) ) // <2> .predicate( f -> f.matchAll() ) - .fetchHits(); // <3> + .fetchHits( 20 ); // <3> assertThat( result ) .containsExactlyInAnyOrder( BOOK1_TITLE, BOOK2_TITLE, BOOK3_TITLE ); diff --git a/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/entityindexmapping/HibernateOrmIndexedIT.java b/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/entityindexmapping/HibernateOrmIndexedIT.java index d124c20f9a3..22488dc8d0f 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/entityindexmapping/HibernateOrmIndexedIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/entityindexmapping/HibernateOrmIndexedIT.java @@ -74,12 +74,12 @@ public void search_separateQueries() { List authorResult = searchSession.search( Author.class ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); assertThat( authorResult ).hasSize( 1 ); List userResult = searchSession.search( User.class ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); assertThat( userResult ).hasSize( 1 ); } ); } @@ -96,7 +96,7 @@ public void search_singleQuery() { Arrays.asList( Author.class, User.class ) ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::cross-backend-search[] } ) ) diff --git a/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/indexing/HibernateOrmAutomaticIndexingIT.java b/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/indexing/HibernateOrmAutomaticIndexingIT.java index 7362b2a88d5..cde7d4ba377 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/indexing/HibernateOrmAutomaticIndexingIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/indexing/HibernateOrmAutomaticIndexingIT.java @@ -77,7 +77,7 @@ public void synchronizationStrategyOverride() { List result = searchSession.search( Book.class ) .predicate( f -> f.match().field( "title" ).matching( "2nd edition" ) ) - .fetchHits(); // <5> + .fetchHits( 20 ); // <5> // end::automatic-indexing-synchronization-strategy-override[] assertThat( result ).extracting( Book::getId ) diff --git a/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/routing/HibernateOrmRoutingIT.java b/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/routing/HibernateOrmRoutingIT.java index 972af6594f3..03cbe0d3655 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/routing/HibernateOrmRoutingIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/routing/HibernateOrmRoutingIT.java @@ -71,7 +71,7 @@ public void routing_single() { .field( "genre" ) .matching( Genre.SCIENCE_FICTION ) ) // <2> .routing( Genre.SCIENCE_FICTION.name() ) // <3> - .fetch(); // <4> + .fetch( 20 ); // <4> // end::routing-single[] // We can't really test whether sharding worked here; see the backend TCK for such test. diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/aggregation/AggregationDslIT.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/aggregation/AggregationDslIT.java index 356c0702490..a27d08c8ffa 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/aggregation/AggregationDslIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/aggregation/AggregationDslIT.java @@ -84,7 +84,7 @@ public void entryPoint() { .matching( "robot" ) ) .aggregation( countsByGenreKey, f -> f.terms() // <4> .field( "genre", Genre.class ) ) - .fetch(); // <5> + .fetch( 20 ); // <5> Map countsByGenre = result.getAggregation( countsByGenreKey ); // <6> // end::entryPoint-lambdas[] @@ -109,7 +109,7 @@ public void entryPoint() { .aggregation( countsByGenreKey, scope.aggregation().terms() .field( "genre", Genre.class ) .toAggregation() ) - .fetch(); + .fetch( 20 ); Map countsByGenre = result.getAggregation( countsByGenreKey ); // end::entryPoint-objects[] @@ -129,7 +129,7 @@ public void terms() { .predicate( f -> f.matchAll() ) .aggregation( countsByGenreKey, f -> f.terms() .field( "genre", Genre.class ) ) // <1> - .fetch(); + .fetch( 20 ); Map countsByGenre = result.getAggregation( countsByGenreKey ); // end::terms[] assertThat( countsByGenre ) @@ -146,7 +146,7 @@ public void terms() { .predicate( f -> f.matchAll() ) .aggregation( countsByGenreKey, f -> f.terms() .field( "genre", String.class, ValueConvert.NO ) ) - .fetch(); + .fetch( 20 ); Map countsByGenre = result.getAggregation( countsByGenreKey ); // end::terms-noConverter[] assertThat( countsByGenre ) @@ -164,7 +164,7 @@ public void terms() { .aggregation( countsByGenreKey, f -> f.terms() .field( "genre", Genre.class ) .maxTermCount( 1 ) ) - .fetch(); + .fetch( 20 ); Map countsByGenre = result.getAggregation( countsByGenreKey ); // end::terms-max-term-count[] assertThat( countsByGenre ) @@ -181,7 +181,7 @@ public void terms() { .aggregation( countsByGenreKey, f -> f.terms() .field( "genre", Genre.class ) .minDocumentCount( 0 ) ) - .fetch(); + .fetch( 20 ); Map countsByGenre = result.getAggregation( countsByGenreKey ); // end::terms-min-doc-count-zero[] assertThat( countsByGenre ) @@ -199,7 +199,7 @@ public void terms() { .aggregation( countsByGenreKey, f -> f.terms() .field( "genre", Genre.class ) .minDocumentCount( 2 ) ) - .fetch(); + .fetch( 20 ); Map countsByGenre = result.getAggregation( countsByGenreKey ); // end::terms-min-doc-count-high[] assertThat( countsByGenre ) @@ -216,7 +216,7 @@ public void terms() { .aggregation( countsByGenreKey, f -> f.terms() .field( "genre", Genre.class ) .orderByTermAscending() ) - .fetch(); + .fetch( 20 ); Map countsByGenre = result.getAggregation( countsByGenreKey ); // end::terms-order-term-ascending[] assertThat( countsByGenre ) @@ -234,7 +234,7 @@ public void terms() { .aggregation( countsByGenreKey, f -> f.terms() .field( "genre", Genre.class ) .orderByTermDescending() ) - .fetch(); + .fetch( 20 ); Map countsByGenre = result.getAggregation( countsByGenreKey ); // end::terms-order-term-descending[] assertThat( countsByGenre ) @@ -252,7 +252,7 @@ public void terms() { .aggregation( countsByGenreKey, f -> f.terms() .field( "genre", Genre.class ) .orderByCountAscending() ) - .fetch(); + .fetch( 20 ); Map countsByGenre = result.getAggregation( countsByGenreKey ); // end::terms-order-count-ascending[] assertThat( countsByGenre ) @@ -276,7 +276,7 @@ public void range() { .range( 10.0, 20.0 ) .range( 20.0, null ) // <3> ) - .fetch(); + .fetch( 20 ); Map, Long> countsByPrice = result.getAggregation( countsByPriceKey ); // end::range[] assertThat( countsByPrice ) @@ -299,7 +299,7 @@ public void range() { 20.0, RangeBoundInclusion.EXCLUDED ) ) // <2> .range( Range.atLeast( 20.0 ) ) // <3> ) - .fetch(); + .fetch( 20 ); Map, Long> countsByPrice = result.getAggregation( countsByPriceKey ); // end::range-objects[] assertThat( countsByPrice ) @@ -329,7 +329,7 @@ public void range() { .atStartOfDay().toInstant( ZoneOffset.UTC ), null ) ) - .fetch(); + .fetch( 20 ); Map, Long> countsByPrice = result.getAggregation( countsByPriceKey ); // end::range-noConverter[] assertThat( countsByPrice ) diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/converter/DslConverterIT.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/converter/DslConverterIT.java index d029fe10ead..6f581589247 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/converter/DslConverterIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/converter/DslConverterIT.java @@ -73,7 +73,7 @@ public void dslConverterEnabled() { List result = searchSession.search( AuthenticationEvent.class ) .predicate( f -> f.match().field( "outcome" ) .matching( AuthenticationOutcome.INVALID_PASSWORD ) ) - .fetchHits(); + .fetchHits( 20 ); // end::dsl-converter-enabled[] assertThat( result ) @@ -91,7 +91,7 @@ public void dslConverterDisabled() { List result = searchSession.search( AuthenticationEvent.class ) .predicate( f -> f.match().field( "outcome" ) .matching( "Invalid password", ValueConvert.NO ) ) - .fetchHits(); + .fetchHits( 20 ); // end::dsl-converter-disabled[] assertThat( result ) diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/converter/ProjectionConverterIT.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/converter/ProjectionConverterIT.java index 02163178265..da6d189934b 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/converter/ProjectionConverterIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/converter/ProjectionConverterIT.java @@ -73,7 +73,7 @@ public void projectionConverterEnabled() { List result = searchSession.search( Order.class ) .asProjection( f -> f.field( "status", OrderStatus.class ) ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::projection-converter-enabled[] assertThat( result ) @@ -90,7 +90,7 @@ public void projectionConverterDisabled() { List result = searchSession.search( Order.class ) .asProjection( f -> f.field( "status", String.class, ValueConvert.NO ) ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::projection-converter-disabled[] assertThat( result ) diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/predicate/PredicateDslIT.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/predicate/PredicateDslIT.java index 807c015fce1..6b61f04249d 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/predicate/PredicateDslIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/predicate/PredicateDslIT.java @@ -91,7 +91,7 @@ public void entryPoint() { List result = searchSession.search( Book.class ) // <1> .predicate( f -> f.match().field( "title" ) // <2> .matching( "robot" ) ) - .fetchHits(); // <3> + .fetchHits( 20 ); // <3> // end::entryPoint-lambdas[] assertThat( result ) .extracting( Book::getId ) @@ -108,7 +108,7 @@ public void entryPoint() { .predicate( scope.predicate().match().field( "title" ) .matching( "robot" ) .toPredicate() ) - .fetchHits(); + .fetchHits( 20 ); // end::entryPoint-objects[] assertThat( result ) .extracting( Book::getId ) @@ -123,7 +123,7 @@ public void matchAll() { // tag::matchAll[] List hits = searchSession.search( Book.class ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::matchAll[] assertThat( hits ) .extracting( Book::getId ) @@ -137,7 +137,7 @@ public void matchAll() { .except( f.match().field( "title" ) .matching( "robot" ) ) ) - .fetchHits(); + .fetchHits( 20 ); // end::matchAll-except[] assertThat( hits ) .extracting( Book::getId ) @@ -154,7 +154,7 @@ public void id() { // tag::id[] List hits = searchSession.search( Book.class ) .predicate( f -> f.id().matching( 1 ) ) - .fetchHits(); + .fetchHits( 20 ); // end::id[] assertThat( hits ) .extracting( Book::getId ) @@ -168,7 +168,7 @@ public void id() { ids.add( 2 ); List hits = searchSession.search( Book.class ) .predicate( f -> f.id().matchingAny( ids ) ) - .fetchHits(); + .fetchHits( 20 ); // end::id-matchingAny[] assertThat( hits ) .extracting( Book::getId ) @@ -187,7 +187,7 @@ public void bool() { .should( f.match().field( "description" ) .matching( "investigation" ) ) // <2> ) - .fetchHits(); // <3> + .fetchHits( 20 ); // <3> // end::bool-or[] assertThat( hits ) .extracting( Book::getId ) @@ -203,7 +203,7 @@ public void bool() { .must( f.match().field( "description" ) .matching( "crime" ) ) // <2> ) - .fetchHits(); // <3> + .fetchHits( 20 ); // <3> // end::bool-and[] assertThat( hits ) .extracting( Book::getId ) @@ -219,7 +219,7 @@ public void bool() { .mustNot( f.match().field( "description" ) .matching( "investigation" ) ) // <2> ) - .fetchHits(); // <3> + .fetchHits( 20 ); // <3> // end::bool-mustNot[] assertThat( hits ) .extracting( Book::getId ) @@ -243,7 +243,7 @@ public void bool() { .matching( "robot" ) ) // <7> ) ) - .fetchHits(); // <8> + .fetchHits( 20 ); // <8> // end::bool-filter[] assertThat( hits ) .extracting( Book::getId ) @@ -261,7 +261,7 @@ public void bool() { .should( f.match().field( "description" ) .matching( "investigation" ) ) // <3> ) - .fetchHits(); // <4> + .fetchHits( 20 ); // <4> // end::bool-mustAndShould[] assertThat( hits ) .extracting( Book::getId ) @@ -280,7 +280,7 @@ public void bool() { .should( f.match().field( "description" ) .matching( "disappearance" ) ) // <4> ) - .fetchHits(); // <5> + .fetchHits( 20 ); // <5> // end::bool-minimumShouldMatchNumber[] assertThat( hits ) .extracting( Book::getId ) @@ -306,7 +306,7 @@ public void bool() { .below( searchParameters.getPageCountMaxFilter() ) ); } } ) ) - .fetchHits(); // <5> + .fetchHits( 20 ); // <5> // end::bool-dynamicParameters[] assertThat( hits ) .extracting( Book::getId ) @@ -321,7 +321,7 @@ public void match() { List hits = searchSession.search( Book.class ) .predicate( f -> f.match().field( "title" ) .matching( "robot" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::match[] assertThat( hits ) .extracting( Book::getId ) @@ -333,7 +333,7 @@ public void match() { List hits = searchSession.search( Book.class ) .predicate( f -> f.match().field( "title" ) .matching( "robot dawn" ) ) // <1> - .fetchHits(); // <2> + .fetchHits( 20 ); // <2> // end::match-multipleTerms[] assertThat( hits ) .extracting( Book::getId ) @@ -346,7 +346,7 @@ public void match() { .predicate( f -> f.match() .field( "title" ).field( "description" ) .matching( "robot" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::match-orField[] assertThat( hits ) .extracting( Book::getId ) @@ -359,7 +359,7 @@ public void match() { .predicate( f -> f.match() .fields( "title", "description" ) .matching( "robot" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::match-fields[] assertThat( hits ) .extracting( Book::getId ) @@ -373,7 +373,7 @@ public void match() { .field( "title" ) .matching( "robto" ) .fuzzy() ) - .fetchHits(); + .fetchHits( 20 ); // end::match-fuzzy[] assertThat( hits ) .extracting( Book::getId ) @@ -387,7 +387,7 @@ public void match() { .field( "title_autocomplete" ) .matching( "robo" ) .analyzer( "autocomplete_query" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::match-analyzer[] assertThat( hits ) .extracting( Book::getId ) @@ -401,7 +401,7 @@ public void match() { .field( "title" ) .matching( "robot" ) .skipAnalysis() ) - .fetchHits(); + .fetchHits( 20 ); // end::match-skipAnalysis[] assertThat( hits ) .extracting( Book::getId ) @@ -416,7 +416,7 @@ public void range() { List hits = searchSession.search( Book.class ) .predicate( f -> f.range().field( "pageCount" ) .from( 210 ).to( 250 ) ) - .fetchHits(); + .fetchHits( 20 ); // end::range-between[] assertThat( hits ) .extracting( Book::getId ) @@ -428,7 +428,7 @@ public void range() { List hits = searchSession.search( Book.class ) .predicate( f -> f.range().field( "pageCount" ) .above( 400 ) ) - .fetchHits(); + .fetchHits( 20 ); // end::range-above[] assertThat( hits ) .extracting( Book::getId ) @@ -440,7 +440,7 @@ public void range() { List hits = searchSession.search( Book.class ) .predicate( f -> f.range().field( "pageCount" ) .below( 400 ) ) - .fetchHits(); + .fetchHits( 20 ); // end::range-below[] assertThat( hits ) .extracting( Book::getId ) @@ -453,7 +453,7 @@ public void range() { .predicate( f -> f.range().field( "pageCount" ) .from( 200 ).excludeLimit() .to( 250 ).excludeLimit() ) - .fetchHits(); + .fetchHits( 20 ); // end::range-excludeLimit[] assertThat( hits ) .extracting( Book::getId ) @@ -468,7 +468,7 @@ public void phrase() { List hits = searchSession.search( Book.class ) .predicate( f -> f.phrase().field( "title" ) .matching( "robots of dawn" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::phrase[] assertThat( hits ) .extracting( Book::getId ) @@ -481,7 +481,7 @@ public void phrase() { .predicate( f -> f.phrase().field( "title" ) .matching( "dawn robot" ) .slop( 3 ) ) - .fetchHits(); + .fetchHits( 20 ); // end::phrase-slop[] assertThat( hits ) .extracting( Book::getId ) @@ -496,7 +496,7 @@ public void simpleQueryString() { List hits = searchSession.search( Book.class ) .predicate( f -> f.simpleQueryString().field( "description" ) .matching( "robots + (crime | investigation | disappearance)" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::simpleQueryString-boolean[] assertThat( hits ) .extracting( Book::getId ) @@ -508,7 +508,7 @@ public void simpleQueryString() { List hits = searchSession.search( Book.class ) .predicate( f -> f.simpleQueryString().field( "description" ) .matching( "robots + -investigation" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::simpleQueryString-not[] assertThat( hits ) .extracting( Book::getId ) @@ -521,7 +521,7 @@ public void simpleQueryString() { .predicate( f -> f.simpleQueryString().field( "description" ) .matching( "robots investigation" ) .defaultOperator( BooleanOperator.AND ) ) - .fetchHits(); + .fetchHits( 20 ); // end::simpleQueryString-defaultOperator-and[] assertThat( hits ) .extracting( Book::getId ) @@ -533,7 +533,7 @@ public void simpleQueryString() { List hits = searchSession.search( Book.class ) .predicate( f -> f.simpleQueryString().field( "description" ) .matching( "rob*" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::simpleQueryString-prefix[] assertThat( hits ) .extracting( Book::getId ) @@ -545,7 +545,7 @@ public void simpleQueryString() { List hits = searchSession.search( Book.class ) .predicate( f -> f.simpleQueryString().field( "description" ) .matching( "robto~2" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::simpleQueryString-fuzzy[] assertThat( hits ) .extracting( Book::getId ) @@ -557,7 +557,7 @@ public void simpleQueryString() { List hits = searchSession.search( Book.class ) .predicate( f -> f.simpleQueryString().field( "title" ) .matching( "\"robots of dawn\"" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::simpleQueryString-phrase[] assertThat( hits ) .extracting( Book::getId ) @@ -569,7 +569,7 @@ public void simpleQueryString() { List hits = searchSession.search( Book.class ) .predicate( f -> f.simpleQueryString().field( "title" ) .matching( "\"dawn robot\"~3" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::simpleQueryString-phrase-slop[] assertThat( hits ) .extracting( Book::getId ) @@ -583,7 +583,7 @@ public void exists() { // tag::exists[] List hits = searchSession.search( Book.class ) .predicate( f -> f.exists().field( "comment" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::exists[] assertThat( hits ) .extracting( Book::getId ) @@ -598,7 +598,7 @@ public void wildcard() { List hits = searchSession.search( Book.class ) .predicate( f -> f.wildcard().field( "description" ) .matching( "rob*t" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::wildcard[] assertThat( hits ) .extracting( Book::getId ) @@ -618,7 +618,7 @@ public void nested() { .must( f.match().field( "authors.lastName" ) .matching( "asimov" ) ) // <3> ) ) - .fetchHits(); // <4> + .fetchHits( 20 ); // <4> // end::nested[] assertThat( hits ) .extracting( Book::getId ) @@ -634,7 +634,7 @@ public void within() { List hits = searchSession.search( Author.class ) .predicate( f -> f.spatial().within().field( "placeOfBirth" ) .circle( center, 50, DistanceUnit.KILOMETERS ) ) - .fetchHits(); + .fetchHits( 20 ); // end::within-circle[] assertThat( hits ) .extracting( Author::getId ) @@ -650,7 +650,7 @@ public void within() { List hits = searchSession.search( Author.class ) .predicate( f -> f.spatial().within().field( "placeOfBirth" ) .boundingBox( box ) ) - .fetchHits(); + .fetchHits( 20 ); // end::within-box[] assertThat( hits ) .extracting( Author::getId ) @@ -670,7 +670,7 @@ public void within() { List hits = searchSession.search( Author.class ) .predicate( f -> f.spatial().within().field( "placeOfBirth" ) .polygon( polygon ) ) - .fetchHits(); + .fetchHits( 20 ); // end::within-polygon[] assertThat( hits ) .extracting( Author::getId ) @@ -689,7 +689,7 @@ public void lucene() { .predicate( f -> f.fromLuceneQuery( new RegexpQuery( new Term( "description", "neighbor|neighbour" ) ) ) ) - .fetchHits(); + .fetchHits( 20 ); // end::lucene-fromLuceneQuery[] assertThat( hits ) .extracting( Book::getId ) @@ -710,7 +710,7 @@ public void elasticsearch() { + "\"description\": \"neighbor|neighbour\"" + "}" + "}" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::elasticsearch-fromJson[] assertThat( hits ) .extracting( Book::getId ) diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/projection/ProjectionDslIT.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/projection/ProjectionDslIT.java index 475af2238c9..e25525aa40d 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/projection/ProjectionDslIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/projection/ProjectionDslIT.java @@ -96,7 +96,7 @@ public void entryPoint() { List result = searchSession.search( Book.class ) // <1> .asProjection( f -> f.field( "title", String.class ) ) // <2> .predicate( f -> f.matchAll() ) - .fetchHits(); // <3> + .fetchHits( 20 ); // <3> // end::entryPoint-lambdas[] assertThat( result ).containsExactlyInAnyOrder( entityManager.getReference( Book.class, BOOK1_ID ).getTitle(), @@ -116,7 +116,7 @@ public void entryPoint() { .asProjection( scope.projection().field( "title", String.class ) .toProjection() ) .predicate( scope.predicate().matchAll().toPredicate() ) - .fetchHits(); + .fetchHits( 20 ); // end::entryPoint-objects[] assertThat( result ).containsExactlyInAnyOrder( entityManager.getReference( Book.class, BOOK1_ID ).getTitle(), @@ -134,7 +134,7 @@ public void documentReference() { List hits = searchSession.search( Book.class ) .asProjection( f -> f.documentReference() ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::documentReference[] SearchHitsAssert.assertThat( hits ).hasDocRefHitsAnyOrder( BOOK_INDEX_NAME, @@ -153,7 +153,7 @@ public void reference() { List hits = searchSession.search( Book.class ) .asProjection( f -> f.entityReference() ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::reference[] assertThat( hits ).containsExactlyInAnyOrder( new EntityReferenceImpl( Book.class, BOOK1_ID ), @@ -171,7 +171,7 @@ public void entity() { List hits = searchSession.search( Book.class ) .asProjection( f -> f.entity() ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::entity[] Session session = searchSession.toOrmSession(); assertThat( hits ).containsExactlyInAnyOrder( @@ -190,7 +190,7 @@ public void field() { List hits = searchSession.search( Book.class ) .asProjection( f -> f.field( "genre", Genre.class ) ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::field[] Session session = searchSession.toOrmSession(); assertThat( hits ).containsExactlyInAnyOrder( @@ -206,7 +206,7 @@ public void field() { List hits = searchSession.search( Book.class ) .asProjection( f -> f.field( "genre" ) ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::field-noType[] Session session = searchSession.toOrmSession(); assertThat( hits ).containsExactlyInAnyOrder( @@ -224,7 +224,7 @@ public void field() { "genre", String.class, ValueConvert.NO ) ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::field-noProjectionConverter[] Session session = searchSession.toOrmSession(); assertThat( hits ).containsExactlyInAnyOrder( @@ -244,7 +244,7 @@ public void score() { .asProjection( f -> f.score() ) .predicate( f -> f.match().field( "title" ) .matching( "robot dawn" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::score[] assertThat( hits ) .hasSize( 2 ) @@ -262,7 +262,7 @@ public void distance() { SearchResult result = searchSession.search( Author.class ) .asProjection( f -> f.distance( "placeOfBirth", center ) ) .predicate( f -> f.matchAll() ) - .fetch(); // <3> + .fetch( 20 ); // <3> // end::distance[] assertThat( result.getHits() ) .hasSize( 2 ) @@ -278,7 +278,7 @@ public void distance() { .asProjection( f -> f.distance( "placeOfBirth", center ) .unit( DistanceUnit.KILOMETERS ) ) .predicate( f -> f.matchAll() ) - .fetch(); // <3> + .fetch( 20 ); // <3> // end::distance-unit[] assertThat( result.getHits() ) .hasSize( 2 ) @@ -299,7 +299,7 @@ public void composite() { f.field( "genre", Genre.class ) // <4> ) ) .predicate( f -> f.matchAll() ) - .fetchHits(); // <5> + .fetchHits( 20 ); // <5> // end::composite-customObject[] Session session = searchSession.toOrmSession(); assertThat( hits ).containsExactlyInAnyOrder( @@ -330,7 +330,7 @@ public void composite() { f.field( "genre", Genre.class ) // <3> ) ) .predicate( f -> f.matchAll() ) - .fetchHits(); // <4> + .fetchHits( 20 ); // <4> // end::composite-list[] Session session = searchSession.toOrmSession(); assertThat( hits ).containsExactlyInAnyOrder( @@ -364,7 +364,7 @@ public void lucene() { .extension( LuceneExtension.get() ) .asProjection( f -> f.document() ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::lucene-document[] assertThat( hits ).hasSize( 4 ); } ); @@ -375,7 +375,7 @@ public void lucene() { .extension( LuceneExtension.get() ) .asProjection( f -> f.explanation() ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::lucene-explanation[] assertThat( hits ).hasSize( 4 ); } ); @@ -391,7 +391,7 @@ public void elasticsearch() { .extension( ElasticsearchExtension.get() ) .asProjection( f -> f.source() ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::elasticsearch-source[] assertThat( hits ).hasSize( 4 ); } ); @@ -402,7 +402,7 @@ public void elasticsearch() { .extension( ElasticsearchExtension.get() ) .asProjection( f -> f.explanation() ) .predicate( f -> f.matchAll() ) - .fetchHits(); + .fetchHits( 20 ); // end::elasticsearch-explanation[] assertThat( hits ).hasSize( 4 ); } ); diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java index 00fd08edb1d..0ce6b6b8c0e 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java @@ -77,7 +77,7 @@ public void entryPoint() { .predicate( f -> f.match() // <3> .field( "title" ) .matching( "robot" ) ) - .fetch(); // <4> + .fetch( 20 ); // <4> long totalHitCount = result.getTotalHitCount(); // <5> List hits = result.getHits(); // <6> @@ -107,7 +107,7 @@ public void cacheLookupStrategy() { .predicate( f -> f.match() .field( "title" ) .matching( "robot" ) ) - .fetch(); // <3> + .fetch( 20 ); // <3> // end::cacheLookupStrategy-persistenceContextThenSecondLevelCache[] assertThat( result.getHits() ).extracting( Book::getId ) @@ -127,7 +127,7 @@ public void fetchSize() { .predicate( f -> f.match() .field( "title" ) .matching( "robot" ) ) - .fetch(); // <3> + .fetch( 200 ); // <3> // end::fetchSize[] assertThat( result.getHits() ).extracting( Book::getId ) diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/sort/SortDslIT.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/sort/SortDslIT.java index 05a2cdd73df..70878415a1e 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/sort/SortDslIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/sort/SortDslIT.java @@ -88,7 +88,7 @@ public void entryPoint() { .predicate( f -> f.matchAll() ) .sort( f -> f.field( "pageCount" ).desc() // <2> .then().field( "title_sort" ) ) - .fetchHits(); // <3> + .fetchHits( 20 ); // <3> // end::entryPoint-lambdas[] assertThat( result ) .extracting( Book::getId ) @@ -107,7 +107,7 @@ public void entryPoint() { .field( "pageCount" ).desc() .then().field( "title_sort" ) .toSort() ) - .fetchHits(); + .fetchHits( 20 ); // end::entryPoint-objects[] assertThat( result ) .extracting( Book::getId ) @@ -123,7 +123,7 @@ public void score() { .predicate( f -> f.match().field( "title" ) .matching( "robot dawn" ) ) .sort( f -> f.score() ) - .fetchHits(); + .fetchHits( 20 ); // end::score[] assertThat( hits ) .extracting( Book::getId ) @@ -138,7 +138,7 @@ public void indexOrder() { List hits = searchSession.search( Book.class ) .predicate( f -> f.matchAll() ) .sort( f -> f.indexOrder() ) - .fetchHits(); + .fetchHits( 20 ); // end::indexOrder[] assertThat( hits ) .extracting( Book::getId ) @@ -154,7 +154,7 @@ public void field() { List hits = searchSession.search( Book.class ) .predicate( f -> f.matchAll() ) .sort( f -> f.field( "title_sort" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::field[] assertThat( hits ) .extracting( Book::getId ) @@ -166,7 +166,7 @@ public void field() { List hits = searchSession.search( Book.class ) .predicate( f -> f.matchAll() ) .sort( f -> f.field( "pageCount" ).missing().first() ) - .fetchHits(); + .fetchHits( 20 ); // end::field-missing-first[] assertThat( hits ) .extracting( Book::getId ) @@ -178,7 +178,7 @@ public void field() { List hits = searchSession.search( Book.class ) .predicate( f -> f.matchAll() ) .sort( f -> f.field( "pageCount" ).missing().last() ) - .fetchHits(); + .fetchHits( 20 ); // end::field-missing-last[] assertThat( hits ) .extracting( Book::getId ) @@ -190,7 +190,7 @@ public void field() { List hits = searchSession.search( Book.class ) .predicate( f -> f.matchAll() ) .sort( f -> f.field( "pageCount" ).missing().use( 300 ) ) - .fetchHits(); + .fetchHits( 20 ); // end::field-missing-use[] assertThat( hits ) .extracting( Book::getId ) @@ -207,7 +207,7 @@ public void composite() { .sort( f -> f.composite() // <1> .add( f.field( "genre_sort" ) ) // <2> .add( f.field( "title_sort" ) ) ) // <3> - .fetchHits(); // <4> + .fetchHits( 20 ); // <4> // end::composite[] assertThat( hits ) .extracting( Book::getId ) @@ -234,7 +234,7 @@ public void composite() { } } } ) ) - .fetchHits(); // <4> + .fetchHits( 20 ); // <4> // end::composite_dynamicParameters[] assertThat( hits ) .extracting( Book::getId ) @@ -250,7 +250,7 @@ public void then() { .predicate( f -> f.matchAll() ) .sort( f -> f.field( "genre_sort" ) // <2> .then().field( "title_sort" ) ) // <3> - .fetchHits(); // <4> + .fetchHits( 20 ); // <4> // end::then[] assertThat( hits ) .extracting( Book::getId ) @@ -266,7 +266,7 @@ public void distance() { List hits = searchSession.search( Author.class ) .predicate( f -> f.matchAll() ) .sort( f -> f.distance( "placeOfBirth", center ) ) - .fetchHits(); + .fetchHits( 20 ); // end::distance[] assertThat( hits ) .extracting( Author::getId ) @@ -289,7 +289,7 @@ public void lucene() { new SortField( "pageCount", SortField.Type.INT ) ) ) ) - .fetchHits(); + .fetchHits( 20 ); // end::lucene-fromLuceneSort[] assertThat( hits ) .extracting( Book::getId ) @@ -304,7 +304,7 @@ public void lucene() { .sort( f -> f.fromLuceneSortField( new SortField( "title_sort", SortField.Type.STRING ) ) ) - .fetchHits(); + .fetchHits( 20 ); // end::lucene-fromLuceneSortField[] assertThat( hits ) .extracting( Book::getId ) @@ -324,7 +324,7 @@ public void elasticsearch() { .sort( f -> f.fromJson( "{" + "\"title_sort\": \"asc\"" + "}" ) ) - .fetchHits(); + .fetchHits( 20 ); // end::elasticsearch-fromJson[] assertThat( hits ) .extracting( Book::getId ) From a2d85cc7da8552ce0e99d8bcf69d83ba1d64af5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 2 Sep 2019 15:34:25 +0200 Subject: [PATCH 3/7] HSEARCH-3615 Document fetching basics --- .../src/main/asciidoc/search-dsl.asciidoc | 60 +++++++++++++++++-- .../searchdsl/query/QueryDslIT.java | 56 +++++++++++++++++ 2 files changed, 110 insertions(+), 6 deletions(-) diff --git a/documentation/src/main/asciidoc/search-dsl.asciidoc b/documentation/src/main/asciidoc/search-dsl.asciidoc index a4c17453bc7..698b84269a1 100644 --- a/documentation/src/main/asciidoc/search-dsl.asciidoc +++ b/documentation/src/main/asciidoc/search-dsl.asciidoc @@ -72,13 +72,61 @@ to group hits and compute aggregated metrics for each group -- hit count by cate // Search 5 anchors backward compatibility [[_result_size]] -include::todo-placeholder.asciidoc[] +In Hibernate Search, the default search result is a little bit more complicated than just "a list of hits". +This is why the default methods return a composite `SearchResult` object offering getters +to retrieve the part of the result you want, +as shown in the example below. + +.Getting information from a `SearchResult` +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java[tags=fetching-searchResult] +---- +<1> Start building the query as usual. +<2> Fetch the results, limiting to the top 20 hits. +<3> Retrieve the total hit count, i.e. the total number of matching entities/documents, +which could be 10,000 even if you only retrieved the top 20 hits. +This is useful to give end users and idea of how many more hits they query produced. +<4> Retrieve the top hits, in this case the top 20 matching entities/documents. +<5> Other kinds of results and information can be retrieved from `SearchResult`. +They are explained in dedicated sections, such as <>. +==== + +It is possible to retrieve the total hit count alone, +for cases where only the number of hits is of interest, +not the hits themselves: + +.Getting the total hit count directly +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java[tags=fetching-totalHitCount] +---- +==== + +The top hits can also be obtained directly, +without going through a `SearchResult`, +which can be handy if only the top hits are useful, and not the total hit count: + +.Getting the top hits directly +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java[tags=fetching-hits] +---- +==== + +If only zero to one hit is expected, it is possible to retrieve it as an `Optional`. +An exception will be thrown if more than one hits are returned. -// TODO -// 1. Fetching the top hits and other results (total hit count, aggregations, ...) -// 2. Fetching the top hits only -// 3. Fetching the only hit -// 4. Fetching the total hit count only +.Getting the only hit directly +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java[tags=fetching-singleHit] +---- +==== [[search-dsl-query-fetching-results-all]] ==== Fetching all hits diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java index 0ce6b6b8c0e..4ed1f390dce 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java @@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.List; +import java.util.Optional; import javax.persistence.EntityManagerFactory; import javax.persistence.SharedCacheMode; @@ -90,6 +91,61 @@ public void entryPoint() { } ); } + @Test + public void fetchingBasics() { + OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { + SearchSession searchSession = Search.session( entityManager ); + // tag::fetching-searchResult[] + SearchResult result = searchSession.search( Book.class ) // <1> + .predicate( f -> f.matchAll() ) + .fetch( 20 ); // <2> + + long totalHitCount = result.getTotalHitCount(); // <3> + List hits = result.getHits(); // <4> + // ... // <5> + // end::fetching-searchResult[] + + assertThat( totalHitCount ).isEqualTo( 4 ); + assertThat( hits ).extracting( Book::getId ) + .containsExactlyInAnyOrder( BOOK1_ID, BOOK2_ID, BOOK3_ID, BOOK4_ID ); + } ); + + OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { + SearchSession searchSession = Search.session( entityManager ); + // tag::fetching-totalHitCount[] + long totalHitCount = searchSession.search( Book.class ) + .predicate( f -> f.matchAll() ) + .fetchTotalHitCount(); + // end::fetching-totalHitCount[] + + assertThat( totalHitCount ).isEqualTo( 4 ); + } ); + + OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { + SearchSession searchSession = Search.session( entityManager ); + // tag::fetching-hits[] + List hits = searchSession.search( Book.class ) + .predicate( f -> f.matchAll() ) + .fetchHits( 20 ); + // end::fetching-hits[] + + assertThat( hits ).extracting( Book::getId ) + .containsExactlyInAnyOrder( BOOK1_ID, BOOK2_ID, BOOK3_ID, BOOK4_ID ); + } ); + + OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { + SearchSession searchSession = Search.session( entityManager ); + // tag::fetching-singleHit[] + Optional hit = searchSession.search( Book.class ) + .predicate( f -> f.id().matching( 1 ) ) + .fetchSingleHit(); + // end::fetching-singleHit[] + + assertThat( hit ).get().extracting( Book::getId ) + .isEqualTo( BOOK1_ID ); + } ); + } + @Test public void cacheLookupStrategy() { OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { From 2b528b3ad7d4091049057fc759d441122c17870c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 2 Sep 2019 16:08:03 +0200 Subject: [PATCH 4/7] HSEARCH-3615 Document fetching all hits --- .../src/main/asciidoc/search-dsl.asciidoc | 37 ++++++++++++++++++- .../searchdsl/query/QueryDslIT.java | 32 ++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/asciidoc/search-dsl.asciidoc b/documentation/src/main/asciidoc/search-dsl.asciidoc index 698b84269a1..bcb7436ac73 100644 --- a/documentation/src/main/asciidoc/search-dsl.asciidoc +++ b/documentation/src/main/asciidoc/search-dsl.asciidoc @@ -131,9 +131,42 @@ include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDsl [[search-dsl-query-fetching-results-all]] ==== Fetching all hits -include::todo-placeholder.asciidoc[] +[WARNING] +==== +Fetching all hits is rarely a good idea: +if the query matches many entities/documents, +this may lead to loading millions of entities in memory, +which will likely crash the JVM, +or at the very least slow it down to a crawl. + +If you know your query will always have less than N hits, +consider setting the limit to N to avoid memory issues. + +If there is no bound to the number of hits you expect, +you should consider <> +or <> +to retrieve data in batches. + +If you still want to fetch all hits in one call, +be aware that the Elasticsearch backend will only ever return 10,000 hits at a time, +due to internal safety mechanisms in the Elasticsearch cluster. +==== + +.Getting all hits in a `SearchResult` +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java[tags=fetching-all-searchResult] +---- +==== -// TODO fetching all hits (warning) +.Getting all hits directly +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java[tags=fetching-all-hits] +---- +==== [[search-dsl-query-fetching-results-pagination]] ==== Pagination diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java index 4ed1f390dce..167b3c841ee 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java @@ -8,6 +8,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.Arrays; import java.util.List; import java.util.Optional; import javax.persistence.EntityManagerFactory; @@ -146,6 +147,37 @@ public void fetchingBasics() { } ); } + @Test + public void fetchingAllHits() { + OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { + SearchSession searchSession = Search.session( entityManager ); + // tag::fetching-all-searchResult[] + SearchResult result = searchSession.search( Book.class ) + .predicate( f -> f.id().matchingAny( Arrays.asList( 1, 2 ) ) ) + .fetch(); + + long totalHitCount = result.getTotalHitCount(); + List hits = result.getHits(); + // end::fetching-all-searchResult[] + + assertThat( totalHitCount ).isEqualTo( 2 ); + assertThat( hits ).extracting( Book::getId ) + .containsExactlyInAnyOrder( BOOK1_ID, BOOK2_ID ); + } ); + + OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { + SearchSession searchSession = Search.session( entityManager ); + // tag::fetching-all-hits[] + List hits = searchSession.search( Book.class ) + .predicate( f -> f.id().matchingAny( Arrays.asList( 1, 2 ) ) ) + .fetchHits(); + // end::fetching-all-hits[] + + assertThat( hits ).extracting( Book::getId ) + .containsExactlyInAnyOrder( BOOK1_ID, BOOK2_ID ); + } ); + } + @Test public void cacheLookupStrategy() { OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { From 0d59e932882716eecd72cf4ce47c57dbb612417e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 2 Sep 2019 16:22:53 +0200 Subject: [PATCH 5/7] HSEARCH-3615 Document pagination --- .../src/main/asciidoc/search-dsl.asciidoc | 32 +++++++++++++++++-- .../searchdsl/query/QueryDslIT.java | 28 ++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/asciidoc/search-dsl.asciidoc b/documentation/src/main/asciidoc/search-dsl.asciidoc index bcb7436ac73..9c4c018c6f6 100644 --- a/documentation/src/main/asciidoc/search-dsl.asciidoc +++ b/documentation/src/main/asciidoc/search-dsl.asciidoc @@ -173,9 +173,37 @@ include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDsl // Search 5 anchors backward compatibility [[_pagination]] -include::todo-placeholder.asciidoc[] +Pagination is the concept of splitting hits in successive "pages", +all pages containing a fixed number of elements (except potentially the last one). +When displaying results on a web page, +the user will be able to go to an arbitrary page and see the corresponding results, +for example "results 151 to 170 of 14,265". + +Pagination is achieved in Hibernate Search by passing a limit and offset to the `fetch` or `fetchHits` method: + +* The limit defines the maximum number of hits to return, i.e. the page size. +* The offset defines the number of documents that should be skipped because they were displayed in previous pages. +It is a *number of documents*, not a number of pages, +so you will usually want to compute it from the page number and page size this way: +`offset = zero-based-page-number * page-size`. + +.Pagination retrieving a `SearchResult` +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java[tags=fetching-pagination-searchResult] +---- +<1> Set the limit to `20` and the offset to `40`. +==== -// TODO https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_pagination +.Pagination retrieving hits directly +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java[tags=fetching-pagination-hits] +---- +<1> Set the limit to `20` and the offset to `40`. +==== [[search-dsl-query-fetching-results-scrolling]] ==== Scrolling diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java index 167b3c841ee..e0dbad60960 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java @@ -178,6 +178,34 @@ public void fetchingAllHits() { } ); } + @Test + public void pagination() { + OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { + SearchSession searchSession = Search.session( entityManager ); + // tag::fetching-pagination-searchResult[] + SearchResult result = searchSession.search( Book.class ) + .predicate( f -> f.matchAll() ) + .fetch( 20, 40 ); // <1> + // end::fetching-pagination-searchResult[] + long totalHitCount = result.getTotalHitCount(); + List hits = result.getHits(); + + assertThat( totalHitCount ).isEqualTo( 4 ); + assertThat( hits ).isEmpty(); + } ); + + OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { + SearchSession searchSession = Search.session( entityManager ); + // tag::fetching-pagination-hits[] + List hits = searchSession.search( Book.class ) + .predicate( f -> f.matchAll() ) + .fetchHits( 20, 40 ); // <1> + // end::fetching-pagination-hits[] + + assertThat( hits ).isEmpty(); + } ); + } + @Test public void cacheLookupStrategy() { OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { From 5359cf4f5915cd6c4645c1d25448314b8bea4def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 2 Sep 2019 17:36:34 +0200 Subject: [PATCH 6/7] HSEARCH-3615 Document SearchQuery --- .../src/main/asciidoc/search-dsl.asciidoc | 70 +++++++++++++++++-- .../searchdsl/query/QueryDslIT.java | 34 +++++++++ 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/documentation/src/main/asciidoc/search-dsl.asciidoc b/documentation/src/main/asciidoc/search-dsl.asciidoc index 9c4c018c6f6..83badc8dc6b 100644 --- a/documentation/src/main/asciidoc/search-dsl.asciidoc +++ b/documentation/src/main/asciidoc/search-dsl.asciidoc @@ -374,13 +374,75 @@ include::todo-placeholder.asciidoc[] [[search-dsl-query-object]] === Obtaining a query object -include::todo-placeholder.asciidoc[] +The example presented in most of this documentation fetch the query results +directly at the end of the query definition DSL, +not showing any "query" object that can be manipulated. +This is because the query object generally only makes code more verbose +without bringing anything worthwhile. + +However, in some cases a query object can be useful. +To get a query object, just call `toQuery()` at the end of the query definition: + +.Getting a `SearchQuery` object +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java[tags=searchQuery] +---- +<1> Build the query as usual. +<2> Retrieve a `SearchQuery` object. +<3> Fetch the results. +==== -// TODO toQuery() and potential uses (=> ORM query, explain(), ...) +This query object supports all <>. +The main advantage over calling these methods directly at the end of a query definition +is mostly related to debugging (see <>), +but the query object can also be useful if you need an adapter to another API. -// TODO turning a SearchQuery into a JPA/ORM query +Hibernate Search provides an adapter to JPA and Hibernate ORM's native APIs, +i.e. a way to turn a `SearchQuery` into a `javax.persistence.TypedQuery` (JPA) +or a `org.hibernate.query.Query` (native ORM API): -// TODO [[_resulttransformer]] https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_resulttransformer +.Getting a `SearchQuery` object +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java[tags=searchQuery-toORM] +---- +<1> Build the query as usual. +<2> Retrieve a `SearchQuery` object. +<3> Turn the `SearchQuery` object into a JPA query. +<4> Turn the `SearchQuery` object into a Hibernate ORM query. +==== + +// Search 5 anchors backward compatibility +[[_resulttransformer]] +[WARNING] +==== +The resulting query *does not support all operations*, +so is recommended to only convert search queries when absolutely required, +for example when integrating with code that only works with Hibernate ORM queries. + +The following operations are expected to work correctly in most cases, +even though they may behave slightly differently from what is expected from a JPA `TypedQuery` +or Hibernate ORM `Query` in some cases +(including, but not limited to, the type of thrown exceptions): + +* Hit retrieval methods (`list`, `getResultList`, `uniqueResult`, ... ). +* `setFirstResult`/`setMaxResults` and getters. +* `setFetchSize` +* `unwrap` + +The following operations are known not to work correctly, +with no plan to fix them at the moment: + +* Hints (`setHint`, ...). +* Parameter-related methods (`setParameter`, ...). +* Result transformer (`setResultTransformer`, ...); +use <> instead. +* Lock-related methods (`setLockOptions`, ...). +* And more (this list is not exhaustive). +==== [[search-dsl-query-debugging]] === Debugging a query diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java index e0dbad60960..865be069094 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java @@ -17,6 +17,7 @@ import org.hibernate.SessionFactory; import org.hibernate.cfg.AvailableSettings; import org.hibernate.search.documentation.testsupport.BackendConfigurations; +import org.hibernate.search.engine.search.query.SearchQuery; import org.hibernate.search.engine.search.query.SearchResult; import org.hibernate.search.mapper.orm.Search; import org.hibernate.search.mapper.orm.automaticindexing.AutomaticIndexingSynchronizationStrategyName; @@ -206,6 +207,39 @@ public void pagination() { } ); } + @Test + public void searchQuery() { + OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { + SearchSession searchSession = Search.session( entityManager ); + // tag::searchQuery[] + SearchQuery query = searchSession.search( Book.class ) // <1> + .predicate( f -> f.matchAll() ) + .toQuery(); // <2> + List hits = query.fetchHits( 20 ); // <3> + // end::searchQuery[] + + assertThat( hits ).extracting( Book::getId ) + .containsExactlyInAnyOrder( BOOK1_ID, BOOK2_ID, BOOK3_ID, BOOK4_ID ); + } ); + + OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { + SearchSession searchSession = Search.session( entityManager ); + // tag::searchQuery-toORM[] + SearchQuery query = searchSession.search( Book.class ) // <1> + .predicate( f -> f.matchAll() ) + .toQuery(); // <2> + javax.persistence.TypedQuery jpaQuery = Search.toJpaQuery( query ); // <3> + org.hibernate.query.Query ormQuery = Search.toOrmQuery( query ); // <4> + // end::searchQuery-toORM[] + List hits = jpaQuery.getResultList(); + assertThat( hits ).extracting( Book::getId ) + .containsExactlyInAnyOrder( BOOK1_ID, BOOK2_ID, BOOK3_ID, BOOK4_ID ); + hits = ormQuery.list(); + assertThat( hits ).extracting( Book::getId ) + .containsExactlyInAnyOrder( BOOK1_ID, BOOK2_ID, BOOK3_ID, BOOK4_ID ); + } ); + } + @Test public void cacheLookupStrategy() { OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { From 2ad1cbe28046875745973a98f77e0f351d81a5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 2 Sep 2019 18:35:29 +0200 Subject: [PATCH 7/7] HSEARCH-3615 Document how to debug search queries --- .../src/main/asciidoc/search-dsl.asciidoc | 84 ++++++++++++++++++- .../documentation/searchdsl/query/Book.java | 2 +- .../searchdsl/query/QueryDslIT.java | 66 +++++++++++++++ 3 files changed, 147 insertions(+), 5 deletions(-) diff --git a/documentation/src/main/asciidoc/search-dsl.asciidoc b/documentation/src/main/asciidoc/search-dsl.asciidoc index 83badc8dc6b..3f54768299b 100644 --- a/documentation/src/main/asciidoc/search-dsl.asciidoc +++ b/documentation/src/main/asciidoc/search-dsl.asciidoc @@ -450,16 +450,80 @@ use <> instead. [[search-dsl-query-debugging-matches]] ==== Explaining matches -include::todo-placeholder.asciidoc[] -// TODO toString(), getQueryString(), and logging, too? +When some documents unexpectedly match or don't match, +you will need information about the exact query being executed, +and about the index content. + +To gain insight about what ends up being executed exactly, +one option is to <> +using `toQuery()` at the end of the query definition, +then call `toString()` to get a String representation of that query. + +Another option is to take advantage of logs: +all executed search queries are logged to the log category `org.hibernate.search.query` +at the `DEBUG` level. + +You may also need to inspect the content of the index. +This is rather obvious with Elasticsearch: run simpler queries using either Hibernate Search or the REST APIs directly. +For the Lucene backend, +https://medium.com/@mocobeta/luke-become-an-apache-lucene-module-as-of-lucene-8-1-7d139c998b2[use the Luke tool] +distributed as part of the https://lucene.apache.org/core/downloads.html[Lucene binary packages]. [[search-dsl-query-debugging-scores]] ==== Explaining scores // Search 5 anchors backward compatibility [[_understanding_results]] -include::todo-placeholder.asciidoc[] -// TODO https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_understanding_results +When the score of some documents is higher or lower than expected, +the best way to gain insight is to <> +using `toQuery()` at the end of the query definition, +and then use the backend-specific `explain` methods; +the result of these methods will explain how the score of a specific document was computed. +See below for examples. + +To retrieve an explanation for all matches in one call, +`explanation` projections are available: +see <> +and <>. + +[WARNING] +==== +Regardless of the API used, explanations are rather costly performance-wise: +only use them for debugging purposes. +==== + +.Retrieving score explanation -- Lucene +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java[tags=explain-lucene] +---- +<1> Build the query as usual, +but using the Lucene extension so that the retrieved query exposes Lucene-specific operations. +<2> Retrieve a `SearchQuery` object. +<3> Retrieve the explanation of the score of the document with ID `1`. +The explanation is of type `Explanation`, but you can convert it to a readable string using `toString()`. +<4> For multi-index queries, it is necessary to refer to the document not only by its ID, +but also by the name of the index it's located in. +<5> If you cannot change the code building the query to use the Lucene extension, +you can instead use the Lucene extension on the `SearchQuery` to convert it after its creation. +==== + +.Retrieving score explanation -- Elasticsearch +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java[tags=explain-elasticsearch] +---- +<1> Build the query as usual, +but using the Elasticsearch extension so that the retrieved query exposes Elasticsearch-specific operations. +<2> Retrieve a `SearchQuery` object. +<3> Retrieve the explanation of the score of the document with ID `1`. +<4> For multi-index queries, it is necessary to refer to the document not only by its ID, +but also by the name of the index it's located in. +<5> If you cannot change the code building the query to use the Elasticsearch extension, +you can instead use the Elasticsearch extension on the `SearchQuery` to convert it after its creation. +==== [[search-dsl-predicate]] == Predicate DSL @@ -1453,6 +1517,12 @@ include::{sourcedir}/org/hibernate/search/documentation/searchdsl/projection/Pro [[search-dsl-projection-extensions-lucene-explanation]] ==== Lucene: `explanation` +[WARNING] +==== +Explanations are rather costly performance-wise: +only use them for <> purposes. +==== + .Returning the score explanation as a native `org.apache.lucene.search.Explanation` ==== [source, JAVA, indent=0, subs="+callouts"] @@ -1475,6 +1545,12 @@ include::{sourcedir}/org/hibernate/search/documentation/searchdsl/projection/Pro [[search-dsl-projection-extensions-elasticsearch-explanation]] ==== Elasticsearch: `explanation` +[WARNING] +==== +Explanations are rather costly performance-wise: +only use them for <> purposes. +==== + .Returning the score explanation as a JSON-formatted String ==== [source, JAVA, indent=0, subs="+callouts"] diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/Book.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/Book.java index aec143190dd..9881f7efbaf 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/Book.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/Book.java @@ -15,7 +15,7 @@ import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed; @Entity -@Indexed +@Indexed(index = "Book") @Cacheable public class Book { diff --git a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java index 865be069094..2cdebedf543 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/searchdsl/query/QueryDslIT.java @@ -16,7 +16,13 @@ import org.hibernate.SessionFactory; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.search.backend.elasticsearch.ElasticsearchExtension; +import org.hibernate.search.backend.elasticsearch.search.query.ElasticsearchSearchQuery; +import org.hibernate.search.backend.lucene.LuceneExtension; +import org.hibernate.search.backend.lucene.search.query.LuceneSearchQuery; import org.hibernate.search.documentation.testsupport.BackendConfigurations; +import org.hibernate.search.documentation.testsupport.ElasticsearchBackendConfiguration; +import org.hibernate.search.documentation.testsupport.LuceneBackendConfiguration; import org.hibernate.search.engine.search.query.SearchQuery; import org.hibernate.search.engine.search.query.SearchResult; import org.hibernate.search.mapper.orm.Search; @@ -29,12 +35,15 @@ import org.hibernate.search.util.impl.integrationtest.orm.OrmUtils; import org.hibernate.stat.Statistics; +import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.apache.lucene.search.Explanation; + @RunWith(Parameterized.class) public class QueryDslIT { @@ -51,10 +60,13 @@ public static Object[] backendConfigurations() { @Rule public OrmSetupHelper setupHelper; + private final BackendConfiguration backendConfiguration; + private EntityManagerFactory entityManagerFactory; public QueryDslIT(BackendConfiguration backendConfiguration) { this.setupHelper = OrmSetupHelper.withSingleBackend( backendConfiguration ); + this.backendConfiguration = backendConfiguration; } @Before @@ -240,6 +252,60 @@ public void searchQuery() { } ); } + @Test + public void explain_lucene() { + Assume.assumeTrue( backendConfiguration instanceof LuceneBackendConfiguration ); + + OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { + SearchSession searchSession = Search.session( entityManager ); + // tag::explain-lucene[] + LuceneSearchQuery query = searchSession.search( Book.class ) + .extension( LuceneExtension.get() ) // <1> + .predicate( f -> f.match() + .onField( "title" ) + .matching( "robot" ) ) + .toQuery(); // <2> + + Explanation explanation1 = query.explain( "1" ); // <3> + Explanation explanation2 = query.explain( "Book", "1" ); // <4> + + LuceneSearchQuery luceneQuery = query.extension( LuceneExtension.get() ); // <5> + // end::explain-lucene[] + + assertThat( explanation1 ).asString() + .contains( "title" ); + assertThat( explanation2 ).asString() + .contains( "title" ); + assertThat( luceneQuery ).isNotNull(); + } ); + } + + @Test + public void explain_elasticsearch() { + Assume.assumeTrue( backendConfiguration instanceof ElasticsearchBackendConfiguration ); + + OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> { + SearchSession searchSession = Search.session( entityManager ); + // tag::explain-elasticsearch[] + ElasticsearchSearchQuery query = searchSession.search( Book.class ) + .extension( ElasticsearchExtension.get() ) // <1> + .predicate( f -> f.match() + .onField( "title" ) + .matching( "robot" ) ) + .toQuery(); // <2> + + String explanation1 = query.explain( "1" ); // <3> + String explanation2 = query.explain( "Book", "1" ); // <4> + + ElasticsearchSearchQuery elasticsearchQuery = query.extension( ElasticsearchExtension.get() ); // <5> + // end::explain-elasticsearch[] + + assertThat( explanation1 ).contains( "title" ); + assertThat( explanation2 ).contains( "title" ); + assertThat( elasticsearchQuery ).isNotNull(); + } ); + } + @Test public void cacheLookupStrategy() { OrmUtils.withinJPATransaction( entityManagerFactory, entityManager -> {