Skip to content

Commit

Permalink
fix: add missing highlight and source field in MultisearchBody.Builder (
Browse files Browse the repository at this point in the history
opensearch-project#442)

* fix: add missing highlight and source field in MultisearchBody.Builder

Signed-off-by: Dominik Szczepanczyk <szczepanczyk.dominik@gmail.com>

* update CHANGELOG.md

Signed-off-by: Dominik Szczepanczyk <szczepanczyk.dominik@gmail.com>

---------

Signed-off-by: Dominik Szczepanczyk <szczepanczyk.dominik@gmail.com>
  • Loading branch information
szczepanczykd committed Apr 12, 2023
1 parent e2869c0 commit 2f99a6c
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Fix AwsSdk2TransportOptions.responseCompression ([#322](https://github.com/opensearch-project/opensearch-java/pull/322))
- Bulk UpdateOperation misses upsert options ([#353](https://github.com/opensearch-project/opensearch-java/pull/353))
- Fix missing key property in the RangeBucket ([#370](https://github.com/opensearch-project/opensearch-java/pull/370))
- Fix missing Highlight and SourceConfig in the MultisearchBody ([#442](https://github.com/opensearch-project/opensearch-java/pull/442))

### Security

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@

import org.opensearch.client.opensearch._types.aggregations.Aggregation;
import org.opensearch.client.opensearch._types.query_dsl.Query;
import org.opensearch.client.opensearch.core.search.Highlight;
import org.opensearch.client.opensearch.core.search.SourceConfig;
import org.opensearch.client.opensearch.core.search.Suggester;
import org.opensearch.client.opensearch.core.search.TrackHits;
import org.opensearch.client.json.JsonpDeserializable;
Expand Down Expand Up @@ -77,6 +79,12 @@ public class MultisearchBody implements JsonpSerializable {
@Nullable
private final Suggester suggest;

@Nullable
private Highlight highlight;

@Nullable
private SourceConfig source;

// ---------------------------------------------------------------------------------------------

private MultisearchBody(Builder builder) {
Expand All @@ -87,6 +95,8 @@ private MultisearchBody(Builder builder) {
this.size = builder.size;
this.trackTotalHits = builder.trackTotalHits;
this.suggest = builder.suggest;
this.highlight = builder.highlight;
this.source = builder.source;

}

Expand Down Expand Up @@ -141,6 +151,22 @@ public final Suggester suggest() {
return this.suggest;
}

/**
* API name: {@code highlight}
*/
@Nullable
public final Highlight highlight() {
return this.highlight;
}

/**
* API name: {@code _source}
*/
@Nullable
public final SourceConfig source() {
return this.source;
}

/**
* Serialize this object to JSON.
*/
Expand Down Expand Up @@ -189,6 +215,18 @@ protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) {

}

if (this.highlight != null) {
generator.writeKey("highlight");
this.highlight.serialize(generator, mapper);

}

if (this.source != null) {
generator.writeKey("_source");
this.source.serialize(generator, mapper);

}

}

// ---------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -216,6 +254,13 @@ public static class Builder extends ObjectBuilderBase implements ObjectBuilder<M
@Nullable
private Suggester suggest;

@Nullable
private Highlight highlight;

@Nullable
private SourceConfig source;


/**
* API name: {@code aggregations}
* <p>
Expand Down Expand Up @@ -306,6 +351,36 @@ public final Builder suggest(Function<Suggester.Builder, ObjectBuilder<Suggester
return this.suggest(fn.apply(new Suggester.Builder()).build());
}

/**
* API name: {@code highlight}
*/
public final Builder highlight(@Nullable Highlight value) {
this.highlight = value;
return this;
}

/**
* API name: {@code highlight}
*/
public final Builder highlight(Function<Highlight.Builder, ObjectBuilder<Highlight>> fn) {
return this.highlight(fn.apply(new Highlight.Builder()).build());
}

/**
* API name: {@code _source}
*/
public final Builder source(@Nullable SourceConfig value) {
this.source = value;
return this;
}

/**
* API name: {@code _source}
*/
public final Builder source(Function<SourceConfig.Builder, ObjectBuilder<SourceConfig>> fn) {
return this.source(fn.apply(new SourceConfig.Builder()).build());
}

/**
* Builds a {@link MultisearchBody}.
*
Expand Down Expand Up @@ -336,6 +411,8 @@ protected static void setupMultisearchBodyDeserializer(ObjectDeserializer<Multis
op.add(Builder::size, JsonpDeserializer.integerDeserializer(), "size");
op.add(Builder::trackTotalHits, TrackHits._DESERIALIZER, "track_total_hits");
op.add(Builder::suggest, Suggester._DESERIALIZER, "suggest");
op.add(Builder::highlight, Highlight._DESERIALIZER, "highlight");
op.add(Builder::source, SourceConfig._DESERIALIZER, "_source");

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.opensearch.integTest;

import org.junit.Test;
import org.opensearch.client.opensearch._types.FieldValue;
import org.opensearch.client.opensearch._types.Refresh;
import org.opensearch.client.opensearch._types.query_dsl.Query;
import org.opensearch.client.opensearch._types.query_dsl.TermQuery;
import org.opensearch.client.opensearch.core.MsearchResponse;
import org.opensearch.client.opensearch.core.msearch.MultiSearchResponseItem;
import org.opensearch.client.opensearch.core.msearch.RequestItem;
import org.opensearch.client.opensearch.core.search.Highlight;
import org.opensearch.client.opensearch.core.search.HighlightField;
import org.opensearch.client.opensearch.core.search.Hit;
import org.opensearch.client.opensearch.core.search.SourceConfig;

import java.io.IOException;
import java.util.List;

public abstract class AbstractMultiSearchRequestIT extends OpenSearchJavaClientTestCase {

@Test
public void shouldReturnMultipleSearches() throws Exception {
String index = "multiple_searches_request";
createTestDocuments(index);

RequestItem largeItemsQuery = createMSearchQuery("large");
RequestItem mediumItemsQuery = createMSearchQuery("medium");
RequestItem smallItemsQuery = createMSearchQuery("small");

MsearchResponse<ShopItem> response = sendMSearchRequest(index, List.of(largeItemsQuery, mediumItemsQuery, smallItemsQuery));
assertEquals(3, response.responses().size());
assertEquals(2, response.responses().get(0).result().hits().hits().size());
assertEquals(2, response.responses().get(1).result().hits().hits().size());
assertEquals(2, response.responses().get(2).result().hits().hits().size());
}

@Test
public void shouldReturnHighlightsInMultipleSearches() throws Exception {
String index = "multiple_searches_request_with_highlights";
createTestDocuments(index);

RequestItem largeItemsQuery = createMSearchQueryWithHighlight("large");
RequestItem mediumItemsQuery = createMSearchQueryWithHighlight("medium");
RequestItem smallItemsQuery = createMSearchQueryWithHighlight("small");

MsearchResponse<ShopItem> response = sendMSearchRequest(index, List.of(largeItemsQuery, mediumItemsQuery, smallItemsQuery));
assertEquals(3, response.responses().size());
assertResponseHighlights(response.responses().get(0));
assertResponseHighlights(response.responses().get(1));
assertResponseHighlights(response.responses().get(2));
}

@Test
public void shouldReturnMultiSearchesWithSelectedSourceFieldsOnly() throws Exception {
String index = "multiple_searches_request_with_sources";
createTestDocuments(index);

RequestItem largeItemsQuery = createMSearchQueryWithSelectedSourceFields("large");
RequestItem mediumItemsQuery = createMSearchQueryWithSelectedSourceFields("medium");
RequestItem smallItemsQuery = createMSearchQueryWithSelectedSourceFields("small");

MsearchResponse<ShopItem> response = sendMSearchRequest(index, List.of(largeItemsQuery, mediumItemsQuery, smallItemsQuery));
assertEquals(3, response.responses().size());
assertResponseSources(response.responses().get(0));
assertResponseSources(response.responses().get(1));
assertResponseSources(response.responses().get(2));
}

private void assertResponseSources(MultiSearchResponseItem<ShopItem> response) {
List<Hit<ShopItem>> hitsWithHighlights = response.result().hits().hits();
assertEquals(2, hitsWithHighlights.size());
for (Hit<ShopItem> shopItemHit : hitsWithHighlights) {
assertNotNull(shopItemHit.source());
assertNotNull(shopItemHit.source().getName());
assertNotNull(shopItemHit.source().getSize());
assertNull(shopItemHit.source().getCompany());
}
}

private void assertResponseHighlights(MultiSearchResponseItem<ShopItem> response) {
List<Hit<ShopItem>> hitsWithHighlights = response.result().hits().hits();
assertEquals(2, hitsWithHighlights.size());
assertEquals(1, hitsWithHighlights.get(0).highlight().size());
assertEquals(1, hitsWithHighlights.get(1).highlight().size());
}

private RequestItem createMSearchQuery(String itemSize) {
return createMSearchQuery(itemSize, null, List.of());
}

private RequestItem createMSearchQueryWithHighlight(String itemSize) {
return createMSearchQuery(itemSize, "size", List.of());
}

private RequestItem createMSearchQueryWithSelectedSourceFields(String itemSize) {
return createMSearchQuery(itemSize, null, List.of("name", "size"));
}

private RequestItem createMSearchQuery(String itemSize, String fieldName, List<String> sources) {
return RequestItem.of(item -> item.header(header -> header)
.body(body -> body.query(createItemSizeSearchQuery(itemSize))
.highlight(createHighlight(fieldName))
.source(createSourcesConfig(sources))
)
);
}

private SourceConfig createSourcesConfig(List<String> sources) {
return sources.isEmpty() ? null : SourceConfig.of(builder -> builder.filter(filter -> filter.includes(sources)));
}

private Highlight createHighlight(String fieldName) {
return fieldName != null ? Highlight.of(
builder -> builder.fields(
fieldName, HighlightField.of(
field -> field.preTags("<em class=\"highlight\">")
.postTags("</em>")
)
)
) : null;
}

private Query createItemSizeSearchQuery(String itemSize) {
return Query.of(query -> query.bool(builder -> builder.filter(
filter -> filter.term(
TermQuery.of(term -> term.field("size")
.value(FieldValue.of(itemSize))
)
)
)));
}

private MsearchResponse<ShopItem> sendMSearchRequest(String index, List<RequestItem> searches) throws IOException {
return javaClient().msearch(builder -> builder.index(List.of(index)).searches(searches), ShopItem.class);
}

private void createTestDocuments(String index) throws IOException {
javaClient().create(_1 -> _1.index(index).id("1").document(createItem("hammer", "large", "yes")).refresh(Refresh.True));
javaClient().create(_1 -> _1.index(index).id("2").document(createItem("drill", "large", "yes")).refresh(Refresh.True));
javaClient().create(_1 -> _1.index(index).id("3").document(createItem("jack", "medium", "yes")).refresh(Refresh.True));
javaClient().create(_1 -> _1.index(index).id("4").document(createItem("wrench", "medium", "no")).refresh(Refresh.True));
javaClient().create(_1 -> _1.index(index).id("5").document(createItem("screws", "small", "no")).refresh(Refresh.True));
javaClient().create(_1 -> _1.index(index).id("6").document(createItem("nuts", "small", "no")).refresh(Refresh.True));
}

private ShopItem createItem(String name, String size, String company) {
return new ShopItem(name, size, company);
}

public static class ShopItem {
private String name;
private String size;
private String company;

public ShopItem() {
}

public ShopItem(String name, String size, String company) {
this.name = name;
this.size = size;
this.company = company;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getSize() {
return size;
}

public void setSize(String size) {
this.size = size;
}

public String getCompany() {
return company;
}

public void setCompany(String company) {
this.company = company;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.opensearch.integTest.httpclient5;

import org.opensearch.client.opensearch.integTest.AbstractMultiSearchRequestIT;

public class MultiSearchRequestIT extends AbstractMultiSearchRequestIT implements HttpClient5TransportSupport {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.opensearch.integTest.restclient;

import org.apache.hc.core5.http.HttpHost;
import org.opensearch.client.json.jackson.JacksonJsonpMapper;
import org.opensearch.client.opensearch.integTest.AbstractMultiSearchRequestIT;
import org.opensearch.client.transport.OpenSearchTransport;
import org.opensearch.client.transport.rest_client.RestClientTransport;
import org.opensearch.common.settings.Settings;

import java.io.IOException;

public class MultiSearchRequestIT extends AbstractMultiSearchRequestIT {
@Override
public OpenSearchTransport buildTransport(Settings settings, HttpHost[] hosts) throws IOException {
return new RestClientTransport(buildClient(settings, hosts), new JacksonJsonpMapper());
}
}

0 comments on commit 2f99a6c

Please sign in to comment.