Skip to content

Commit

Permalink
Change Elastic QueryBuilder to return ObjectNode instead of String
Browse files Browse the repository at this point in the history
This is helpful to add more json fields (like from / size) on the top of existing query
  • Loading branch information
asereda-gs committed Jun 7, 2019
1 parent 6a69056 commit 845a109
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 128 deletions.
Expand Up @@ -26,7 +26,7 @@
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.immutables.criteria.Criterias;
import org.immutables.criteria.expression.Expressional;
import org.immutables.criteria.expression.Expression;
import org.immutables.criteria.internal.Backend;
import org.immutables.criteria.internal.Query;
import org.immutables.criteria.internal.Reactive;
Expand Down Expand Up @@ -66,16 +66,15 @@ public ElasticBackend(RestClient restClient,
public Publisher<T> execute(Query query) {
Objects.requireNonNull(query, "query");

return queryInternal(Criterias.toExpressional(query.criteria()));
return queryInternal(query);
}

private Publisher<T> queryInternal(Expressional expressional) {
private Publisher<T> queryInternal(Query query) {
final Request request = new Request("POST", String.format("/%s/_search", index));

final String json = Elasticsearch.converter(mapper).convert(expressional.expression());

request.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));

final Expression expression = Criterias.toExpression(query.criteria());
final ObjectNode json = Elasticsearch.converter(mapper).convert(expression);
request.setEntity(new StringEntity(json.toString(), ContentType.APPLICATION_JSON));
return Reactive.flatMapIterable(Reactive.map(new AsyncRestPublisher(restClient, request), converter()), x -> x);
}

Expand Down
Expand Up @@ -16,42 +16,29 @@

package org.immutables.criteria.elasticsearch;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.immutables.criteria.expression.Expression;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.immutables.criteria.expression.ExpressionConverter;
import org.immutables.criteria.expression.Expressions;

import java.io.IOException;
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.util.Objects;

