Skip to content

fix(clients): correctly deserialize SearchResult #4756

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import io.github.cdimascio.dotenv.Dotenv;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

class Actor extends Hit {

Expand Down Expand Up @@ -47,6 +46,7 @@ public static void main(String[] args) throws Exception {

singleSearch(client, indexName, query);
multiSearch(indexName, query, client);
multiSearchHits(indexName, query, client);
client.close();
}

Expand All @@ -73,13 +73,27 @@ private static void multiSearch(String indexName, String query, SearchClient cli
searchMethodParams.setRequests(requests);

var responses = client.search(searchMethodParams, Actor.class);
var results = responses.getResults();
System.out.println("-> Multi Index Search:");
for (var result : results) {
var response = (SearchResponse) result;
for (var hit : response.getHits()) {
var record = (Map) hit;
System.out.println("> " + record.get("name"));
for (var result : responses.getResults()) {
for (var hit : ((SearchResponse<Actor>) result).getHits()) {
System.out.println("> " + hit.name);
}
}
}

private static void multiSearchHits(String indexName, String query, SearchClient client) {
var searchQuery = new SearchForHits()
.setIndexName(indexName)
.setQuery(query)
.addAttributesToSnippet("title")
.addAttributesToSnippet("alternative_titles");
List<SearchForHits> requests = List.of(searchQuery);
// with searchForHits, all the types are known, no need to cast
var responses = client.searchForHits(requests, Actor.class);
System.out.println("-> Multi Index SearchForHits:");
for (var result : responses) {
for (var hit : result.getHits()) {
System.out.println("> " + hit.name);
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions specs/search/paths/search/search.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ post:

- Different indices for different purposes, such as, one index for products, another one for marketing content.
- Multiple searches to the same index—for example, with different filters.

Use the helper `searchForHits` or `searchForFacets` to get the results in a more convenient format, if you already know the return type you want.
requestBody:
required: true
description: Muli-search request body. Results are returned in the same order as the requests.
Expand Down
34 changes: 31 additions & 3 deletions templates/java/oneof_interface.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.fasterxml.jackson.databind.annotation.*;
import com.fasterxml.jackson.core.type.TypeReference;
import com.algolia.exceptions.AlgoliaRuntimeException;
import com.algolia.utils.CompoundType;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.BeanProperty;

import java.io.IOException;
import java.util.List;
Expand Down Expand Up @@ -87,19 +89,45 @@ public interface {{classname}}{{#vendorExtensions.x-has-child-generic}}<T>{{/ven
{{/isModel}}
{{/composedSchemas.oneOf}}

class Deserializer{{#vendorExtensions.x-has-child-generic}}<T>{{/vendorExtensions.x-has-child-generic}} extends JsonDeserializer<{{classname}}{{#vendorExtensions.x-has-child-generic}}<T>{{/vendorExtensions.x-has-child-generic}}> {
class Deserializer{{#vendorExtensions.x-has-child-generic}}<T>{{/vendorExtensions.x-has-child-generic}} extends JsonDeserializer<{{classname}}{{#vendorExtensions.x-has-child-generic}}<T>{{/vendorExtensions.x-has-child-generic}}>{{#vendorExtensions.x-has-child-generic}} implements ContextualDeserializer{{/vendorExtensions.x-has-child-generic}} {

private static final Logger LOGGER = Logger.getLogger(Deserializer.class.getName());

{{#vendorExtensions.x-has-child-generic}}
private JavaType returnType;

public Deserializer() {}

private Deserializer(JavaType returnType) {
this.returnType = returnType;
}

@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
JavaType contextualType = ctxt.getContextualType().containedType(0);
return new Deserializer(contextualType);
}
{{/vendorExtensions.x-has-child-generic}}

@Override
public {{classname}}{{#vendorExtensions.x-has-child-generic}}<T>{{/vendorExtensions.x-has-child-generic}} deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
JsonNode tree = jp.readValueAsTree();
{{#composedSchemas.oneOf}}
// deserialize {{{datatypeWithEnum}}}
if (tree.{{#isModel}}isObject(){{#vendorExtensions.x-discriminator-fields}} && tree.has("{{{.}}}"){{/vendorExtensions.x-discriminator-fields}}{{/isModel}}{{#isEnumRef}}isTextual(){{/isEnumRef}}{{#isArray}}isArray(){{/isArray}}{{#isInteger}}isInt(){{/isInteger}}{{#isLong}}isLong(){{/isLong}}{{#isDouble}}isDouble(){{/isDouble}}{{#isBoolean}}isBoolean(){{/isBoolean}}{{#isString}}isTextual(){{/isString}}{{^isEnumRef}}{{^isModel}}{{^isArray}}{{^isInteger}}{{^isLong}}{{^isDouble}}{{^isBoolean}}{{^isString}}isObject(){{/isString}}{{/isBoolean}}{{/isDouble}}{{/isLong}}{{/isInteger}}{{/isArray}}{{/isModel}}{{/isEnumRef}}){
try(JsonParser parser = tree.traverse(jp.getCodec())) {
try(JsonParser parser = tree.traverse(jp.getCodec())) {
{{#isModel}}
return parser.readValueAs({{#vendorExtensions.x-has-child-generic}}new TypeReference<{{datatypeWithEnum}}<T>>() {}{{/vendorExtensions.x-has-child-generic}}{{^vendorExtensions.x-has-child-generic}}{{{datatypeWithEnum}}}.class{{/vendorExtensions.x-has-child-generic}});
{{#vendorExtensions.x-has-child-generic}}
// For generic types, the innerType is erased by Java, we need to use the contextual type.
JavaType innerType = ctxt.getTypeFactory().constructParametricType({{dataType}}.class, returnType);
if (parser.getCurrentToken() == null) {
parser.nextToken();
}
return ctxt.readValue(parser, innerType);
{{/vendorExtensions.x-has-child-generic}}
{{^vendorExtensions.x-has-child-generic}}
return parser.readValueAs({{{datatypeWithEnum}}}.class);
{{/vendorExtensions.x-has-child-generic}}
{{/isModel}}
{{#isEnumRef}}
return parser.readValueAs({{{datatypeWithEnum}}}.class);
Expand Down