From 10df60f3a7312b4cd89404372f493b4d68942985 Mon Sep 17 00:00:00 2001 From: bfindlay Date: Tue, 21 Nov 2023 14:19:19 +1100 Subject: [PATCH] update flatobject and user guides Signed-off-by: bfindlay --- USER_GUIDE.md | 13 + .../_types/mapping/FlatObjectProperty.java | 261 ------------------ .../integTest/AbstractIndicesClientIT.java | 41 ++- .../client/samples/FlatObjectBasics.java | 111 ++++++++ .../client/samples/util/IssueDocument.java | 94 +++++++ 5 files changed, 252 insertions(+), 268 deletions(-) create mode 100644 samples/src/main/java/org/opensearch/client/samples/FlatObjectBasics.java create mode 100644 samples/src/main/java/org/opensearch/client/samples/util/IssueDocument.java diff --git a/USER_GUIDE.md b/USER_GUIDE.md index 4b21974f32..cce234bb3e 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -7,6 +7,7 @@ - [Creating an index](#creating-an-index) - [With default settings](#with-default-settings) - [With custom settings and mappings](#with-custom-settings-and-mappings) + - [With FlatObject mappings](#with-flat-object-mappings) - [Indexing data](#indexing-data) - [Searching for a document](#searching-for-a-document) - [Deleting a document](#deleting-a-document) @@ -136,6 +137,18 @@ CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder() .build(); client.indices().create(createIndexRequest); ``` +#### With flat object mappings +OpenSearch supports FlatObject mappings from version 2.7.0 without additional parameters. + +```java +final CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder().index(indexName) + .mappings(m -> m.properties("issue", Property.of(p -> p.flatObject(new FlatObjectProperty.Builder().build())))) + .build(); +client.indices().create(createIndexRequest); +``` + +You can find a working sample of the above code in [FlatObjectBasics.java](./samples/src/main/java/org/opensearch/client/samples/FlatObjectBasics.java). + ### Indexing data diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/_types/mapping/FlatObjectProperty.java b/java-client/src/main/java/org/opensearch/client/opensearch/_types/mapping/FlatObjectProperty.java index fc21564e65..bb317da753 100644 --- a/java-client/src/main/java/org/opensearch/client/opensearch/_types/mapping/FlatObjectProperty.java +++ b/java-client/src/main/java/org/opensearch/client/opensearch/_types/mapping/FlatObjectProperty.java @@ -34,7 +34,6 @@ import jakarta.json.stream.JsonGenerator; import java.util.function.Function; -import javax.annotation.Nullable; import org.opensearch.client.json.JsonpDeserializable; import org.opensearch.client.json.JsonpDeserializer; import org.opensearch.client.json.JsonpMapper; @@ -46,48 +45,12 @@ @JsonpDeserializable public class FlatObjectProperty extends PropertyBase implements PropertyVariant { - @Nullable - private final Double boost; - - @Nullable - private final Integer depthLimit; - - @Nullable - private final Boolean docValues; - - @Nullable - private final Boolean eagerGlobalOrdinals; - - @Nullable - private final Boolean index; - - @Nullable - private final IndexOptions indexOptions; - - @Nullable - private final String nullValue; - - @Nullable - private final String similarity; - - @Nullable - private final Boolean splitQueriesOnWhitespace; // --------------------------------------------------------------------------------------------- private FlatObjectProperty(Builder builder) { super(builder); - this.boost = builder.boost; - this.depthLimit = builder.depthLimit; - this.docValues = builder.docValues; - this.eagerGlobalOrdinals = builder.eagerGlobalOrdinals; - this.index = builder.index; - this.indexOptions = builder.indexOptions; - this.nullValue = builder.nullValue; - this.similarity = builder.similarity; - this.splitQueriesOnWhitespace = builder.splitQueriesOnWhitespace; - } public static FlatObjectProperty of(Function> fn) { @@ -102,127 +65,10 @@ public Property.Kind _propertyKind() { return Property.Kind.FlatObject; } - /** - * API name: {@code boost} - */ - @Nullable - public final Double boost() { - return this.boost; - } - - /** - * API name: {@code depth_limit} - */ - @Nullable - public final Integer depthLimit() { - return this.depthLimit; - } - - /** - * API name: {@code doc_values} - */ - @Nullable - public final Boolean docValues() { - return this.docValues; - } - - /** - * API name: {@code eager_global_ordinals} - */ - @Nullable - public final Boolean eagerGlobalOrdinals() { - return this.eagerGlobalOrdinals; - } - - /** - * API name: {@code index} - */ - @Nullable - public final Boolean index() { - return this.index; - } - - /** - * API name: {@code index_options} - */ - @Nullable - public final IndexOptions indexOptions() { - return this.indexOptions; - } - - /** - * API name: {@code null_value} - */ - @Nullable - public final String nullValue() { - return this.nullValue; - } - - /** - * API name: {@code similarity} - */ - @Nullable - public final String similarity() { - return this.similarity; - } - - /** - * API name: {@code split_queries_on_whitespace} - */ - @Nullable - public final Boolean splitQueriesOnWhitespace() { - return this.splitQueriesOnWhitespace; - } - protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { generator.write("type", "flat_object"); super.serializeInternal(generator, mapper); - if (this.boost != null) { - generator.writeKey("boost"); - generator.write(this.boost); - - } - if (this.depthLimit != null) { - generator.writeKey("depth_limit"); - generator.write(this.depthLimit); - - } - if (this.docValues != null) { - generator.writeKey("doc_values"); - generator.write(this.docValues); - - } - if (this.eagerGlobalOrdinals != null) { - generator.writeKey("eager_global_ordinals"); - generator.write(this.eagerGlobalOrdinals); - - } - if (this.index != null) { - generator.writeKey("index"); - generator.write(this.index); - - } - if (this.indexOptions != null) { - generator.writeKey("index_options"); - this.indexOptions.serialize(generator, mapper); - } - if (this.nullValue != null) { - generator.writeKey("null_value"); - generator.write(this.nullValue); - - } - if (this.similarity != null) { - generator.writeKey("similarity"); - generator.write(this.similarity); - - } - if (this.splitQueriesOnWhitespace != null) { - generator.writeKey("split_queries_on_whitespace"); - generator.write(this.splitQueriesOnWhitespace); - - } - } // --------------------------------------------------------------------------------------------- @@ -232,104 +78,6 @@ protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { */ public static class Builder extends PropertyBase.AbstractBuilder implements ObjectBuilder { - @Nullable - private Double boost; - - @Nullable - private Integer depthLimit; - - @Nullable - private Boolean docValues; - - @Nullable - private Boolean eagerGlobalOrdinals; - - @Nullable - private Boolean index; - - @Nullable - private IndexOptions indexOptions; - - @Nullable - private String nullValue; - - @Nullable - private String similarity; - - @Nullable - private Boolean splitQueriesOnWhitespace; - - /** - * API name: {@code boost} - */ - public final Builder boost(@Nullable Double value) { - this.boost = value; - return this; - } - - /** - * API name: {@code depth_limit} - */ - public final Builder depthLimit(@Nullable Integer value) { - this.depthLimit = value; - return this; - } - - /** - * API name: {@code doc_values} - */ - public final Builder docValues(@Nullable Boolean value) { - this.docValues = value; - return this; - } - - /** - * API name: {@code eager_global_ordinals} - */ - public final Builder eagerGlobalOrdinals(@Nullable Boolean value) { - this.eagerGlobalOrdinals = value; - return this; - } - - /** - * API name: {@code index} - */ - public final Builder index(@Nullable Boolean value) { - this.index = value; - return this; - } - - /** - * API name: {@code index_options} - */ - public final Builder indexOptions(@Nullable IndexOptions value) { - this.indexOptions = value; - return this; - } - - /** - * API name: {@code null_value} - */ - public final Builder nullValue(@Nullable String value) { - this.nullValue = value; - return this; - } - - /** - * API name: {@code similarity} - */ - public final Builder similarity(@Nullable String value) { - this.similarity = value; - return this; - } - - /** - * API name: {@code split_queries_on_whitespace} - */ - public final Builder splitQueriesOnWhitespace(@Nullable Boolean value) { - this.splitQueriesOnWhitespace = value; - return this; - } @Override protected Builder self() { @@ -361,15 +109,6 @@ public FlatObjectProperty build() { protected static void setupFlatObjectPropertyDeserializer(ObjectDeserializer op) { PropertyBase.setupPropertyBaseDeserializer(op); - op.add(Builder::boost, JsonpDeserializer.doubleDeserializer(), "boost"); - op.add(Builder::depthLimit, JsonpDeserializer.integerDeserializer(), "depth_limit"); - op.add(Builder::docValues, JsonpDeserializer.booleanDeserializer(), "doc_values"); - op.add(Builder::eagerGlobalOrdinals, JsonpDeserializer.booleanDeserializer(), "eager_global_ordinals"); - op.add(Builder::index, JsonpDeserializer.booleanDeserializer(), "index"); - op.add(Builder::indexOptions, IndexOptions._DESERIALIZER, "index_options"); - op.add(Builder::nullValue, JsonpDeserializer.stringDeserializer(), "null_value"); - op.add(Builder::similarity, JsonpDeserializer.stringDeserializer(), "similarity"); - op.add(Builder::splitQueriesOnWhitespace, JsonpDeserializer.booleanDeserializer(), "split_queries_on_whitespace"); op.ignore("type"); } diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractIndicesClientIT.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractIndicesClientIT.java index 8642f3a6c2..11d7de218f 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractIndicesClientIT.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractIndicesClientIT.java @@ -31,6 +31,8 @@ import org.opensearch.client.opensearch.indices.GetIndexRequest; import org.opensearch.client.opensearch.indices.GetIndexResponse; import org.opensearch.client.opensearch.indices.GetIndicesSettingsRequest; +import org.opensearch.client.opensearch.indices.GetMappingRequest; +import org.opensearch.client.opensearch.indices.GetMappingResponse; import org.opensearch.client.opensearch.indices.IndexState; import org.opensearch.client.opensearch.indices.PutIndexTemplateResponse; import org.opensearch.common.settings.Settings; @@ -200,13 +202,9 @@ public void testGetNotExistingIndexAlias() throws Exception { } @Test - public void createIndex_withFlatObject_IndexCreatesSucessfully() throws IOException { - InfoResponse info = javaClient().info(); - String version = info.version().number(); - if (version.contains("SNAPSHOT")) { - version = version.split("-")[0]; - } - assumeTrue("Flat Object is supported after version 2.7.0 only", Version.fromString(version).onOrAfter(Version.fromString("2.7.0"))); + public void createIndex_withFlatObject_indexCreatesSuccessfully() throws IOException { + assumeFlatObjectSupport(); + try { final CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder().index("flat-object-test") .mappings(m -> m.properties("sample_flat_object", Property.of(p -> p.flatObject(new FlatObjectProperty.Builder().build())))) @@ -217,4 +215,33 @@ public void createIndex_withFlatObject_IndexCreatesSucessfully() throws IOExcept fail(ex.getMessage()); } } + + @Test + public void createIndex_withFlatObject_mappingCanBeRetrieved() throws IOException { + assumeFlatObjectSupport(); + + try { + final String indexName = "flat-object-mapping-index"; + final CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder().index(indexName) + .mappings(m -> m.properties("sample_flat_object", Property.of(p -> p.flatObject(new FlatObjectProperty.Builder().build())))) + .build(); + final CreateIndexResponse createIndexResponse = javaClient().indices().create(createIndexRequest); + assertTrue(createIndexResponse.acknowledged()); + + final GetMappingResponse response = javaClient().indices().getMapping(GetMappingRequest.of(m -> m.index(indexName))); + final Property.Kind mappingKind = response.result().get(indexName).mappings().properties().get("sample_flat_object")._kind(); + assertEquals(mappingKind, Property.Kind.FlatObject); + } catch (OpenSearchException ex) { + fail(ex.getMessage()); + } + } + + private void assumeFlatObjectSupport() throws IOException { + InfoResponse info = javaClient().info(); + String version = info.version().number(); + if (version.contains("SNAPSHOT")) { + version = version.split("-")[0]; + } + assumeTrue("Flat Object is supported after version 2.7.0 only", Version.fromString(version).onOrAfter(Version.fromString("2.7.0"))); + } } diff --git a/samples/src/main/java/org/opensearch/client/samples/FlatObjectBasics.java b/samples/src/main/java/org/opensearch/client/samples/FlatObjectBasics.java new file mode 100644 index 0000000000..240e53c67e --- /dev/null +++ b/samples/src/main/java/org/opensearch/client/samples/FlatObjectBasics.java @@ -0,0 +1,111 @@ +package org.opensearch.client.samples; + +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.client.opensearch._types.FieldValue; +import org.opensearch.client.opensearch._types.mapping.FlatObjectProperty; +import org.opensearch.client.opensearch._types.mapping.Property; +import org.opensearch.client.opensearch.core.IndexRequest; +import org.opensearch.client.opensearch.core.SearchRequest; +import org.opensearch.client.opensearch.core.SearchResponse; +import org.opensearch.client.opensearch.indices.CreateIndexRequest; +import org.opensearch.client.samples.util.IssueDocument; + +public class FlatObjectBasics { + private static final Logger LOGGER = LogManager.getLogger(IndexingBasics.class); + + public static void main(String[] args) { + try { + var client = SampleClient.create(); + + var version = client.info().version(); + LOGGER.info("Server: {}@{}", version.distribution(), version.number()); + + final var indexName = "flat_object-sample"; + + // Create a new index with an "issue" field with flat_object type + if (!client.indices().exists(e -> e.index(indexName)).value()) { + final CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder().index(indexName) + .mappings(m -> m.properties("issue", Property.of(p -> p.flatObject(new FlatObjectProperty.Builder().build())))) + .build(); + client.indices().create(createIndexRequest); + } + + // Index some flat object documents + final IssueDocument issueDocument = new IssueDocument(); + final IssueDocument.Issue issue = new IssueDocument.Issue(); + issue.setNumber("123456"); + final IssueDocument.Labels labels = new IssueDocument.Labels(); + labels.setBackport(List.of("1.3", "2.0")); + labels.setVersion("2.1"); + final IssueDocument.Category category = new IssueDocument.Category(); + category.setType("API"); + category.setLevel("enhancement"); + + labels.setCategory(category); + issue.setLabels(labels); + + issueDocument.setIssue(issue); + final IndexRequest indexRequest = new IndexRequest.Builder().index(indexName) + .id("1") + .document(issueDocument) + .build(); + + client.index(indexRequest); + + // Index second document + final IssueDocument document2 = new IssueDocument(); + final IssueDocument.Issue issue2 = new IssueDocument.Issue(); + issue2.setNumber("123457"); + + IssueDocument.Labels labels2 = new IssueDocument.Labels(); + labels2.setVersion("2.2"); + + IssueDocument.Category category2 = new IssueDocument.Category(); + category2.setType("API"); + category2.setLevel("bug"); + + labels2.setCategory(category2); + issue2.setLabels(labels2); + + document2.setIssue(issue2); + + final IndexRequest indexRequest2 = new IndexRequest.Builder().index(indexName) + .id("2") + .document(document2) + .build(); + + client.index(indexRequest2); + + // wait for the document to index - as refresh period is set to 3s + Thread.sleep(3000); + + // Search for the documents + // Ref: https://opensearch.org/docs/latest/field-types/supported-field-types/flat-object/ + // Even if you don’t know the field names, you can search for a leaf value in the entire flat object. + // For example, the following request searches for all issues labeled as bugs + SearchRequest searchRequest = new SearchRequest.Builder().index(indexName) + .query(q -> q.match(m -> m.field("issue").query(FieldValue.of("bug")))) + .build(); + + SearchResponse searchResponse = client.search(searchRequest, IssueDocument.class); + for (var hit : searchResponse.hits().hits()) { + LOGGER.info("Found {} with score {}", hit.source(), hit.score()); + } + + // Alternatively, if you know the subfield name in which to search, provide the field’s path in dot notation + SearchRequest searchRequest2 = new SearchRequest.Builder().index(indexName) + .query(q -> q.match(m -> m.field("issue.labels.category.level").query(FieldValue.of("bug")))) + .build(); + + SearchResponse searchResponse2 = client.search(searchRequest2, IssueDocument.class); + for (var hit : searchResponse2.hits().hits()) { + LOGGER.info("Found {} with score {}", hit.source(), hit.score()); + } + + } catch (Exception e) { + LOGGER.error("Unexpected exception", e); + } + } +} diff --git a/samples/src/main/java/org/opensearch/client/samples/util/IssueDocument.java b/samples/src/main/java/org/opensearch/client/samples/util/IssueDocument.java new file mode 100644 index 0000000000..03f367019f --- /dev/null +++ b/samples/src/main/java/org/opensearch/client/samples/util/IssueDocument.java @@ -0,0 +1,94 @@ +package org.opensearch.client.samples.util; + +import java.util.List; + +public class IssueDocument { + + private Issue issue; + + // Getter and setter for "issue" + public Issue getIssue() { + return issue; + } + + public void setIssue(Issue issue) { + this.issue = issue; + } + + public static class Issue { + private String number; + private Labels labels; + + // Getters and setters for "number" and "labels" + public String getNumber() { + return number; + } + + public void setNumber(String number) { + this.number = number; + } + + public Labels getLabels() { + return labels; + } + + public void setLabels(Labels labels) { + this.labels = labels; + } + + } + + public static class Labels { + private String version; + private List backport; + private Category category; + + // Getters and setters for "version", "backport", and "category" + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public List getBackport() { + return backport; + } + + public void setBackport(List backport) { + this.backport = backport; + } + + public Category getCategory() { + return category; + } + + public void setCategory(Category category) { + this.category = category; + } + + } + + public static class Category { + private String type; + private String level; + + // Getters and setters for "type" and "level" + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getLevel() { + return level; + } + + public void setLevel(String level) { + this.level = level; + } + } +}