final class Elasticsearch {

private Elasticsearch() {}

static ExpressionConverter<String> converter(ObjectMapper mapper) {
static ExpressionConverter<ObjectNode> converter(ObjectMapper mapper) {
Objects.requireNonNull(mapper, "expression");

return expression -> {
if (Expressions.isNil(expression)) {
return mapper.createObjectNode().toString();
return mapper.createObjectNode();
}
ElasticsearchQueryVisitor visitor = new ElasticsearchQueryVisitor();
final ObjectNode query = mapper.createObjectNode();
final ElasticsearchQueryVisitor visitor = new ElasticsearchQueryVisitor();
final QueryBuilders.QueryBuilder builder = expression.accept(visitor);

try (StringWriter writer = new StringWriter();
JsonGenerator generator = mapper.getFactory().createGenerator(writer)) {
generator.writeStartObject();
generator.writeFieldName("query");
QueryBuilders.constantScoreQuery(builder).writeJson(generator);
generator.writeEndObject();
generator.flush();
return writer.toString();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
query.set("query", QueryBuilders.constantScoreQuery(builder).toJson(mapper));
return query;
};
}

Expand Down
Expand Up @@ -15,9 +15,11 @@
*/
package org.immutables.criteria.elasticsearch;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -188,10 +190,8 @@ abstract static class QueryBuilder {

/**
* Convert existing query to JSON format using jackson API.
* @param generator used to generate JSON elements
* @throws IOException if IO error occurred
*/
abstract void writeJson(JsonGenerator generator) throws IOException;
abstract ObjectNode toJson(ObjectMapper mapper);
}

/**
Expand Down Expand Up @@ -227,33 +227,29 @@ BoolQueryBuilder should(QueryBuilder queryBuilder) {
return this;
}

@Override protected void writeJson(JsonGenerator gen) throws IOException {
gen.writeStartObject();
gen.writeFieldName("bool");
gen.writeStartObject();
writeJsonArray("must", mustClauses, gen);
writeJsonArray("filter", filterClauses, gen);
writeJsonArray("must_not", mustNotClauses, gen);
writeJsonArray("should", shouldClauses, gen);
gen.writeEndObject();
gen.writeEndObject();
@Override
ObjectNode toJson(ObjectMapper mapper) {
ObjectNode result = mapper.createObjectNode();
ObjectNode bool = result.with("bool");
writeJsonArray("must", mustClauses, bool, mapper);
writeJsonArray("filter", filterClauses, bool, mapper);
writeJsonArray("must_not", mustNotClauses, bool, mapper);
writeJsonArray("should", shouldClauses, bool, mapper);
return result;
}

private void writeJsonArray(String field, List<QueryBuilder> clauses, JsonGenerator gen)
throws IOException {
private void writeJsonArray(String field, List<QueryBuilder> clauses, ObjectNode node, ObjectMapper mapper) {
if (clauses.isEmpty()) {
return;
}

if (clauses.size() == 1) {
gen.writeFieldName(field);
clauses.get(0).writeJson(gen);
node.set(field, clauses.get(0).toJson(mapper));
} else {
gen.writeArrayFieldStart(field);
final ArrayNode arrayNode = node.withArray(field);
for (QueryBuilder clause: clauses) {
clause.writeJson(gen);
arrayNode.add(clause.toJson(mapper));
}
gen.writeEndArray();
}
}
}
Expand All @@ -270,14 +266,11 @@ private TermQueryBuilder(final String fieldName, final Object value) {
this.value = Objects.requireNonNull(value, "value");
}

@Override void writeJson(final JsonGenerator generator) throws IOException {
generator.writeStartObject();
generator.writeFieldName("term");
generator.writeStartObject();
generator.writeFieldName(fieldName);
writeObject(generator, value);
generator.writeEndObject();
generator.writeEndObject();
@Override
ObjectNode toJson(ObjectMapper mapper) {
ObjectNode result = mapper.createObjectNode();
result.with("term").set(fieldName, toJsonValue(value, mapper));
return result;
}
}

Expand All @@ -293,33 +286,17 @@ private TermsQueryBuilder(final String fieldName, final Iterable<?> values) {
this.values = Objects.requireNonNull(values, "values");
}

@Override void writeJson(final JsonGenerator generator) throws IOException {
generator.writeStartObject();
generator.writeFieldName("terms");
generator.writeStartObject();
generator.writeFieldName(fieldName);
generator.writeStartArray();
@Override
ObjectNode toJson(ObjectMapper mapper) {
ObjectNode result = mapper.createObjectNode();
final ArrayNode terms = result.with("terms").withArray(fieldName);
for (Object value: values) {
writeObject(generator, value);
terms.add(toJsonValue(value, mapper));
}
generator.writeEndArray();
generator.writeEndObject();
generator.writeEndObject();
return result;
}
}

/**
* Write usually simple (scalar) value (string, number, boolean or null) to json output.
* In case of complex objects delegates to jackson serialization.
*
* @param generator api to generate JSON document
* @param value JSON value to write
* @throws IOException if can't write to output
*/
private static void writeObject(JsonGenerator generator, Object value) throws IOException {
generator.writeObject(value);
}

/**
* A Query that matches documents within an range of terms.
*/
Expand Down Expand Up @@ -370,36 +347,29 @@ RangeQueryBuilder format(String format) {
return this;
}

@Override void writeJson(final JsonGenerator generator) throws IOException {
@Override
ObjectNode toJson(ObjectMapper mapper) {
if (lt == null && gt == null) {
throw new IllegalStateException("Either lower or upper bound should be provided");
}

generator.writeStartObject();
generator.writeFieldName("range");
generator.writeStartObject();
generator.writeFieldName(fieldName);
generator.writeStartObject();

final ObjectNode result = mapper.createObjectNode();
final ObjectNode range = result.with("range").with(fieldName);
if (gt != null) {
final String op = gte ? "gte" : "gt";
generator.writeFieldName(op);
writeObject(generator, gt);
range.set(op, toJsonValue(gt, mapper));
}

if (lt != null) {
final String op = lte ? "lte" : "lt";
generator.writeFieldName(op);
writeObject(generator, lt);
range.set(op, toJsonValue(lt, mapper));
}

if (format != null) {
generator.writeStringField("format", format);
range.put("format", format);
}

generator.writeEndObject();
generator.writeEndObject();
generator.writeEndObject();
return result;
}
}

Expand All @@ -415,7 +385,8 @@ static class RegexpQueryBuilder extends QueryBuilder {
this.value = value;
}

@Override void writeJson(final JsonGenerator generator) throws IOException {
@Override
ObjectNode toJson(ObjectMapper mapper) {
throw new UnsupportedOperationException();
}
}
Expand All @@ -430,13 +401,11 @@ static class ExistsQueryBuilder extends QueryBuilder {
this.fieldName = Objects.requireNonNull(fieldName, "fieldName");
}

@Override void writeJson(final JsonGenerator generator) throws IOException {
generator.writeStartObject();
generator.writeFieldName("exists");
generator.writeStartObject();
generator.writeStringField("field", fieldName);
generator.writeEndObject();
generator.writeEndObject();
@Override
ObjectNode toJson(ObjectMapper mapper) {
ObjectNode result = mapper.createObjectNode();
result.with("exists").put("field", fieldName);
return result;
}
}

Expand All @@ -452,14 +421,11 @@ private ConstantScoreQueryBuilder(final QueryBuilder builder) {
this.builder = Objects.requireNonNull(builder, "builder");
}

@Override void writeJson(final JsonGenerator generator) throws IOException {
generator.writeStartObject();
generator.writeFieldName("constant_score");
generator.writeStartObject();
generator.writeFieldName("filter");
builder.writeJson(generator);
generator.writeEndObject();
generator.writeEndObject();
@Override
ObjectNode toJson(ObjectMapper mapper) {
ObjectNode result = mapper.createObjectNode();
result.with("constant_score").set("filter", builder.toJson(mapper));
return result;
}
}

Expand All @@ -475,13 +441,26 @@ static class MatchAllQueryBuilder extends QueryBuilder {

private MatchAllQueryBuilder() {}

@Override void writeJson(final JsonGenerator generator) throws IOException {
generator.writeStartObject();
generator.writeFieldName("match_all");
generator.writeStartObject();
generator.writeEndObject();
generator.writeEndObject();
@Override
ObjectNode toJson(ObjectMapper mapper) {
ObjectNode node = mapper.createObjectNode();
node.putObject("match_all");
return node;
}
}

/**
* Write usually simple (scalar) value (string, number, boolean or null) to json output.
* In case of complex objects delegates to jackson serialization.
*
* @param value JSON value to write
*/
private static JsonNode toJsonValue(Object value, ObjectMapper mapper) {
if (value == null) {
return mapper.getNodeFactory().nullNode();
}

return mapper.convertValue(value, JsonNode.class);
}
}

Expand Up @@ -15,13 +15,9 @@
*/
package org.immutables.criteria.elasticsearch;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.junit.Test;

import java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
Expand Down Expand Up @@ -166,17 +162,12 @@ public void range() throws Exception {
}

@Test
public void matchAll() throws IOException {
public void matchAll() {
assertEquals("{\"match_all\":{}}",
toJson(QueryBuilders.matchAll()));
}

private String toJson(QueryBuilders.QueryBuilder builder) throws IOException {
StringWriter writer = new StringWriter();
JsonGenerator gen = mapper.getFactory().createGenerator(writer);
builder.writeJson(gen);
gen.flush();
gen.close();
return writer.toString();
private String toJson(QueryBuilders.QueryBuilder builder) {
return builder.toJson(mapper).toString();
}
}

0 comments on commit 845a109

Please sign in to comment.