diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a20cfb5db..abec0346b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased 2.x] ### Added +- Adds `queryImage` (query_image) field to `NeuralQuery`, following definition in ([Neural Query](https://opensearch.org/docs/latest/query-dsl/specialized/neural/)) ([#1137](https://github.com/opensearch-project/opensearch-java/pull/1138)) ### Dependencies diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/_types/query_dsl/NeuralQuery.java b/java-client/src/main/java/org/opensearch/client/opensearch/_types/query_dsl/NeuralQuery.java index 2bf6d0baef..baadf3e849 100644 --- a/java-client/src/main/java/org/opensearch/client/opensearch/_types/query_dsl/NeuralQuery.java +++ b/java-client/src/main/java/org/opensearch/client/opensearch/_types/query_dsl/NeuralQuery.java @@ -17,6 +17,7 @@ import org.opensearch.client.json.ObjectBuilderDeserializer; import org.opensearch.client.json.ObjectDeserializer; import org.opensearch.client.util.ApiTypeHelper; +import org.opensearch.client.util.MissingRequiredPropertiesException; import org.opensearch.client.util.ObjectBuilder; @JsonpDeserializable @@ -24,6 +25,7 @@ public class NeuralQuery extends QueryBase implements QueryVariant { private final String field; private final String queryText; + private final String queryImage; private final int k; @Nullable private final String modelId; @@ -34,7 +36,11 @@ private NeuralQuery(NeuralQuery.Builder builder) { super(builder); this.field = ApiTypeHelper.requireNonNull(builder.field, this, "field"); - this.queryText = ApiTypeHelper.requireNonNull(builder.queryText, this, "queryText"); + if (builder.queryText == null && builder.queryImage == null && !ApiTypeHelper.requiredPropertiesCheckDisabled()) { + throw new MissingRequiredPropertiesException(this, "queryText", "queryImage"); + } + this.queryText = builder.queryText; + this.queryImage = builder.queryImage; this.k = ApiTypeHelper.requireNonNull(builder.k, this, "k"); this.modelId = builder.modelId; this.filter = builder.filter; @@ -64,7 +70,8 @@ public final String field() { } /** - * Required - Search query text. + * Required - The query_text if query_image is not set. + * Optional - The query_text if query_image is set. * * @return Search query text. */ @@ -72,6 +79,16 @@ public final String queryText() { return this.queryText; } + /** + * Required - The query_image if query_text is not set. + * Optional - The query_image if query_text is set. + * + * @return Search query image. + */ + public final String queryImage() { + return this.queryImage; + } + /** * Required - The number of neighbors to return. * @@ -112,7 +129,13 @@ protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { super.serializeInternal(generator, mapper); - generator.write("query_text", this.queryText); + if (this.queryText != null) { + generator.write("query_text", this.queryText); + } + + if (this.queryImage != null) { + generator.write("query_image", this.queryImage); + } if (this.modelId != null) { generator.write("model_id", this.modelId); @@ -129,7 +152,7 @@ protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { } public Builder toBuilder() { - return new Builder().field(field).queryText(queryText).k(k).modelId(modelId).filter(filter); + return new Builder().field(field).queryText(queryText).queryImage(queryImage).k(k).modelId(modelId).filter(filter); } /** @@ -138,6 +161,7 @@ public Builder toBuilder() { public static class Builder extends QueryBase.AbstractBuilder implements ObjectBuilder { private String field; private String queryText; + private String queryImage; private Integer k; @Nullable private String modelId; @@ -156,7 +180,8 @@ public NeuralQuery.Builder field(@Nullable String field) { } /** - * Required - Search query text. + * Required - The query_text if query_image is not set. + * Optional - The query_text if query_image is set. * * @param queryText Search query text. * @return This builder. @@ -166,6 +191,18 @@ public NeuralQuery.Builder queryText(@Nullable String queryText) { return this; } + /** + * Required - The query_image if query_text is not set. + * Optional - The query_image if query_text is set. + * + * @param queryImage Search query image. + * @return This builder. + */ + public NeuralQuery.Builder queryImage(@Nullable String queryImage) { + this.queryImage = queryImage; + return this; + } + /** * Optional - The model_id field if the default model for the index or field is set. * Required - The model_id field if there is no default model set for the index or field. @@ -227,6 +264,7 @@ protected static void setupNeuralQueryDeserializer(ObjectDeserializer + * If you think this is an error and that the reported property is actually optional, a workaround is + * available in {@link ApiTypeHelper} to disable checks. Use with caution. + */ +public class MissingRequiredPropertiesException extends RuntimeException { + private Class clazz; + private String[] properties; + + public MissingRequiredPropertiesException(Object obj, String... properties) { + super( + "Missing at least one required property between " + + buildPropertiesMsg(properties) + + " in '" + + obj.getClass().getSimpleName() + + "'" + ); + this.clazz = obj.getClass(); + this.properties = properties; + } + + /** + * The class where the missing property was found + */ + public Class getObjectClass() { + return clazz; + } + + public String[] getPropertiesName() { + return properties; + } + + private static String buildPropertiesMsg(String[] properties) { + final StringJoiner sj = new StringJoiner(",", "'", "'"); + for (final String property : properties) { + sj.add(property); + } + return sj.toString(); + } +} diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/_types/query_dsl/NeuralQueryTest.java b/java-client/src/test/java/org/opensearch/client/opensearch/_types/query_dsl/NeuralQueryTest.java index 7bc16e6fd6..76f0d59a74 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/_types/query_dsl/NeuralQueryTest.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/_types/query_dsl/NeuralQueryTest.java @@ -10,10 +10,11 @@ import org.junit.Test; import org.opensearch.client.opensearch.model.ModelTestCase; +import org.opensearch.client.util.MissingRequiredPropertiesException; public class NeuralQueryTest extends ModelTestCase { @Test - public void toBuilder() { + public void toBuilder_queryText() { NeuralQuery origin = new NeuralQuery.Builder().field("field") .queryText("queryText") .k(1) @@ -23,4 +24,37 @@ public void toBuilder() { assertEquals(toJson(copied), toJson(origin)); } + + @Test + public void toBuilder_queryImage() { + NeuralQuery origin = new NeuralQuery.Builder().field("field") + .queryImage("queryImage") + .k(1) + .filter(IdsQuery.of(builder -> builder.values("Some_ID")).toQuery()) + .build(); + NeuralQuery copied = origin.toBuilder().build(); + + assertEquals(toJson(copied), toJson(origin)); + } + + @Test + public void toBuilder_both() { + NeuralQuery origin = new NeuralQuery.Builder().field("field") + .queryText("queryText") + .queryImage("queryImage") + .k(1) + .filter(IdsQuery.of(builder -> builder.values("Some_ID")).toQuery()) + .build(); + NeuralQuery copied = origin.toBuilder().build(); + + assertEquals(toJson(copied), toJson(origin)); + } + + @Test + public void toBuilder_missing_query() { + assertThrows( + MissingRequiredPropertiesException.class, + () -> new NeuralQuery.Builder().field("field").k(1).filter(IdsQuery.of(builder -> builder.values("Some_ID")).toQuery()).build() + ); + } } diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/model/VariantsTest.java b/java-client/src/test/java/org/opensearch/client/opensearch/model/VariantsTest.java index addb6af775..f647ae56f0 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/model/VariantsTest.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/model/VariantsTest.java @@ -234,6 +234,7 @@ public void testNeuralQueryFromJson() { + " \"neural\": {\n" + " \"passage_embedding\": {\n" + " \"query_text\": \"Hi world!\",\n" + + " \"query_image\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj+L+U4T8ABu8CpCYJ1DQAAAAASUVORK5CYII=\",\n" + " \"model_id\": \"bQ1J8ooBpBj3wT4HVUsb\",\n" + " \"k\": 100\n" + " }\n" @@ -245,6 +246,10 @@ public void testNeuralQueryFromJson() { assertEquals("passage_embedding", searchRequest.query().neural().field()); assertEquals("Hi world!", searchRequest.query().neural().queryText()); + assertEquals( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj+L+U4T8ABu8CpCYJ1DQAAAAASUVORK5CYII=", + searchRequest.query().neural().queryImage() + ); assertEquals("bQ1J8ooBpBj3wT4HVUsb", searchRequest.query().neural().modelId()); assertEquals(100, searchRequest.query().neural().k()); }