From 74f1327b5dbded6b777b3ad5cf6109d44723a5ff Mon Sep 17 00:00:00 2001 From: Sylvain Wallez Date: Mon, 18 Apr 2022 16:15:04 +0200 Subject: [PATCH] [7.17] Restructure and enhance docs (#243) (#247) --- docs/api-conventions.asciidoc | 274 ------------------ .../blocking-and-async.asciidoc | 19 ++ .../api-conventions/building-objects.asciidoc | 68 +++++ docs/api-conventions/exceptions.asciidoc | 15 + docs/api-conventions/index.asciidoc | 32 ++ docs/api-conventions/lists-and-maps.asciidoc | 40 +++ .../loading-json.asciidoc | 22 +- docs/api-conventions/method-naming.asciidoc | 15 + .../object-lifecycles.asciidoc | 31 ++ .../package-structure.asciidoc | 26 ++ docs/api-conventions/variant-types.asciidoc | 60 ++++ docs/external-resources.asciidoc | 10 + .../{ => getting-started}/connecting.asciidoc | 10 +- docs/getting-started/index.asciidoc | 12 + .../installation.asciidoc | 8 +- .../migrate-hlrc.asciidoc} | 10 +- docs/index-local.asciidoc | 1 + docs/index.asciidoc | 21 +- docs/introduction.asciidoc | 27 +- docs/troubleshooting/index.asciidoc | 13 + docs/usage/aggregations.asciidoc | 40 +++ docs/usage/index.asciidoc | 31 ++ docs/usage/indexing-bulk.asciidoc | 52 ++++ docs/usage/indexing.asciidoc | 61 ++++ docs/usage/indices.asciidoc | 4 + docs/usage/reading.asciidoc | 40 +++ docs/usage/searching.asciidoc | 46 +++ .../documentation/DocTestsTransport.java | 8 +- .../ApiConventionsTest.java | 41 +-- .../LoadingJsonTest.java | 9 +- .../{ => getting_started}/ConnectingTest.java | 5 +- .../MigrateHlrcTest.java | 2 +- .../documentation/usage/AggregationsTest.java | 107 +++++++ .../documentation/usage/IndexingBulkTest.java | 130 +++++++++ .../documentation/usage/IndexingTest.java | 165 +++++++++++ .../clients/documentation/usage/Product.java | 57 ++++ .../documentation/usage/ReadingTest.java | 97 +++++++ .../documentation/usage/SearchingTest.java | 136 +++++++++ .../json/JsonpMappingExceptionTest.java | 2 +- .../{ => api_conventions}/some-index.json | 0 40 files changed, 1383 insertions(+), 364 deletions(-) delete mode 100644 docs/api-conventions.asciidoc create mode 100644 docs/api-conventions/blocking-and-async.asciidoc create mode 100644 docs/api-conventions/building-objects.asciidoc create mode 100644 docs/api-conventions/exceptions.asciidoc create mode 100644 docs/api-conventions/index.asciidoc create mode 100644 docs/api-conventions/lists-and-maps.asciidoc rename docs/{ => api-conventions}/loading-json.asciidoc (85%) create mode 100644 docs/api-conventions/method-naming.asciidoc create mode 100644 docs/api-conventions/object-lifecycles.asciidoc create mode 100644 docs/api-conventions/package-structure.asciidoc create mode 100644 docs/api-conventions/variant-types.asciidoc create mode 100644 docs/external-resources.asciidoc rename docs/{ => getting-started}/connecting.asciidoc (86%) create mode 100644 docs/getting-started/index.asciidoc rename docs/{ => getting-started}/installation.asciidoc (95%) rename docs/{migrate.asciidoc => getting-started/migrate-hlrc.asciidoc} (88%) create mode 100644 docs/troubleshooting/index.asciidoc create mode 100644 docs/usage/aggregations.asciidoc create mode 100644 docs/usage/index.asciidoc create mode 100644 docs/usage/indexing-bulk.asciidoc create mode 100644 docs/usage/indexing.asciidoc create mode 100644 docs/usage/indices.asciidoc create mode 100644 docs/usage/reading.asciidoc create mode 100644 docs/usage/searching.asciidoc rename java-client/src/test/java/co/elastic/clients/documentation/{ => api_conventions}/ApiConventionsTest.java (88%) rename java-client/src/test/java/co/elastic/clients/documentation/{ => api_conventions}/LoadingJsonTest.java (94%) rename java-client/src/test/java/co/elastic/clients/documentation/{ => getting_started}/ConnectingTest.java (95%) rename java-client/src/test/java/co/elastic/clients/documentation/{ => getting_started}/MigrateHlrcTest.java (97%) create mode 100644 java-client/src/test/java/co/elastic/clients/documentation/usage/AggregationsTest.java create mode 100644 java-client/src/test/java/co/elastic/clients/documentation/usage/IndexingBulkTest.java create mode 100644 java-client/src/test/java/co/elastic/clients/documentation/usage/IndexingTest.java create mode 100644 java-client/src/test/java/co/elastic/clients/documentation/usage/Product.java create mode 100644 java-client/src/test/java/co/elastic/clients/documentation/usage/ReadingTest.java create mode 100644 java-client/src/test/java/co/elastic/clients/documentation/usage/SearchingTest.java rename java-client/src/test/resources/co/elastic/clients/documentation/{ => api_conventions}/some-index.json (100%) diff --git a/docs/api-conventions.asciidoc b/docs/api-conventions.asciidoc deleted file mode 100644 index 9fc71b53a..000000000 --- a/docs/api-conventions.asciidoc +++ /dev/null @@ -1,274 +0,0 @@ -[[api-conventions]] -== API conventions - -The {java-client} uses a very consistent code structure, using modern code -patterns that make complex requests easier to write and complex responses easier -to process. This page explains these so that you quickly feel at home. - -[discrete] -=== Package structure and namespace clients - -The {es} API is large and is organized into feature groups, as can be seen in -the {ref}/rest-apis.html[{es} API documentation]. - -The {java-client} follows this structure: feature groups are called “namespaces”, -and each namespace is located in a subpackage of -`co.elastic.clients.elasticsearch`. - -Each of the namespace clients can be accessed from the top level {es} client. The -only exceptions are the “search” and “document” APIs which are located in the `core` -subpackage and can be accessed on the main {es} client object. - -The snippet below shows how to use the indices namespace client to create an -index (the lambda syntax is <>): - -["source","java"] --------------------------------------------------- -// Create the "products" index -ElasticsearchClient client = ... -client.indices().create(c -> c.index("products")); --------------------------------------------------- - -Namespace clients are very lightweight objects that can be created on the fly. - - -[discrete] -=== Method naming conventions - -Classes in the {java-client} contain two kinds of methods and properties: - -* Methods and properties that are part of the API, such as -`ElasticsearchClient.search()` or `SearchResponse.maxScore()`. They are derived -from their respective names in the {es} JSON API using the standard Java -`camelCaseNaming` convention. - -* Methods and properties that are part of the framework on which the Java API -Client is built, such as `Query._kind()`. These methods and properties are -prefixed with an underscore to both avoid any naming conflicts with API names, -and as an easy way to distinguish the API from the framework. - - -[discrete] -[[builder-lambdas]] -=== Immutable objects, builders and builder lambdas - -All data types in the {java-client} are immutable. Object creation uses the -https://www.informit.com/articles/article.aspx?p=1216151&seqNum=2[builder pattern] -that was popularized in *Effective Java* in 2008. - -["source","java"] --------------------------------------------------- -ElasticsearchClient client = ... -include-tagged::{doc-tests}/ApiConventionsTest.java[builders] --------------------------------------------------- - -Note that a builder should not be reused after its `build()` method has been -called. - -Although this works nicely, having to instantiate builder classes and call the -`build()` method is a bit verbose. So every property setter in the {java-client} also -accepts a lambda expression that takes a newly created builder as a parameter -and returns a populated builder. The snippet above can also be written as: - -["source","java"] --------------------------------------------------- -ElasticsearchClient client = ... -include-tagged::{doc-tests}/ApiConventionsTest.java[builder-lambdas] --------------------------------------------------- - -This approach allows for much more concise code, and also avoids importing -classes (and even remembering their names) since types are inferred from the -method parameter signature. - -Note in the above example that builder variables are only used to start a chain -of property setters. The names of these variables are therefore unimportant and -can be shortened to improve readability: - -["source","java"] --------------------------------------------------- -ElasticsearchClient client = ... -include-tagged::{doc-tests}/ApiConventionsTest.java[builder-lambdas-short] --------------------------------------------------- - - -Builder lambdas become particularly useful with complex nested queries like the -one below, taken from the -{ref}/query-dsl-intervals-query.html[intervals query API documentation]. - -This example also highlights a useful naming convention for builder parameters in -deeply nested structures. For lambda expressions with a single argument, Kotlin -provides the implicit `it` parameter and Scala allows use of `_`. This can be approximated -in Java by using an underscore prefix followed by a number representing the depth -level (i.e. `_0`, `_1`, and so on). Not only does this remove the need to create -throw-away variable names, but it also improves code readability. Correct indentation -also allows the structure of the query to stand out. - -["source","java"] --------------------------------------------------- -ElasticsearchClient client = ... -include-tagged::{doc-tests}/ApiConventionsTest.java[builder-intervals] --------------------------------------------------- -<1> Search results will be mapped to `SomeApplicationData` instances to - be readily available to the application. - -[discrete] -=== Lists and maps - -[discrete] -==== Additive builder setters - -Properties of type `List` and `Map` are exposed by object builders as a set of overloaded -additive-only methods that _update_ the property value, by appending to lists and adding -new entries to maps (or replacing existing ones). - -Object builders create immutable objects, and this also applies to list and map properties -that are made immutable at object construction time. - -["source","java"] --------------------------------------------------- -include-tagged::{doc-tests}/ApiConventionsTest.java[collections] --------------------------------------------------- - -[discrete] -==== List and map values are never `null` - -The {es} API has a lot of optional properties. For single-valued properties, the {java-client} -represents missing optional values as `null`. Applications therefore have to null-check -optional values before using them. - -For lists and maps however, applications often only care about whether they're empty or not, -or even just iterate on their content. Using `null` values is then cumbersome. To avoid this, -{java-client} collection properties are never `null`, and missing optional collections are -returned as an empty collection. - -If you ever need to distinguish between a missing (undefined) optional collection and an -effectively-empty collection returned by {es}, the `ApiTypeHelper` class provides a utility -method to distinguish them: - -["source","java"] --------------------------------------------------- -include-tagged::{doc-tests}/ApiConventionsTest.java[optional-collections] --------------------------------------------------- - - -[discrete] -=== Variant types - -The {es} API has a lot of variant types: queries, aggregations, field mappings, -analyzers, and so on. Finding the correct class name in such large collections -can be challenging. - -The {java-client} builders make this easy: the builders for variant types, such as -`Query`, have methods for each of the available implementations. We’ve seen this -in action above with `intervals` (a kind of query) and `allOf`, `match` and -`anyOf` (various kinds of intervals). - -This is because variant objects in the {java-client} are implementations of a -“tagged union”: they contain the identifier (or tag) of the variant they hold -and the value for that variant. For example, a `Query` object can contain an -`IntervalsQuery` with tag `intervals`, a `TermQuery` with tag `term`, and so on. -This approach allows writing fluent code where you can let the IDE completion -features guide you to build and navigate complex nested structures: - -Variant builders have setter methods for every available implementation. They -use the same conventions as regular properties and accept both a builder lambda -expression and a ready-made object of the actual type of the variant. Here’s an -example to build a term query: - -["source","java"] --------------------------------------------------- -include-tagged::{doc-tests}/ApiConventionsTest.java[variant-creation] --------------------------------------------------- -<1> Choose the `term` variant to build a term query. -<2> Build the terms query with a builder lambda expression. -<3> Build the `Query` that now holds a `TermQuery` object of kind `term`. - -Variant objects have getter methods for every available implementation. These -methods check that the object actually holds a variant of that kind and return -the value downcasted to the correct type. They throw an `IllegalStateException` -otherwise. This approach allows writing fluent code to traverse variants. - -["source","java"] --------------------------------------------------- -include-tagged::{doc-tests}/ApiConventionsTest.java[variant-navigation] --------------------------------------------------- - -Variant objects also provide information on the variant kind they currently hold: - -* with `is` methods for each of the variant kinds: `isTerm()`, `isIntervals()`, `isFuzzy()`, etc. - -* with a nested `Kind` enumeration that defines all variant kinds. - -This information can then be used to navigate down into specific variants after checking -their actual kind: - -["source","java"] --------------------------------------------------- -include-tagged::{doc-tests}/ApiConventionsTest.java[variant-kind] --------------------------------------------------- -<1> Test if the variant is of a specific kind. -<2> Test a larger set of variant kinds. -<3> Get the kind and value held by the variant object. - -[discrete] -=== Blocking and asynchronous clients - -API clients come in two flavors: blocking and asynchronous. All methods on -asynchronous clients return a standard `CompletableFuture`. - -Both flavors can be used at the same time depending on your needs, sharing the -same transport object: - -["source","java"] --------------------------------------------------- -ElasticsearchTransport transport = ... - -include-tagged::{doc-tests}/ApiConventionsTest.java[blocking-and-async] --------------------------------------------------- - -[discrete] -=== Exceptions - -Client methods can throw two kinds of exceptions: - -* Requests that were received by the {es} server but that were rejected -(validation error, server internal timeout exceeded, etc) will produce an -`ElasticsearchException`. This exception contains details about the error, -provided by {es}. - -* Requests that failed to reach the server (network error, server unavailable, -etc) will produce a `TransportException`. That exception's cause is the exception -thrown by the lower-level implementation. In the case of the `RestClientTransport` -it will be a `ResponseException` that contains the low level HTTP response. - -[discrete] -=== Object life cycles - -There are five kinds of objects in the {java-client} with different life cycles: - - -**Object mapper**:: -Stateless and thread-safe, but can be costly to create. -It’s usually a singleton that is created at application startup and used to -create the transport. - -**Transport**:: -Thread-safe, holds network resources through the underlying HTTP client. A -transport object is associated with an {es} cluster and has to be explicitly -closed to release the underlying resources such as network connections. - -**Clients**:: -Immutable, stateless and thread-safe. -These are very lightweight objects that just wrap a transport and provide API -endpoints as methods. - -**Builders**:: -Mutable, non thread-safe. -Builders are transient objects that should not be reused after calling -`build()`. - -**Requests & other API objects**:: -Immutable, thread-safe. -If your application uses the same request or same parts of a request over and -over, these objects can be prepared in advance and reused across multiple calls -over multiple clients with different transports. diff --git a/docs/api-conventions/blocking-and-async.asciidoc b/docs/api-conventions/blocking-and-async.asciidoc new file mode 100644 index 000000000..41a880002 --- /dev/null +++ b/docs/api-conventions/blocking-and-async.asciidoc @@ -0,0 +1,19 @@ +[[blocking-and-async]] +=== Blocking and asynchronous clients + +API clients come in two flavors: blocking and asynchronous. All methods on +asynchronous clients return a standard `CompletableFuture`. + +Both flavors can be used at the same time depending on your needs, sharing the +same transport object: + +["source","java"] +-------------------------------------------------- +ElasticsearchTransport transport = ... + +include-tagged::{doc-tests-src}/api_conventions/ApiConventionsTest.java[blocking-and-async] +-------------------------------------------------- + +Although we won't go in deeper details on asynchronous programming in Java, remember to handle failures of asynchronous tasks. It's easy to overlook them and have errors go unnoticed. + +{doc-tests-blurb} diff --git a/docs/api-conventions/building-objects.asciidoc b/docs/api-conventions/building-objects.asciidoc new file mode 100644 index 000000000..db3e3e493 --- /dev/null +++ b/docs/api-conventions/building-objects.asciidoc @@ -0,0 +1,68 @@ +[[building-objects]] +=== Building API objects + +[discrete] +==== Builder objects + +All data types in the {java-client} are immutable. Object creation uses the +https://www.informit.com/articles/article.aspx?p=1216151&seqNum=2[builder pattern] +that was popularized in *Effective Java* in 2008. + +["source","java"] +-------------------------------------------------- +ElasticsearchClient client = ... +include-tagged::{doc-tests-src}/api_conventions/ApiConventionsTest.java[builders] +-------------------------------------------------- + +Note that a builder should not be reused after its `build()` method has been +called. + +[discrete] +==== Builder lambda expressions + +Although this works nicely, having to instantiate builder classes and call the +`build()` method is a bit verbose. So every property setter in the {java-client} also +accepts a lambda expression that takes a newly created builder as a parameter +and returns a populated builder. The snippet above can also be written as: + +["source","java"] +-------------------------------------------------- +ElasticsearchClient client = ... +include-tagged::{doc-tests-src}/api_conventions/ApiConventionsTest.java[builder-lambdas] +-------------------------------------------------- + +This approach allows for much more concise code, and also avoids importing +classes (and even remembering their names) since types are inferred from the +method parameter signature. + +Note in the above example that builder variables are only used to start a chain +of property setters. The names of these variables are therefore unimportant and +can be shortened to improve readability: + +["source","java"] +-------------------------------------------------- +ElasticsearchClient client = ... +include-tagged::{doc-tests-src}/api_conventions/ApiConventionsTest.java[builder-lambdas-short] +-------------------------------------------------- + +Builder lambdas become particularly useful with complex nested queries like the +one below, taken from the +{ref}/query-dsl-intervals-query.html[intervals query API documentation]. + +This example also highlights a useful naming convention for builder parameters in +deeply nested structures. For lambda expressions with a single argument, Kotlin +provides the implicit `it` parameter and Scala allows use of `_`. This can be approximated +in Java by using an underscore or a single letter prefix followed by a number representing the depth +level (i.e. `_0`, `_1`, or `b0`, `b1` and so on). Not only does this remove the need to create +throw-away variable names, but it also improves code readability. Correct indentation +also allows the structure of the query to stand out. + +["source","java"] +-------------------------------------------------- +ElasticsearchClient client = ... +include-tagged::{doc-tests-src}/api_conventions/ApiConventionsTest.java[builder-intervals] +-------------------------------------------------- +<1> Search results will be mapped to `SomeApplicationData` instances to +be readily available to the application. + +{doc-tests-blurb} diff --git a/docs/api-conventions/exceptions.asciidoc b/docs/api-conventions/exceptions.asciidoc new file mode 100644 index 000000000..43d730ec9 --- /dev/null +++ b/docs/api-conventions/exceptions.asciidoc @@ -0,0 +1,15 @@ +[[exceptions]] +=== Exceptions + +Client methods can throw two kinds of exceptions: + +* Requests that were received by the {es} server but that were rejected +(validation error, server internal timeout exceeded, etc) will produce an +`ElasticsearchException`. This exception contains details about the error, +provided by {es}. + +* Requests that failed to reach the server (network error, server unavailable, +etc) will produce a `TransportException`. That exception's cause is the exception +thrown by the lower-level implementation. In the case of the `RestClientTransport` +it will be a `ResponseException` that contains the low level HTTP response. + diff --git a/docs/api-conventions/index.asciidoc b/docs/api-conventions/index.asciidoc new file mode 100644 index 000000000..7c4373d91 --- /dev/null +++ b/docs/api-conventions/index.asciidoc @@ -0,0 +1,32 @@ +[[api-conventions]] +== API conventions + +The {java-client} uses a very consistent code structure, using modern code +patterns that make complex requests easier to write and complex responses easier +to process. The sections below explain these in details. + +* <> +* <> +* <> +* <> +* <> +* <> +* <> +ifdef::is_main_branch,v8_1_1_released,v7_17_2_released[] +* <> +endif::is_main_branch,v8_1_1_released,v7_17_2_released[] +* <> + +include::package-structure.asciidoc[] +include::method-naming.asciidoc[] +include::blocking-and-async.asciidoc[] +include::building-objects.asciidoc[] +include::lists-and-maps.asciidoc[] +include::variant-types.asciidoc[] +include::object-lifecycles.asciidoc[] + +ifdef::is_main_branch,v8_1_1_released,v7_17_2_released[] +include::loading-json.asciidoc[] +endif::is_main_branch,v8_1_1_released,v7_17_2_released[] + +include::exceptions.asciidoc[] diff --git a/docs/api-conventions/lists-and-maps.asciidoc b/docs/api-conventions/lists-and-maps.asciidoc new file mode 100644 index 000000000..117295b50 --- /dev/null +++ b/docs/api-conventions/lists-and-maps.asciidoc @@ -0,0 +1,40 @@ +[[lists-and-maps]] +=== Lists and maps + +[discrete] +==== Additive builder setters + +Properties of type `List` and `Map` are exposed by object builders as a set of overloaded +additive-only methods that _update_ the property value, by appending to lists and adding +new entries to maps (or replacing existing ones). + +Object builders create immutable objects, and this also applies to list and map properties +that are made immutable at object construction time. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/api_conventions/ApiConventionsTest.java[collections] +-------------------------------------------------- + +[discrete] +==== List and map values are never `null` + +The {es} API has a lot of optional properties. For single-valued properties, the {java-client} +represents missing optional values as `null`. Applications therefore have to null-check +optional values before using them. + +For lists and maps however, applications often only care about whether they're empty or not, +or even just iterate on their content. Using `null` values is then cumbersome. To avoid this, +{java-client} collection properties are never `null`, and missing optional collections are +returned as an empty collection. + +If you ever need to distinguish between a missing (undefined) optional collection and an +effectively-empty collection returned by {es}, the `ApiTypeHelper` class provides a utility +method to distinguish them: + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/api_conventions/ApiConventionsTest.java[optional-collections] +-------------------------------------------------- + +{doc-tests-blurb} diff --git a/docs/loading-json.asciidoc b/docs/api-conventions/loading-json.asciidoc similarity index 85% rename from docs/loading-json.asciidoc rename to docs/api-conventions/loading-json.asciidoc index 39a3b2293..5c50f4a1a 100644 --- a/docs/loading-json.asciidoc +++ b/docs/api-conventions/loading-json.asciidoc @@ -1,5 +1,5 @@ [[loading-json]] -== Creating API objects from JSON data +=== Creating API objects from JSON data ifeval::["{minor-version}" == "7.17"] NOTE: This feature was added in version 7.17.2 @@ -16,10 +16,10 @@ As translating these JSON snippets to Java code can be time-consuming and error- Under the hood, the `withJson()` methods call the object's deserializer. The JSON text's structure and value types therefore have to be correct for the target data structure. Using `withJson()` keeps the strong typing guarantees of the {java-client}. [discrete] -=== Examples +==== Examples [discrete] -==== Loading an index definition from a resource file +===== Loading an index definition from a resource file Consider a resource file `some-index.json` containing an index definition: @@ -38,43 +38,43 @@ You can create an index from that definition as follows: ["source","java"] -------------------------------------------------- -include-tagged::{doc-tests}/LoadingJsonTest.java[load-index] +include-tagged::{doc-tests-src}/api_conventions/LoadingJsonTest.java[load-index] -------------------------------------------------- <1> open an input stream for the JSON resource file. <2> populate the index creation request with the resource file contents. [discrete] -==== Ingesting documents from JSON files +===== Ingesting documents from JSON files Similarly, you can read documents to be stored in {es} from data files: ["source","java"] -------------------------------------------------- -include-tagged::{doc-tests}/LoadingJsonTest.java[ingest-data] +include-tagged::{doc-tests-src}/api_conventions/LoadingJsonTest.java[ingest-data] -------------------------------------------------- <1> when calling `withJson()` on data structures that have generic type parameters, these generic types will be considered to be `JsonData`. [discrete] -==== Creating a search request combining JSON and programmatic construction +===== Creating a search request combining JSON and programmatic construction You can combine `withJson()` with regular calls to setter methods. The example below loads the query part of a search request from a `String` and programmatically adds an aggregation. ["source","java"] -------------------------------------------------- -include-tagged::{doc-tests}/LoadingJsonTest.java[query] +include-tagged::{doc-tests-src}/api_conventions/LoadingJsonTest.java[query] -------------------------------------------------- <1> loads the query from the JSON string. <2> adds the aggregation. <3> since this is an aggregation we don't care about result documents and set their target class to `Void`, meaning they will just be ignored. Note that setting `size` to zero actually prevents any document from being returned. [discrete] -==== Creating a search request from multiple JSON snippets +===== Creating a search request from multiple JSON snippets The `withJson()` methods are partial deserializers: the properties loaded from the JSON will set property values or replace the previous ones, but will not reset other properties not found in the JSON input. You can use this to combine multiple JSON snippets to build complex search requests. In the example below, we combine separate definitions of a query that selects some documents and an aggregation that is run on the results of this query. ["source","java"] -------------------------------------------------- -include-tagged::{doc-tests}/LoadingJsonTest.java[query-and-agg] +include-tagged::{doc-tests-src}/api_conventions/LoadingJsonTest.java[query-and-agg] -------------------------------------------------- <1> set max number of returned document to 100 for queries. <2> we do not want any matching document in aggregations. @@ -83,3 +83,5 @@ include-tagged::{doc-tests}/LoadingJsonTest.java[query-and-agg] <5> additional request properties set programmatically. Notice that order matters when the JSON snippets have some common properties: just as when setting property values programmatically, the last value that is set for a property overwrites the previous one. + +{doc-tests-blurb} diff --git a/docs/api-conventions/method-naming.asciidoc b/docs/api-conventions/method-naming.asciidoc new file mode 100644 index 000000000..704f8a9c3 --- /dev/null +++ b/docs/api-conventions/method-naming.asciidoc @@ -0,0 +1,15 @@ +[[method-naming]] +=== Method naming conventions + +Classes in the {java-client} contain two kinds of methods and properties: + +* Methods and properties that are part of the API, such as +`ElasticsearchClient.search()` or `SearchResponse.maxScore()`. They are derived +from their respective names in the {es} JSON API using the standard Java +`camelCaseNaming` convention. + +* Methods and properties that are part of the framework on which the Java API +Client is built, such as `Query._kind()`. These methods and properties are +prefixed with an underscore to both avoid any naming conflicts with API names, +and as an easy way to distinguish the API from the framework. + diff --git a/docs/api-conventions/object-lifecycles.asciidoc b/docs/api-conventions/object-lifecycles.asciidoc new file mode 100644 index 000000000..25348d027 --- /dev/null +++ b/docs/api-conventions/object-lifecycles.asciidoc @@ -0,0 +1,31 @@ +[[object-lifecycles]] +=== Object life cycles and thread safety + +There are five kinds of objects in the {java-client} with different life cycles: + + +**Object mapper**:: +Stateless and thread-safe, but can be costly to create. +It’s usually a singleton that is created at application startup and used to +create the transport. + +**Transport**:: +Thread-safe, holds network resources through the underlying HTTP client. A +transport object is associated with an {es} cluster and has to be explicitly +closed to release the underlying resources such as network connections. + +**Clients**:: +Immutable, stateless and thread-safe. +These are very lightweight objects that just wrap a transport and provide API +endpoints as methods. + +**Builders**:: +Mutable, non thread-safe. +Builders are transient objects that should not be reused after calling +`build()`. + +**Requests & other API objects**:: +Immutable, thread-safe. +If your application uses the same request or same parts of a request over and +over, these objects can be prepared in advance and reused across multiple calls +over multiple clients with different transports. diff --git a/docs/api-conventions/package-structure.asciidoc b/docs/api-conventions/package-structure.asciidoc new file mode 100644 index 000000000..f51d0f476 --- /dev/null +++ b/docs/api-conventions/package-structure.asciidoc @@ -0,0 +1,26 @@ +[[package-structure]] +=== Package structure and namespace clients + +The {es} API is large and is organized into feature groups, as can be seen in +the {ref}/rest-apis.html[{es} API documentation]. + +The {java-client} follows this structure: feature groups are called “namespaces”, +and each namespace is located in a subpackage of +`co.elastic.clients.elasticsearch`. + +Each of the namespace clients can be accessed from the top level {es} client. The +only exceptions are the “search” and “document” APIs which are located in the `core` +subpackage and can be accessed on the main {es} client object. + +The snippet below shows how to use the indices namespace client to create an +index (the lambda syntax is explained in <>): + +["source","java"] +-------------------------------------------------- +// Create the "products" index +ElasticsearchClient client = ... +client.indices().create(c -> c.index("products")); +-------------------------------------------------- + +Namespace clients are very lightweight objects that can be created on the fly. + diff --git a/docs/api-conventions/variant-types.asciidoc b/docs/api-conventions/variant-types.asciidoc new file mode 100644 index 000000000..dfee4fb78 --- /dev/null +++ b/docs/api-conventions/variant-types.asciidoc @@ -0,0 +1,60 @@ +[[variant-types]] +=== Variant types + +The {es} API has a lot of variant types: queries, aggregations, field mappings, +analyzers, and so on. Finding the correct class name in such large collections +can be challenging. + +The {java-client} builders make this easy: the builders for variant types, such as +`Query`, have methods for each of the available implementations. We’ve seen this +in action above with `intervals` (a kind of query) and `allOf`, `match` and +`anyOf` (various kinds of intervals). + +This is because variant objects in the {java-client} are implementations of a +“tagged union”: they contain the identifier (or tag) of the variant they hold +and the value for that variant. For example, a `Query` object can contain an +`IntervalsQuery` with tag `intervals`, a `TermQuery` with tag `term`, and so on. +This approach allows writing fluent code where you can let the IDE completion +features guide you to build and navigate complex nested structures: + +Variant builders have setter methods for every available implementation. They +use the same conventions as regular properties and accept both a builder lambda +expression and a ready-made object of the actual type of the variant. Here’s an +example to build a term query: + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/api_conventions/ApiConventionsTest.java[variant-creation] +-------------------------------------------------- +<1> Choose the `term` variant to build a term query. +<2> Build the terms query with a builder lambda expression. +<3> Build the `Query` that now holds a `TermQuery` object of kind `term`. + +Variant objects have getter methods for every available implementation. These +methods check that the object actually holds a variant of that kind and return +the value downcasted to the correct type. They throw an `IllegalStateException` +otherwise. This approach allows writing fluent code to traverse variants. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/api_conventions/ApiConventionsTest.java[variant-navigation] +-------------------------------------------------- + +Variant objects also provide information on the variant kind they currently hold: + +* with `is` methods for each of the variant kinds: `isTerm()`, `isIntervals()`, `isFuzzy()`, etc. + +* with a nested `Kind` enumeration that defines all variant kinds. + +This information can then be used to navigate down into specific variants after checking +their actual kind: + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/api_conventions/ApiConventionsTest.java[variant-kind] +-------------------------------------------------- +<1> Test if the variant is of a specific kind. +<2> Test a larger set of variant kinds. +<3> Get the kind and value held by the variant object. + +{doc-tests-blurb} diff --git a/docs/external-resources.asciidoc b/docs/external-resources.asciidoc new file mode 100644 index 000000000..5a73c851c --- /dev/null +++ b/docs/external-resources.asciidoc @@ -0,0 +1,10 @@ +[[external-resources]] +== External resources + +// Note: we keep the actual links in GitHub to ease maintenance and avoid publishing external +// links on the corporate website. This is particularly important as the documentation for +// older versions of the Elastic Stack may stop being updated on the website. + +There is some material related to the {java-client} available outside this website that provides additional information or different perspectives on the library. + +You can find a community-maintained list of external resources in the https://github.com/elastic/elasticsearch-java/tree/main/docs/external-resources.md[{java-client} GitHub repository]. diff --git a/docs/connecting.asciidoc b/docs/getting-started/connecting.asciidoc similarity index 86% rename from docs/connecting.asciidoc rename to docs/getting-started/connecting.asciidoc index a091d69a5..75949a91a 100644 --- a/docs/connecting.asciidoc +++ b/docs/getting-started/connecting.asciidoc @@ -1,5 +1,5 @@ [[connecting]] -== Connecting +=== Connecting The {java-client} is structured around three main components: @@ -16,7 +16,7 @@ This code snippet creates and wires together these three components: ["source","java"] -------------------------------------------------- -include-tagged::{doc-tests}/ConnectingTest.java[create-client] +include-tagged::{doc-tests-src}/getting_started/ConnectingTest.java[create-client] -------------------------------------------------- Authentication is managed by the <>. For further details on @@ -24,7 +24,7 @@ configuring authentication, refer to {java-api-client}/_basic_authentication.html[its documentation]. [discrete] -=== Your first request +==== Your first request The code snippet below searches all items from a “product” index whose name matches “bicycle” and return them as instances of a `Product` application class. @@ -35,5 +35,7 @@ concise DSL-like code. This pattern is explained in more detail in ["source","java"] -------------------------------------------------- -include-tagged::{doc-tests}/ConnectingTest.java[first-request] +include-tagged::{doc-tests-src}/getting_started/ConnectingTest.java[first-request] -------------------------------------------------- + +{doc-tests-blurb} diff --git a/docs/getting-started/index.asciidoc b/docs/getting-started/index.asciidoc new file mode 100644 index 000000000..785ba634b --- /dev/null +++ b/docs/getting-started/index.asciidoc @@ -0,0 +1,12 @@ +== Getting started + +* <> +* <> +* <> +* <> + +include::installation.asciidoc[] +include::connecting.asciidoc[] +include::migrate-hlrc.asciidoc[] + +// include::low-level.asciidoc[leveloffset=+2] diff --git a/docs/installation.asciidoc b/docs/getting-started/installation.asciidoc similarity index 95% rename from docs/installation.asciidoc rename to docs/getting-started/installation.asciidoc index 3e898192e..89611157d 100644 --- a/docs/installation.asciidoc +++ b/docs/getting-started/installation.asciidoc @@ -1,5 +1,5 @@ [[installation]] -== Installation +=== Installation Requirements: @@ -19,7 +19,7 @@ available at https://snapshots.elastic.co/maven/. [discrete] [[gradle]] -=== Installation in a Gradle project by using Jackson +==== Installation in a Gradle project by using Jackson ["source","groovy",subs="attributes+"] -------------------------------------------------- @@ -36,7 +36,7 @@ dependencies { [discrete] [[maven]] -=== Installation in a Maven project by using Jackson +==== Installation in a Maven project by using Jackson In the `pom.xml` of your project, add the following repository definition and dependencies: @@ -73,7 +73,7 @@ dependencies: [discrete] [[spring-jakarta-json]] -=== Spring Boot and jakarta.json +==== Spring Boot and jakarta.json Spring Boot comes with Gradle and Maven plugins to ease development and dependency management. These plugins define built-in versions for a number of well-known libraries. diff --git a/docs/migrate.asciidoc b/docs/getting-started/migrate-hlrc.asciidoc similarity index 88% rename from docs/migrate.asciidoc rename to docs/getting-started/migrate-hlrc.asciidoc index 1cec90c40..7b3f75777 100644 --- a/docs/migrate.asciidoc +++ b/docs/getting-started/migrate-hlrc.asciidoc @@ -1,5 +1,5 @@ [[migrate-hlrc]] -== Migrating from the High Level Rest Client +=== Migrating from the High Level Rest Client The {es} Java API Client is an entirely new client library that has no relation to the older High Level Rest Client (HLRC). This was a deliberate choice to @@ -11,7 +11,7 @@ application. This transition can however happen progressively as the two client libraries can coexist in a single application with no operational overhead. [discrete] -=== Compatibility mode: using a 7.17 client with {es} 8.x +==== Compatibility mode: using a 7.17 client with {es} 8.x The HLRC version `7.17` can be used with {es} version `8.x` by enabling HLRC's compatibility mode (see code sample below). In this mode HLRC sends additional headers that instruct {es} `8.x` to behave like a `7.x` server. @@ -20,7 +20,7 @@ The {java-client} doesn't need this setting as compatibility mode is always enabled. [discrete] -=== Using the same http client with the HLRC and the Java API Client +==== Using the same http client with the HLRC and the Java API Client To avoid any operational overhead during the transition phase where an application would use both the HLRC and the new Java API Client, both clients @@ -31,12 +31,12 @@ The code below shows how to initialize both clients with the same HTTP client: ["source","java"] -------------------------------------------------- -include-tagged::{doc-tests}/MigrateHlrcTest.java[migrate] +include-tagged::{doc-tests-src}/getting_started/MigrateHlrcTest.java[migrate] -------------------------------------------------- <1> Enables compatibility mode that allows HLRC `7.17` to work with {es} `8.x`. [discrete] -=== Transition strategies +==== Transition strategies There are different ways you can start transitioning away from the HLRC in your application code. diff --git a/docs/index-local.asciidoc b/docs/index-local.asciidoc index 48b04093a..f7860b40d 100644 --- a/docs/index-local.asciidoc +++ b/docs/index-local.asciidoc @@ -1,4 +1,5 @@ // Allow building docs locally without a checkout of the Elasticsearch repo :elasticsearch-root: {docdir}/local/elasticsearch +:local-docs: true include::index.asciidoc[] diff --git a/docs/index.asciidoc b/docs/index.asciidoc index 6785d8b30..6c4733cf1 100644 --- a/docs/index.asciidoc +++ b/docs/index.asciidoc @@ -6,7 +6,11 @@ include::{asciidoc-dir}/../../shared/attributes.asciidoc[] include::_releases.asciidoc[] :java-client: Java API Client -:doc-tests: {docdir}/../java-client/src/test/java/co/elastic/clients/documentation +:doc-tests-src: {docdir}/../java-client/src/test/java/co/elastic/clients/documentation +:doc-tests-url: https://github.com/elastic/elasticsearch-java/tree/{branch}/java-client/src/test/java/co/elastic/clients/documentation +:doc-tests-blurb: The source code for the examples above can be found in the {doc-tests-url}[{java-client} tests]. + +:es-docs: https://www.elastic.co/guide/en/elasticsearch/reference/{branch} ifeval::["{release-state}"=="unreleased"] :java-client-javadoc: https://snapshots.elastic.co/javadoc/co/elastic/clients/elasticsearch-java/{version}-SNAPSHOT @@ -23,14 +27,13 @@ ifeval::["{release-state}"!="unreleased"] endif::[] include::introduction.asciidoc[] -include::installation.asciidoc[] -include::connecting.asciidoc[] -include::migrate.asciidoc[] -include::api-conventions.asciidoc[] -ifdef::is_main_branch,v8_1_1_released,v7_17_2_released[] -include::loading-json.asciidoc[] -endif::is_main_branch,v8_1_1_released,v7_17_2_released[] +include::getting-started/index.asciidoc[] +include::api-conventions/index.asciidoc[] + +include::usage/index.asciidoc[] + +// include::troubleshooting/index.asciidoc[] include::javadoc-and-source.asciidoc[] include::release-notes/index.asciidoc[] - +include::external-resources.asciidoc[] include::{elasticsearch-root}/docs/java-rest/low-level/index.asciidoc[] diff --git a/docs/introduction.asciidoc b/docs/introduction.asciidoc index 1e7ee88e5..74c7ca351 100644 --- a/docs/introduction.asciidoc +++ b/docs/introduction.asciidoc @@ -18,32 +18,7 @@ provides strongly typed requests and responses for all {es} APIs. retries, node discovery, and so on. [discrete] -=== Main changes since version 7.15 - -Version 7.15 of the {java-client} was a beta release. We learned a lot from the -initial feedback and this led to a few breaking changes in this version: - -* the `_core` package that contains top-level {es} APIs has been renamed to `core`. -* property setters for lists and maps have been revisited. -* all API data types are now supported, and there are no more raw `JsonValue` properties. -* the supporting JSON framework and transport classes have been refactored and improved. - However the high level APIs used by applications haven't changed so there should be - no or very little impact on existing application code. - -This version of the {java-client} supports all {es} APIs are implemented except the -Vector tile search API and the Find structure API. - -While this version is considered stable, most of the code is generated from the -https://github.com/elastic/elasticsearch-specification[{es} API specification] where -some of the less often used {es} APIs still need to be fully validated. Changes in -the specification that may impact application code will be clearly identified in later -versions of the Java API client. - -[discrete] -=== Compatibility - -The `main` branch targets the next major release (8.0), the `7.x` branch targets the -next minor release for the `7.x` series. +=== Elasticsearch server compatibility policy The {es} Java client is forward compatible; meaning that the client supports communicating with greater or equal minor versions of {es}. {es} language diff --git a/docs/troubleshooting/index.asciidoc b/docs/troubleshooting/index.asciidoc new file mode 100644 index 000000000..433981b6d --- /dev/null +++ b/docs/troubleshooting/index.asciidoc @@ -0,0 +1,13 @@ +== Troubleshooting + +=== Understanding exceptions + +TBD + +=== Debugging + +TBD + +=== Elasticsearch deprecation warnings + +TBD diff --git a/docs/usage/aggregations.asciidoc b/docs/usage/aggregations.asciidoc new file mode 100644 index 000000000..d4ca1afd8 --- /dev/null +++ b/docs/usage/aggregations.asciidoc @@ -0,0 +1,40 @@ +[[aggregations]] +=== Aggregations + +An aggregation summarizes your data as metrics, statistics, or other analytics. + +NOTE: See the es-docs}/search-aggregations[{es} documentation] for a full explanation of aggregations. + +[discrete] +==== A simple aggregation + +In the example below we run an aggregation that creates a price histogram from a product index, for the products whose name match a user-provided text. To achieve this, we use a search request that has a query (explained in <>) and an aggregation definition. + +This example is an analytics-type aggregation where we do not want to use the matching documents. A general pattern for search requests used for analytics is to set the result `size` to zero and the target class for search results to `Void`. + +If that same aggregation was used for to display products and the price histogram as drill-down facets, we would have set `size` to a non-zero value and used `Product` as the target class to process the results. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/AggregationsTest.java[price-histo-request] +-------------------------------------------------- +<1> Set the number of matching documents to zero as we only use the price histogram. +<2> Set the query that fill filter the products on which to run the aggregation +<3> Create an aggregation named "price-histogram". You can add as many named aggregations as needed. +<4> Select the `histogram` aggregation variant. +<5> We do not care about matches (`size` is set to zero), using `Void` will ignore any document in the response. + +The response contains an aggregation result for each aggregation in the request. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/AggregationsTest.java[price-histo-response] +-------------------------------------------------- +<1> Get the results for the "price-histogram" aggregation. +<2> Cast it down to the `histogram` variant results. This has to be consistent with the aggregation definition. +<3> Buckets can be expressed as arrays or maps. This casts down to the array variant (the default). + + +// TODO: nested aggregations + +{doc-tests-blurb} diff --git a/docs/usage/index.asciidoc b/docs/usage/index.asciidoc new file mode 100644 index 000000000..c71aa34f3 --- /dev/null +++ b/docs/usage/index.asciidoc @@ -0,0 +1,31 @@ +[[usage]] +== Using the Java API Client + +The sections below provide tutorials on the most frequently used and some less obvious features of {es}. + +For a full reference, see the {es-docs}/[Elasticsearch documentation] and in particular the {es-docs}/rest-apis.html[REST APIs] section. The {java-client} follows closely the JSON structures described there, using the <>. + +If you're new to Elasticsearch, make sure also to read {es-docs}/getting-started.html[Elasticsearch's quick start] that provides a good introduction. + +* <> +* <> +* <> +* <> +* <> + +//// +* <> +//// + +NOTE: This is still a work in progress, more sections will be added in the near future. + +include::indexing.asciidoc[] +include::indexing-bulk.asciidoc[] +include::reading.asciidoc[] +include::searching.asciidoc[] +include::aggregations.asciidoc[] + +//// +include::indices.asciidoc[] +//// + diff --git a/docs/usage/indexing-bulk.asciidoc b/docs/usage/indexing-bulk.asciidoc new file mode 100644 index 000000000..67c0a3a22 --- /dev/null +++ b/docs/usage/indexing-bulk.asciidoc @@ -0,0 +1,52 @@ +[[indexing-bulk]] +=== Bulk: indexing multiple documents + +Bulk requests allow sending multiple document-related operations to {es} in one request. When you have multiple documents to ingest, this is more efficient than sending each document with a separate request. + +A bulk request can contain several kinds of operations: + +* create a document, indexing it after ensuring it doesn't already exist, +* index a document, creating it if needed and replacing it if it exists, +* update a document that already exists in place, either with a script or a partial document, +* delete a document. + +NOTE: See the {es-docs}/docs-bulk.html[{es} API documentation] for a full explanation of bulk requests. + +[discrete] +==== Indexing application objects + +A `BulkRequest` contains a collection of operations, each operation being a <>. To create this request, it is convenient to use a builder object for the main request, and the fluent DSL for each operation. + +The example below shows how to index a list or application objects. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/IndexingBulkTest.java[bulk-objects] +-------------------------------------------------- +<1> Adds an operation (remember that <>). `op` is is a builder for `BulkOperation` which is a <>. This type has `index`, `create`, `update` and `delete` variants. +<2> Selects the `index` operation variant, `idx` is a builder for `IndexOperation`. +<3> Sets the properties for the index operation, similar to <>: index name, identifier and document. + +[discrete] +==== Indexing raw JSON data + +The `document` property of a bulk index request can be any object that can be serialized to JSON using your Elasticsearch client's JSON mapper. In the example below we will use the {java-client}'s `JsonData` object to read json files from a log directory and send them in a bulk request. + +Since `JsonData` doesn't allow reading directly from an input stream (this will be added in a future release), we will use the following function for that: + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/IndexingBulkTest.java[read-json] +-------------------------------------------------- + +We can now read the contents of the log directory and send it to {es}: + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/IndexingBulkTest.java[bulk-json] +-------------------------------------------------- + + + + +{doc-tests-blurb} diff --git a/docs/usage/indexing.asciidoc b/docs/usage/indexing.asciidoc new file mode 100644 index 000000000..f6bc7446a --- /dev/null +++ b/docs/usage/indexing.asciidoc @@ -0,0 +1,61 @@ +[[indexing]] +=== Indexing single documents + +The {java-client} offers several ways to index data: you can provide application objects that will be automatically mapped to JSON, or you can provide raw JSON data. Using application objects is more suited to applications with a well-defined domain model, whereas raw JSON is more suited to logging use cases with semi-structured data. + +In the examples below we use a `Product` domain object that has `sku`, `name` and `price` properties. + +NOTE: See the {es-docs}/docs-index_.html[{es} API documentation] for a full explanation of index requests. + +[discrete] +==== Using the fluent DSL + +The most direct way to build requests is using the fluent DSL. In the example below we index a product description in the `products` index, using the product's SKU as the document identifier in the index. The `product` object will be mapped to JSON using the object mapper configured on the {es} client. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/IndexingTest.java[single-doc-dsl] +-------------------------------------------------- + +You can also assign objects created with the DSL to variables. {java-client} classes have a static `of()` method for this, that creates an object with the DSL syntax. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/IndexingTest.java[single-doc-dsl-of] +-------------------------------------------------- + +[discrete] +==== Using classic builders + +If you're more used to the classic builder pattern, it is also available. Builder objects are used under the hood by the fluent DSL syntax. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/IndexingTest.java[single-doc-builder] +-------------------------------------------------- + +[discrete] +==== Using the asynchronous client + +The examples above used the synchronous {es} client. All {es} APIs are also available in the asynchronous client, using the same request and response types. See also <> for additional details. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/IndexingTest.java[single-doc-dsl-async] +-------------------------------------------------- + +[discrete] +==== Using raw JSON data + +When the data you want to index comes from external sources, having to create domain objects may be cumbersome or outright impossible with semi-structured data. + +You can index data from an arbitrary source using `withJson()`. Using this method will read the source and use it for the index request's `document` property. See <> for additional details. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/IndexingTest.java[single-doc-json] +-------------------------------------------------- + +// TODO: Add an example with version conflict detection + +{doc-tests-blurb} diff --git a/docs/usage/indices.asciidoc b/docs/usage/indices.asciidoc new file mode 100644 index 000000000..f5416f7e9 --- /dev/null +++ b/docs/usage/indices.asciidoc @@ -0,0 +1,4 @@ +[[indices]] +=== Creating and managing indices + +TBD diff --git a/docs/usage/reading.asciidoc b/docs/usage/reading.asciidoc new file mode 100644 index 000000000..ab47662f3 --- /dev/null +++ b/docs/usage/reading.asciidoc @@ -0,0 +1,40 @@ +[[reading]] +=== Reading documents by id + +Elasticsearch is all about search, but you may also want to access documents directly, knowing their identifier. The "get" request is meant for this. + +NOTE: See the {es-docs}/docs-get.html[{es} API documentation] for a full explanation of get requests. + +[discrete] +==== Reading a domain object + +The example below reads the document with identifier `bk-1` from the `products` index. + +The `get` request has two parameters: + +* the first parameter is the actual request, built below with the fluent DSL +* the second parameter is the class we want the document's JSON to be mapped to. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/ReadingTest.java[get-by-id] +-------------------------------------------------- +<1> The get request, with the index name and identifier. +<2> The target class, here `Product`. + +[discrete] +==== Reading raw JSON + +When your index contains semi-structured data or if you don't have a domain object definition, you can also read the document as raw JSON data. + +Raw JSON data is just another class that you can use as the result type for the get request. In the example below we use Jackson's `ObjectNode`. We could also have used any JSON representation that can be deserialized by the JSON mapper associated to the `ElasticsearchClient`. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/ReadingTest.java[get-by-id-json] +-------------------------------------------------- +<1> The target class is a raw JSON object. + +{doc-tests-blurb} + + diff --git a/docs/usage/searching.asciidoc b/docs/usage/searching.asciidoc new file mode 100644 index 000000000..622905427 --- /dev/null +++ b/docs/usage/searching.asciidoc @@ -0,0 +1,46 @@ +[[searching]] +=== Searching for documents + +Indexed documents are available for search in near real-time. + +NOTE: See the {es} documentation for a full explanation of search requests: {es-docs}/search-your-data.html[search your data], {es-docs}/search-your-data.html[the query DSL], and {es-docs}/search.html[search APIs]. + +[discrete] +==== Simple search query + +There are many types of search queries that can be combined. We will start with the simple text match query, searching for bikes in the `products` index. + +The search result has a `hits` properties that contains the documents that matched the query along with information about the total number of matches that exist in the index. + +The total value comes with a relation that indicates if the total is exact (`eq` -- equal) or approximate (`gte` -- greater than or equal). + +Each returned document comes with its relevance score and additional information about its location in the index. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/SearchingTest.java[search-simple] +-------------------------------------------------- +<1> Name of the index we want to search. +<2> The query part of the search request (a search request can also have other components like <>). +<3> Choose a query variant among the many available. We choose here the match query (full text search). +<4> Configure the match query: we search for a term in the `name` field. +<5> The target class for the matching documents. We use `Product` here, just like in <> examples. + +Similarly to <> operations, you can fetch documents matching your query as raw JSON by using a corresponding target class instead of `Product`, like JSON-P's `JsonValue` or Jackson's `ObjectNode`. + +[discrete] +==== Nested search queries + +{es} allows individual queries to be combined to build more complex search requests. In the example below we will search for bikes with a maximum price of 200. + +["source","java"] +-------------------------------------------------- +include-tagged::{doc-tests-src}/usage/SearchingTest.java[search-nested] +-------------------------------------------------- +<1> We're creating the queries for individual criteria separately. +<2> A `MatchQuery` is a query _variant_ that we have to turn into the `Query` _union type_. See <> for additional details. +<3> {es} range query accepts a large range of value types. We create here a JSON representation of the maximum price. +<4> The search query is a boolean query that combines the text search and max price queries. +<5> Both queries are added as `must` as we want results to match all criteria. + +{doc-tests-blurb} diff --git a/java-client/src/test/java/co/elastic/clients/documentation/DocTestsTransport.java b/java-client/src/test/java/co/elastic/clients/documentation/DocTestsTransport.java index a5f9c25a6..b458613d9 100644 --- a/java-client/src/test/java/co/elastic/clients/documentation/DocTestsTransport.java +++ b/java-client/src/test/java/co/elastic/clients/documentation/DocTestsTransport.java @@ -20,7 +20,7 @@ package co.elastic.clients.documentation; import co.elastic.clients.json.JsonpMapper; -import co.elastic.clients.json.jsonb.JsonbJsonpMapper; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.Endpoint; import co.elastic.clients.transport.TransportOptions; @@ -40,11 +40,11 @@ */ public class DocTestsTransport implements ElasticsearchTransport { - private JsonpMapper mapper = new JsonbJsonpMapper(); + private final JsonpMapper mapper = new JacksonJsonpMapper(); - private ThreadLocal result = new ThreadLocal<>(); + private final ThreadLocal result = new ThreadLocal<>(); - private TransportOptions options = new TransportOptions() { + private final TransportOptions options = new TransportOptions() { @Override public Collection> headers() { return Collections.emptyList(); diff --git a/java-client/src/test/java/co/elastic/clients/documentation/ApiConventionsTest.java b/java-client/src/test/java/co/elastic/clients/documentation/api_conventions/ApiConventionsTest.java similarity index 88% rename from java-client/src/test/java/co/elastic/clients/documentation/ApiConventionsTest.java rename to java-client/src/test/java/co/elastic/clients/documentation/api_conventions/ApiConventionsTest.java index 8d06bbaa8..31dee9305 100644 --- a/java-client/src/test/java/co/elastic/clients/documentation/ApiConventionsTest.java +++ b/java-client/src/test/java/co/elastic/clients/documentation/api_conventions/ApiConventionsTest.java @@ -17,8 +17,9 @@ * under the License. */ -package co.elastic.clients.documentation; +package co.elastic.clients.documentation.api_conventions; +import co.elastic.clients.documentation.DocTestsTransport; import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient; import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch._types.NodeStatistics; @@ -34,20 +35,20 @@ import co.elastic.clients.util.ApiTypeHelper; import org.junit.Assert; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.logging.LogManager; -import java.util.logging.Logger; public class ApiConventionsTest extends Assert { private static class SomeApplicationData {} private ElasticsearchTransport transport = new DocTestsTransport(); - Logger logger = LogManager.getLogManager().getLogger(ApiConventionsTest.class.getName()); + Logger logger = LoggerFactory.getLogger(this.getClass()); public void blockingAndAsync() throws Exception { @@ -65,9 +66,11 @@ public void blockingAndAsync() throws Exception { asyncClient .exists(b -> b.index("products").id("foo")) - .thenAccept(response -> { - if (response.value()) { - logger.info("product exists"); + .whenComplete((response, exception) -> { + if (exception != null) { + logger.error("Failed to index", exception); + } else { + logger.info("Product exists"); } }); //end::blocking-and-async @@ -122,28 +125,28 @@ public void builderIntervals() throws Exception { //tag::builder-intervals SearchResponse results = client - .search(_0 -> _0 - .query(_1 -> _1 - .intervals(_2 -> _2 + .search(b0 -> b0 + .query(b1 -> b1 + .intervals(b2 -> b2 .field("my_text") - .allOf(_3 -> _3 + .allOf(b3 -> b3 .ordered(true) - .intervals(_4 -> _4 - .match(_5 -> _5 + .intervals(b4 -> b4 + .match(b5 -> b5 .query("my favorite food") .maxGaps(0) .ordered(true) ) ) - .intervals(_4 -> _4 - .anyOf(_5 -> _5 - .intervals(_6 -> _6 - .match(_7 -> _7 + .intervals(b4 -> b4 + .anyOf(b5 -> b5 + .intervals(b6 -> b6 + .match(b7 -> b7 .query("hot water") ) ) - .intervals(_6 -> _6 - .match(_7 -> _7 + .intervals(b6 -> b6 + .match(b7 -> b7 .query("cold porridge") ) ) diff --git a/java-client/src/test/java/co/elastic/clients/documentation/LoadingJsonTest.java b/java-client/src/test/java/co/elastic/clients/documentation/api_conventions/LoadingJsonTest.java similarity index 94% rename from java-client/src/test/java/co/elastic/clients/documentation/LoadingJsonTest.java rename to java-client/src/test/java/co/elastic/clients/documentation/api_conventions/LoadingJsonTest.java index de6c87f4b..867d922c4 100644 --- a/java-client/src/test/java/co/elastic/clients/documentation/LoadingJsonTest.java +++ b/java-client/src/test/java/co/elastic/clients/documentation/api_conventions/LoadingJsonTest.java @@ -17,8 +17,9 @@ * under the License. */ -package co.elastic.clients.documentation; +package co.elastic.clients.documentation.api_conventions; +import co.elastic.clients.documentation.DocTestsTransport; import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch._types.aggregations.Aggregate; import co.elastic.clients.elasticsearch._types.aggregations.CalendarInterval; @@ -45,10 +46,10 @@ public class LoadingJsonTest extends ModelTestCase { - private DocTestsTransport transport = new DocTestsTransport(); - private ElasticsearchClient client = new ElasticsearchClient(transport); + private final DocTestsTransport transport = new DocTestsTransport(); + private final ElasticsearchClient client = new ElasticsearchClient(transport); - private static SearchResponse searchResponse = SearchResponse.of(b -> b + private static final SearchResponse searchResponse = SearchResponse.of(b -> b .aggregations(new HashMap<>()) .took(0) .timedOut(false) diff --git a/java-client/src/test/java/co/elastic/clients/documentation/ConnectingTest.java b/java-client/src/test/java/co/elastic/clients/documentation/getting_started/ConnectingTest.java similarity index 95% rename from java-client/src/test/java/co/elastic/clients/documentation/ConnectingTest.java rename to java-client/src/test/java/co/elastic/clients/documentation/getting_started/ConnectingTest.java index cb811f6c4..7fee39785 100644 --- a/java-client/src/test/java/co/elastic/clients/documentation/ConnectingTest.java +++ b/java-client/src/test/java/co/elastic/clients/documentation/getting_started/ConnectingTest.java @@ -17,8 +17,9 @@ * under the License. */ -package co.elastic.clients.documentation; +package co.elastic.clients.documentation.getting_started; +import co.elastic.clients.documentation.usage.Product; import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch.core.SearchResponse; import co.elastic.clients.elasticsearch.core.search.Hit; @@ -32,8 +33,6 @@ public class ConnectingTest { - private static class Product{} - @Ignore // we don't have a running ES @Test public void createClient() throws Exception { diff --git a/java-client/src/test/java/co/elastic/clients/documentation/MigrateHlrcTest.java b/java-client/src/test/java/co/elastic/clients/documentation/getting_started/MigrateHlrcTest.java similarity index 97% rename from java-client/src/test/java/co/elastic/clients/documentation/MigrateHlrcTest.java rename to java-client/src/test/java/co/elastic/clients/documentation/getting_started/MigrateHlrcTest.java index f607fb871..9a5a3cbb4 100644 --- a/java-client/src/test/java/co/elastic/clients/documentation/MigrateHlrcTest.java +++ b/java-client/src/test/java/co/elastic/clients/documentation/getting_started/MigrateHlrcTest.java @@ -17,7 +17,7 @@ * under the License. */ -package co.elastic.clients.documentation; +package co.elastic.clients.documentation.getting_started; import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.json.jackson.JacksonJsonpMapper; diff --git a/java-client/src/test/java/co/elastic/clients/documentation/usage/AggregationsTest.java b/java-client/src/test/java/co/elastic/clients/documentation/usage/AggregationsTest.java new file mode 100644 index 000000000..2e5606d47 --- /dev/null +++ b/java-client/src/test/java/co/elastic/clients/documentation/usage/AggregationsTest.java @@ -0,0 +1,107 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package co.elastic.clients.documentation.usage; + +import co.elastic.clients.documentation.DocTestsTransport; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.aggregations.HistogramBucket; +import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery; +import co.elastic.clients.elasticsearch._types.query_dsl.Query; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.TotalHitsRelation; +import co.elastic.clients.elasticsearch.model.ModelTestCase; +import co.elastic.clients.json.JsonData; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +public class AggregationsTest extends ModelTestCase { + + private final DocTestsTransport transport = new DocTestsTransport(); + private final ElasticsearchClient esClient = new ElasticsearchClient(transport); + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private static final SearchResponse searchResponse = SearchResponse.of(b -> b + .aggregations(new HashMap<>()) + .took(0) + .timedOut(false) + .hits(h -> h + .total(t -> t.value(0).relation(TotalHitsRelation.Eq)) + .hits(new ArrayList<>()) + ) + .shards(s -> s + .total(1) + .failed(0) + .successful(1) + ) + .aggregations("price-histogram", a -> a.histogram(h -> h + .buckets(bu -> bu.array(Collections.singletonList(HistogramBucket.of(hb -> hb + .key(50).docCount(1) + )))) + )) + ); + + @Test + public void priceHistogram() throws Exception { + + transport.setResult(searchResponse); + + //tag::price-histo-request + String searchText = "bike"; + + Query query = MatchQuery.of(m -> m + .field("name") + .query(searchText) + )._toQuery(); + + SearchResponse response = esClient.search(b -> b + .index("products") + .size(0) // <1> + .query(query) // <2> + .aggregations("price-histogram", a -> a // <3> + .histogram(h -> h // <4> + .field("price") + .interval(50.0) + ) + ), + Void.class // <5> + ); + //end::price-histo-request + + //tag::price-histo-response + List buckets = response.aggregations() + .get("price-histogram") // <1> + .histogram() // <2> + .buckets().array(); // <3> + + for (HistogramBucket bucket: buckets) { + logger.info("There are " + bucket.docCount() + + " bikes under " + bucket.key()); + } + + //end::price-histo-response + } +} diff --git a/java-client/src/test/java/co/elastic/clients/documentation/usage/IndexingBulkTest.java b/java-client/src/test/java/co/elastic/clients/documentation/usage/IndexingBulkTest.java new file mode 100644 index 000000000..4663ce44a --- /dev/null +++ b/java-client/src/test/java/co/elastic/clients/documentation/usage/IndexingBulkTest.java @@ -0,0 +1,130 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package co.elastic.clients.documentation.usage; + +import co.elastic.clients.documentation.DocTestsTransport; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.core.BulkRequest; +import co.elastic.clients.elasticsearch.core.BulkResponse; +import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem; +import co.elastic.clients.elasticsearch.model.ModelTestCase; +import co.elastic.clients.json.JsonData; +import co.elastic.clients.json.JsonpMapper; +import jakarta.json.spi.JsonProvider; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class IndexingBulkTest extends ModelTestCase { + + private final DocTestsTransport transport = new DocTestsTransport(); + private final ElasticsearchClient esClient = new ElasticsearchClient(transport); + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + BulkResponse result = BulkResponse.of(r -> r + .errors(false) + .items(Collections.emptyList()) + .took(1) + ); + + private List fetchProducts() { + List list = new ArrayList<>(); + list.add(new Product("bk-1", "City Bike", 123.0)); + list.add(new Product("bk-2", "Mountain Bike", 134.0)); + return list; + } + + @Test + public void indexBulk() throws Exception { + transport.setResult(result); + + //tag::bulk-objects + List products = fetchProducts(); + + BulkRequest.Builder br = new BulkRequest.Builder(); + + for (Product product : products) { + br.operations(op -> op //<1> + .index(idx -> idx //<2> + .index("products") //<3> + .id(product.getSku()) + .document(product) + ) + ); + } + + BulkResponse result = esClient.bulk(br.build()); + + // Log errors, if any + if (result.errors()) { + logger.error("Bulk had errors"); + for (BulkResponseItem item: result.items()) { + if (item.error() != null) { + logger.error(item.error().reason()); + } + } + } + //end::bulk-objects + } + + //tag::read-json + public static JsonData readJson(InputStream input, ElasticsearchClient esClient) { + JsonpMapper jsonpMapper = esClient._transport().jsonpMapper(); + JsonProvider jsonProvider = jsonpMapper.jsonProvider(); + + return JsonData.from(jsonProvider.createParser(input), jsonpMapper); + } + //end::read-json + + @Test + public void indexBulkJson() throws Exception { + transport.setResult(result); + + File logDir = new File("."); + + //tag::bulk-json + // List json log files in the log directory + File[] logFiles = logDir.listFiles( + file -> file.getName().matches("log-.*\\.json") + ); + + BulkRequest.Builder br = new BulkRequest.Builder(); + + for (File file: logFiles) { + JsonData json = readJson(new FileInputStream(file), esClient); + + br.operations(op -> op + .index(idx -> idx + .index("logs") + .document(json) + ) + ); + } + //end::bulk-json + } +} diff --git a/java-client/src/test/java/co/elastic/clients/documentation/usage/IndexingTest.java b/java-client/src/test/java/co/elastic/clients/documentation/usage/IndexingTest.java new file mode 100644 index 000000000..aed7d4d32 --- /dev/null +++ b/java-client/src/test/java/co/elastic/clients/documentation/usage/IndexingTest.java @@ -0,0 +1,165 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package co.elastic.clients.documentation.usage; + +import co.elastic.clients.documentation.DocTestsTransport; +import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.Result; +import co.elastic.clients.elasticsearch.core.IndexRequest; +import co.elastic.clients.elasticsearch.core.IndexResponse; +import co.elastic.clients.elasticsearch.model.ModelTestCase; +import co.elastic.clients.json.JsonData; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Reader; +import java.io.StringReader; + +public class IndexingTest extends ModelTestCase { + + private final DocTestsTransport transport = new DocTestsTransport(); + private final ElasticsearchClient esClient = new ElasticsearchClient(transport); + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + IndexResponse result = IndexResponse.of(r -> r + .index("product") + .id("bk-1") + .version(1) + .primaryTerm(1) + .seqNo(1) + .result(Result.Created) + .shards(s -> s.total(1).successful(1).failed(0)) + ); + + @Test + public void singleDocumentDSL() throws Exception { + + // Stub response + transport.setResult(result); + + //tag::single-doc-dsl + Product product = new Product("bk-1", "City bike", 123.0); + + IndexResponse response = esClient.index(i -> i + .index("products") + .id(product.getSku()) + .document(product) + ); + + logger.info("Indexed with version " + response.version()); + //end::single-doc-dsl + } + + @Test + public void singleDocumentDSLwithOf() throws Exception { + + // Stub response + transport.setResult(result); + + //tag::single-doc-dsl-of + Product product = new Product("bk-1", "City bike", 123.0); + + IndexRequest request = IndexRequest.of(i -> i + .index("products") + .id(product.getSku()) + .document(product) + ); + + IndexResponse response = esClient.index(request); + + logger.info("Indexed with version " + response.version()); + //end::single-doc-dsl-of + } + + @Test + public void singleDocumentBuilder() throws Exception { + + // Stub response + transport.setResult(result); + + //tag::single-doc-builder + Product product = new Product("bk-1", "City bike", 123.0); + + IndexRequest.Builder indexReqBuilder = new IndexRequest.Builder<>(); + indexReqBuilder.index("product"); + indexReqBuilder.id(product.getSku()); + indexReqBuilder.document(product); + + IndexResponse response = esClient.index(indexReqBuilder.build()); + + logger.info("Indexed with version " + response.version()); + //end::single-doc-builder + } + + @Test + public void singleDocumentDSLAsync() throws Exception { + + // Stub response + transport.setResult(result); + + //tag::single-doc-dsl-async + ElasticsearchAsyncClient esAsyncClient = new ElasticsearchAsyncClient(transport); + + Product product = new Product("bk-1", "City bike", 123.0); + + esAsyncClient.index(i -> i + .index("products") + .id(product.getSku()) + .document(product) + ).whenComplete((response, exception) -> { + if (exception != null) { + logger.error("Failed to index", exception); + } else { + logger.info("Indexed with version " + response.version()); + } + }); + //end::single-doc-dsl-async + } + + @Test + public void singleDocumentJson() throws Exception { + // Stub response + transport.setResult(result); + + //tag::single-doc-json + Reader input = new StringReader( + "{'@timestamp': '2022-04-08T13:55:32Z', 'level': 'warn', 'message': 'Some log message'}" + .replace('\'', '"')); + + IndexRequest request = IndexRequest.of(i -> i + .index("logs") + .withJson(input) + ); + + IndexResponse response = esClient.index(request); + + logger.info("Indexed with version " + response.version()); + //end::single-doc-json + + assertEquals("warn", request.document().toJson().asJsonObject().getString("level")); + assertEquals( + "{\"@timestamp\":\"2022-04-08T13:55:32Z\",\"level\":\"warn\",\"message\":\"Some log message\"}", + toJson(request) + ); + } +} diff --git a/java-client/src/test/java/co/elastic/clients/documentation/usage/Product.java b/java-client/src/test/java/co/elastic/clients/documentation/usage/Product.java new file mode 100644 index 000000000..29d4c2310 --- /dev/null +++ b/java-client/src/test/java/co/elastic/clients/documentation/usage/Product.java @@ -0,0 +1,57 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package co.elastic.clients.documentation.usage; + +public class Product { + private String sku; + private String name; + private double price; + + public Product() {} + public Product(String sku, String name, double price) { + this.sku = sku; + this.name = name; + this.price = price; + } + + public String getSku() { + return sku; + } + + public void setSku(String sku) { + this.sku = sku; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return this.price; + } + + public void setPrice(double price) { + this.price = price; + } +} diff --git a/java-client/src/test/java/co/elastic/clients/documentation/usage/ReadingTest.java b/java-client/src/test/java/co/elastic/clients/documentation/usage/ReadingTest.java new file mode 100644 index 000000000..053a5476f --- /dev/null +++ b/java-client/src/test/java/co/elastic/clients/documentation/usage/ReadingTest.java @@ -0,0 +1,97 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package co.elastic.clients.documentation.usage; + +import co.elastic.clients.documentation.DocTestsTransport; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.core.GetResponse; +import co.elastic.clients.elasticsearch.model.ModelTestCase; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ReadingTest extends ModelTestCase { + + private final DocTestsTransport transport = new DocTestsTransport(); + private final ElasticsearchClient esClient = new ElasticsearchClient(transport); + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Test + public void getById() throws Exception { + + transport.setResult(GetResponse.of(r -> r + .found(true) + .index("products") + .id("bk-1") + .version(1L) + .seqNo(0L) + .primaryTerm(0L) + .source(new Product("bk-1", "City Bike", 123.0)) + )); + + //tag::get-by-id + GetResponse response = esClient.get(g -> g + .index("products") // <1> + .id("bk-1"), + Product.class // <2> + ); + + if (response.found()) { + Product product = response.source(); + logger.info("Product name " + product.getName()); + } else { + logger.info ("Product not found"); + } + //end::get-by-id + + } + + @Test + public void getByIdJson() throws Exception { + + transport.setResult(GetResponse.of(r -> r + .found(true) + .index("products") + .id("bk-1") + .version(1L) + .seqNo(0L) + .primaryTerm(0L) + .source(fromJson("{\"name\":\"City Bike\"}", ObjectNode.class, transport.jsonpMapper())) + )); + + //tag::get-by-id-json + GetResponse response = esClient.get(g -> g + .index("products") + .id("bk-1"), + ObjectNode.class // <1> + ); + + if (response.found()) { + ObjectNode json = response.source(); + String name = json.get("name").asText(); + logger.info("Product name " + name); + } else { + logger.info("Product not found"); + } + //end::get-by-id-json + } +} diff --git a/java-client/src/test/java/co/elastic/clients/documentation/usage/SearchingTest.java b/java-client/src/test/java/co/elastic/clients/documentation/usage/SearchingTest.java new file mode 100644 index 000000000..fe9c3fd94 --- /dev/null +++ b/java-client/src/test/java/co/elastic/clients/documentation/usage/SearchingTest.java @@ -0,0 +1,136 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package co.elastic.clients.documentation.usage; + +import co.elastic.clients.documentation.DocTestsTransport; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery; +import co.elastic.clients.elasticsearch._types.query_dsl.Query; +import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.elasticsearch.core.search.TotalHits; +import co.elastic.clients.elasticsearch.core.search.TotalHitsRelation; +import co.elastic.clients.json.JsonData; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class SearchingTest { + + private final DocTestsTransport transport = new DocTestsTransport(); + private final ElasticsearchClient esClient = new ElasticsearchClient(transport); + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private static final SearchResponse searchResponse = SearchResponse.of(b -> b + .aggregations(new HashMap<>()) + .took(0) + .timedOut(false) + .hits(h -> h + .total(t -> t.value(0).relation(TotalHitsRelation.Eq)) + .hits(new ArrayList<>()) + ) + .shards(s -> s + .total(1) + .failed(0) + .successful(1) + ) + ); + + @Test + public void searchMatch() throws Exception { + + transport.setResult(searchResponse); + //tag::search-simple + + String searchText = "bike"; + + SearchResponse response = esClient.search(s -> s + .index("products") // <1> + .query(q -> q // <2> + .match(t -> t // <3> + .field("name") // <4> + .query(searchText) + ) + ), + Product.class // <5> + ); + + TotalHits total = response.hits().total(); + boolean isExactResult = total.relation() == TotalHitsRelation.Eq; + + if (isExactResult) { + logger.info("There are " + total.value() + " results"); + } else { + logger.info("There are more than " + total.value() + " results"); + } + + List> hits = response.hits().hits(); + for (Hit hit: hits) { + Product product = hit.source(); + logger.info("Found product " + product.getSku() + ", score " + hit.score()); + } + //end::search-simple + } + + @Test + public void searchNested() throws Exception { + transport.setResult(searchResponse); + //tag::search-nested + String searchText = "bike"; + double maxPrice = 200.0; + + // Search by product name + Query byName = MatchQuery.of(m -> m // <1> + .field("name") + .query(searchText) + )._toQuery(); // <2> + + // Search by max price + Query byMaxPrice = RangeQuery.of(r -> r + .field("price") + .gte(JsonData.of(maxPrice)) // <3> + )._toQuery(); + + // Combine name and price queries to search the product index + SearchResponse response = esClient.search(s -> s + .index("products") + .query(q -> q + .bool(b -> b // <4> + .must(byName) // <5> + .must(byMaxPrice) + ) + ), + Product.class + ); + + List> hits = response.hits().hits(); + for (Hit hit: hits) { + Product product = hit.source(); + logger.info("Found product " + product.getSku() + ", score " + hit.score()); + } + //end::search-nested + } +} diff --git a/java-client/src/test/java/co/elastic/clients/json/JsonpMappingExceptionTest.java b/java-client/src/test/java/co/elastic/clients/json/JsonpMappingExceptionTest.java index 2b9837abd..4c753459d 100644 --- a/java-client/src/test/java/co/elastic/clients/json/JsonpMappingExceptionTest.java +++ b/java-client/src/test/java/co/elastic/clients/json/JsonpMappingExceptionTest.java @@ -69,7 +69,7 @@ public void testObjectAndArrayPath() { JsonpMappingException e = assertThrows(JsonpMappingException.class, () -> { // withJson() will read values of the generic parameter type as JsonData - SearchResponse r = SearchResponse.searchResponseOf(b -> b + SearchResponse r = SearchResponse.of(b -> b .withJson(new StringReader(json)) ); }); diff --git a/java-client/src/test/resources/co/elastic/clients/documentation/some-index.json b/java-client/src/test/resources/co/elastic/clients/documentation/api_conventions/some-index.json similarity index 100% rename from java-client/src/test/resources/co/elastic/clients/documentation/some-index.json rename to java-client/src/test/resources/co/elastic/clients/documentation/api_conventions/some-index.json