From a52894f6630e24b2caced353b20e2ed8de2a31ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Wed, 20 Oct 2021 16:39:12 +0200 Subject: [PATCH] replace xcontent with datastructures for JSON building --- cql-elasticsearch/build.gradle | 3 +- .../src/main/java/module-info.java | 4 +- .../ElasticsearchFilterGenerator.java | 12 +- .../ElasticsearchQueryGenerator.java | 44 ++-- .../cql/elasticsearch/FacetsGenerator.java | 49 ++-- .../cql/elasticsearch/FilterGenerator.java | 150 +++++------ .../cql/elasticsearch/QueryGenerator.java | 235 +++++++++--------- .../xbib/cql/elasticsearch/SortGenerator.java | 33 ++- .../cql/elasticsearch/SourceGenerator.java | 46 ++-- .../elasticsearch/ElasticsearchQueryTest.java | 15 +- gradle.properties | 4 +- 11 files changed, 289 insertions(+), 306 deletions(-) diff --git a/cql-elasticsearch/build.gradle b/cql-elasticsearch/build.gradle index 5d65fcd..0cf98ad 100644 --- a/cql-elasticsearch/build.gradle +++ b/cql-elasticsearch/build.gradle @@ -1,5 +1,4 @@ dependencies { api project(':cql-common') - implementation "org.xbib:content-core:${project.property('xbib-content.version')}" - implementation "org.xbib:content-json:${project.property('xbib-content.version')}" + implementation "org.xbib:datastructures-json-tiny:${project.property('xbib-datastructures.version')}" } diff --git a/cql-elasticsearch/src/main/java/module-info.java b/cql-elasticsearch/src/main/java/module-info.java index fd8fb9a..92bca0d 100644 --- a/cql-elasticsearch/src/main/java/module-info.java +++ b/cql-elasticsearch/src/main/java/module-info.java @@ -3,6 +3,6 @@ module org.xbib.cql.elasticsearch { exports org.xbib.cql.elasticsearch.ast; exports org.xbib.cql.elasticsearch.model; requires transitive org.xbib.cql; - requires org.xbib.content.core; - requires org.xbib.content.json; + requires org.xbib.datastructures.api; + requires org.xbib.datastructures.json.tiny; } diff --git a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/ElasticsearchFilterGenerator.java b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/ElasticsearchFilterGenerator.java index 48bd7c8..f6c2549 100644 --- a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/ElasticsearchFilterGenerator.java +++ b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/ElasticsearchFilterGenerator.java @@ -1,6 +1,5 @@ package org.xbib.cql.elasticsearch; -import org.xbib.content.XContentBuilder; import org.xbib.cql.BooleanGroup; import org.xbib.cql.BooleanOperator; import org.xbib.cql.Comparitor; @@ -27,6 +26,7 @@ import org.xbib.cql.elasticsearch.ast.Operator; import org.xbib.cql.elasticsearch.ast.Token; import org.xbib.cql.elasticsearch.ast.TokenType; import org.xbib.cql.elasticsearch.model.ElasticsearchQueryModel; +import org.xbib.datastructures.json.tiny.JsonBuilder; import java.io.IOException; import java.time.ZonedDateTime; @@ -45,7 +45,7 @@ public class ElasticsearchFilterGenerator implements Visitor { private final String globalField; - private FilterGenerator filterGen; + private final FilterGenerator filterGen; public ElasticsearchFilterGenerator(String globalField) { this(globalField, new ElasticsearchQueryModel()); @@ -55,11 +55,7 @@ public class ElasticsearchFilterGenerator implements Visitor { this.globalField = globalField; this.model = model; this.stack = new Stack<>(); - try { - this.filterGen = new FilterGenerator(); - } catch (IOException e) { - // ignore - } + this.filterGen = new FilterGenerator(); } public void addOrFilter(String filterKey, Collection filterValues) { @@ -76,7 +72,7 @@ public class ElasticsearchFilterGenerator implements Visitor { } } - public XContentBuilder getResult() throws IOException { + public JsonBuilder getResult() { return filterGen.getResult(); } diff --git a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/ElasticsearchQueryGenerator.java b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/ElasticsearchQueryGenerator.java index 13ce411..4cf4a12 100644 --- a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/ElasticsearchQueryGenerator.java +++ b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/ElasticsearchQueryGenerator.java @@ -1,7 +1,5 @@ package org.xbib.cql.elasticsearch; -import org.xbib.content.XContentBuilder; -import org.xbib.content.core.DefaultXContentBuilder; import org.xbib.cql.BooleanGroup; import org.xbib.cql.BooleanOperator; import org.xbib.cql.CQLParser; @@ -29,6 +27,7 @@ import org.xbib.cql.elasticsearch.ast.Operator; import org.xbib.cql.elasticsearch.ast.Token; import org.xbib.cql.elasticsearch.ast.TokenType; import org.xbib.cql.elasticsearch.model.ElasticsearchQueryModel; +import org.xbib.datastructures.json.tiny.JsonBuilder; import java.io.IOException; import java.time.ZonedDateTime; @@ -67,7 +66,7 @@ public class ElasticsearchQueryGenerator implements Visitor { private FacetsGenerator facetGen; - private XContentBuilder sort; + private SortGenerator sortGen; private final String globalField; @@ -82,6 +81,7 @@ public class ElasticsearchQueryGenerator implements Visitor { this.queryGen = new QueryGenerator(); this.filterGen = new FilterGenerator(); this.facetGen = new FacetsGenerator(); + this.sortGen = new SortGenerator(); } public ElasticsearchQueryModel getModel() { @@ -98,11 +98,6 @@ public class ElasticsearchQueryGenerator implements Visitor { return this; } - public ElasticsearchQueryGenerator setSort(XContentBuilder sort) { - this.sort = sort; - return this; - } - public ElasticsearchQueryGenerator setBoostParams(String boostField, String modifier, Float factor, String boostMode) { this.boostField = boostField; this.modifier = modifier; @@ -137,16 +132,16 @@ public class ElasticsearchQueryGenerator implements Visitor { return this; } - public String getQueryResult() throws IOException { - return queryGen.getResult().string(); + public String getQueryResult() { + return queryGen.getResult().build(); } - public String getFacetResult() throws IOException { - return facetGen.getResult().string(); + public String getFacetResult() { + return facetGen.getResult().build(); } - public String getSourceResult() throws IOException { - return sourceGen.getResult().string(); + public String getSourceResult() { + return sourceGen.getResult().build(); } @Override @@ -162,7 +157,7 @@ public class ElasticsearchQueryGenerator implements Visitor { } if (model.hasFilter()) { queryGen.startFiltered(); - } else if (filterGenerator.getResult().string().length() > 0) { + } else if (filterGenerator.getResult().build().length() > 0) { queryGen.startFiltered(); } Node querynode = stack.pop(); @@ -180,11 +175,10 @@ public class ElasticsearchQueryGenerator implements Visitor { filterGen.visit(model.getFilterExpression()); filterGen.endFilter(); queryGen.end(); - } else if (filterGenerator.getResult().string().length() > 0) { + } else if (filterGenerator.getResult().build().length() > 0) { queryGen.end(); - DefaultXContentBuilder contentBuilder = (DefaultXContentBuilder) filterGenerator.getResult(); - byte[] b = contentBuilder.bytes().toBytes(); - queryGen.getResult().rawField("filter", b, 0, b.length); + JsonBuilder contentBuilder = filterGenerator.getResult(); + queryGen.getResult(). copy(contentBuilder); queryGen.endFiltered(); } if (boostField != null) { @@ -195,15 +189,13 @@ public class ElasticsearchQueryGenerator implements Visitor { facetGen.visit(model.getFacetExpression()); } queryGen.end(); - Expression sortnode = model.getSort(); - SortGenerator sortGen = new SortGenerator(); - if (sortnode != null) { + if (model.getSort() != null) { + sortGen = new SortGenerator(); sortGen.start(); - sortGen.visit(sortnode); + sortGen.visit(model.getSort()); sortGen.end(); - sort = sortGen.getResult(); } - sourceGen.build(queryGen, from, size, sort, facetGen.getResult()); + sourceGen.build(queryGen, from, size, sortGen.getResult(), facetGen.getResult()); } catch (IOException e) { throw new SyntaxException("unable to build a valid query from " + node + " , reason: " + e.getMessage(), e); } @@ -326,7 +318,7 @@ public class ElasticsearchQueryGenerator implements Visitor { if (sb.length() > 0) { sb.append('.'); } - sb.append(modifier.toString()); + sb.append(modifier); modifier = stack.pop(); } String modifiable = sb.toString(); diff --git a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/FacetsGenerator.java b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/FacetsGenerator.java index 243b0bd..669d11d 100644 --- a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/FacetsGenerator.java +++ b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/FacetsGenerator.java @@ -1,13 +1,12 @@ package org.xbib.cql.elasticsearch; -import org.xbib.content.XContentBuilder; -import org.xbib.content.json.JsonXContent; import org.xbib.cql.SyntaxException; import org.xbib.cql.elasticsearch.ast.Expression; import org.xbib.cql.elasticsearch.ast.Modifier; import org.xbib.cql.elasticsearch.ast.Name; import org.xbib.cql.elasticsearch.ast.Operator; import org.xbib.cql.elasticsearch.ast.Token; +import org.xbib.datastructures.json.tiny.JsonBuilder; import java.io.IOException; import java.util.HashMap; @@ -20,36 +19,36 @@ public class FacetsGenerator implements Visitor { private int facetlength = 10; - private final XContentBuilder builder; + private final JsonBuilder builder; public FacetsGenerator() throws IOException { - this.builder = JsonXContent.contentBuilder(); + this.builder = new JsonBuilder(); } public void start() throws IOException { - builder.startObject(); + builder.beginMap(); } public void end() throws IOException { - builder.endObject(); + builder.endMap(); } public void startFacets() throws IOException { - builder.startObject("aggregations"); + builder.beginMap("aggregations"); } public void endFacets() throws IOException { - builder.endObject(); + builder.endMap(); } - public XContentBuilder getResult() throws IOException { + public JsonBuilder getResult() { return builder; } @Override public void visit(Token node) { try { - builder.value(node.toString().getBytes()); + builder.buildValue(node.toString()); } catch (IOException e) { throw new SyntaxException(e.getMessage(), e); } @@ -58,7 +57,7 @@ public class FacetsGenerator implements Visitor { @Override public void visit(Name node) { try { - builder.value(node.toString().getBytes()); + builder.buildValue(node.toString()); } catch (IOException e) { throw new SyntaxException(e.getMessage(), e); } @@ -67,7 +66,7 @@ public class FacetsGenerator implements Visitor { @Override public void visit(Modifier node) { try { - builder.value(node.toString().getBytes()); + builder.buildValue(node.toString()); } catch (IOException e) { throw new SyntaxException(e.getMessage(), e); } @@ -76,7 +75,7 @@ public class FacetsGenerator implements Visitor { @Override public void visit(Operator node) { try { - builder.value(node.toString().getBytes()); + builder.buildValue(node.toString()); } catch (IOException e) { throw new SyntaxException(e.getMessage(), e); } @@ -88,8 +87,9 @@ public class FacetsGenerator implements Visitor { Operator op = node.getOperator(); switch (op) { case TERMS_FACET: { - builder.startObject().field("myfacet", "myvalue") - .endObject(); + builder.beginMap() + .field("myfacet", "myvalue") + .endMap(); break; } default: @@ -122,7 +122,7 @@ public class FacetsGenerator implements Visitor { break; } } - builder.startObject(); + builder.beginMap(); for (String index : facetMap.keySet()) { if ("*".equals(index)) { continue; @@ -130,18 +130,19 @@ public class FacetsGenerator implements Visitor { // TODO(jprante) range aggregations etc. String facetType = "terms"; Integer size = facetMap.get(index); - builder.field(index) - .startObject() - .field(facetType).startObject() + builder.buildKey(index) + .beginMap() + .buildKey(facetType) + .beginMap() .field("field", index) .field("size", size > 0 ? size : 10) - .startObject("order") + .beginMap("order") .field(order, dir) - .endObject() - .endObject(); - builder.endObject(); + .endMap() + .endMap(); + builder.endMap(); } - builder.endObject(); + builder.endMap(); return this; } diff --git a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/FilterGenerator.java b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/FilterGenerator.java index f045ead..c6abe44 100644 --- a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/FilterGenerator.java +++ b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/FilterGenerator.java @@ -1,7 +1,5 @@ package org.xbib.cql.elasticsearch; -import org.xbib.content.XContentBuilder; -import org.xbib.content.json.JsonXContent; import org.xbib.cql.SyntaxException; import org.xbib.cql.elasticsearch.ast.Expression; import org.xbib.cql.elasticsearch.ast.Modifier; @@ -10,6 +8,7 @@ import org.xbib.cql.elasticsearch.ast.Node; import org.xbib.cql.elasticsearch.ast.Operator; import org.xbib.cql.elasticsearch.ast.Token; import org.xbib.cql.util.QuotedStringTokenizer; +import org.xbib.datastructures.json.tiny.JsonBuilder; import java.io.IOException; @@ -18,10 +17,10 @@ import java.io.IOException; */ public class FilterGenerator implements Visitor { - private XContentBuilder builder; + private final JsonBuilder builder; - public FilterGenerator() throws IOException { - this.builder = JsonXContent.contentBuilder(); + public FilterGenerator() { + this.builder = new JsonBuilder(); } public FilterGenerator(QueryGenerator queryGenerator) throws IOException { @@ -29,33 +28,33 @@ public class FilterGenerator implements Visitor { } public FilterGenerator start() throws IOException { - builder.startObject(); + builder.beginMap(); return this; } public FilterGenerator end() throws IOException { - builder.endObject(); + builder.endMap(); return this; } public FilterGenerator startFilter() throws IOException { - builder.startObject("filter"); + builder.beginMap("filter"); return this; } public FilterGenerator endFilter() throws IOException { - builder.endObject(); + builder.endMap(); return this; } - public XContentBuilder getResult() throws IOException { + public JsonBuilder getResult() { return builder; } @Override public void visit(Token node) { try { - builder.value(node.getString()); + builder.buildValue(node.getString()); } catch (IOException e) { throw new SyntaxException(e.getMessage(), e); } @@ -64,7 +63,7 @@ public class FilterGenerator implements Visitor { @Override public void visit(Name node) { try { - builder.field(node.toString()); + builder.buildKey(node.toString()); } catch (IOException e) { throw new SyntaxException(e.getMessage(), e); } @@ -73,7 +72,7 @@ public class FilterGenerator implements Visitor { @Override public void visit(Modifier node) { try { - builder.value(node.toString()); + builder.buildValue(node.toString()); } catch (IOException e) { throw new SyntaxException(e.getMessage(), e); } @@ -82,7 +81,7 @@ public class FilterGenerator implements Visitor { @Override public void visit(Operator node) { try { - builder.value(node.toString()); + builder.buildValue(node.toString()); } catch (IOException e) { throw new SyntaxException(e.getMessage(), e); } @@ -111,17 +110,18 @@ public class FilterGenerator implements Visitor { case EQUALS: { String field = arg1.toString(); String value = tok2 != null ? tok2.getString() : ""; - builder.startObject(tok2 != null && tok2.isBoundary() ? "prefix" : "term"); - builder.field(field, value).endObject(); + builder.beginMap(tok2 != null && tok2.isBoundary() ? "prefix" : "term"); + builder.field(field, value) + .endMap(); break; } case NOT_EQUALS: { String field = arg1.toString(); String value = tok2 != null ? tok2.getString() : ""; - builder.startObject("not") - .startObject(tok2 != null && tok2.isBoundary() ? "prefix" : "term") + builder.beginMap("not") + .beginMap(tok2 != null && tok2.isBoundary() ? "prefix" : "term") .field(field, value) - .endObject().endObject(); + .endMap().endMap(); break; } case ALL: { @@ -129,18 +129,18 @@ public class FilterGenerator implements Visitor { String value = arg2 != null ? arg2.toString() : ""; boolean phrase = arg2 instanceof Token && ((Token) arg2).isQuoted(); if (phrase) { - builder.startArray("and"); + builder.beginCollection("and"); QuotedStringTokenizer qst = new QuotedStringTokenizer(value); while (qst.hasMoreTokens()) { - builder.startObject().startObject("term") + builder.beginMap().beginMap("term") .field(field, qst.nextToken()) - .endObject().endObject(); + .endMap().endMap(); } - builder.endArray(); + builder.endCollection(); } else { - builder.startObject(tok2 != null && tok2.isBoundary() ? "prefix" : "term") + builder.beginMap(tok2 != null && tok2.isBoundary() ? "prefix" : "term") .field(field, value) - .endObject(); + .endMap(); } break; } @@ -149,66 +149,66 @@ public class FilterGenerator implements Visitor { String field = arg1.toString(); String value = arg2 != null ? arg2.toString() : ""; if (phrase) { - builder.startArray("or"); + builder.beginCollection("or"); QuotedStringTokenizer qst = new QuotedStringTokenizer(value); while (qst.hasMoreTokens()) { - builder.startObject().startObject("term") - .field(field, qst.nextToken()).endObject().endObject(); + builder.beginMap().beginMap("term") + .field(field, qst.nextToken()).endMap().endMap(); } - builder.endArray(); + builder.endCollection(); } else { - builder.startObject(tok2 != null && tok2.isBoundary() ? "prefix" : "term") + builder.beginMap(tok2 != null && tok2.isBoundary() ? "prefix" : "term") .field(field, value) - .endObject(); + .endMap(); } break; } case RANGE_GREATER_THAN: { String field = arg1.toString(); String value = tok2 != null ? tok2.getString() : ""; - builder.startObject("range").startObject(field) + builder.beginMap("range").beginMap(field) .field("from", value) .field("include_lower", false) - .endObject().endObject(); + .endMap().endMap(); break; } case RANGE_GREATER_OR_EQUAL: { String field = arg1.toString(); String value = arg2 != null ? arg2.toString() : ""; - builder.startObject("range").startObject(field) + builder.beginMap("range").beginMap(field) .field("from", value) .field("include_lower", true) - .endObject().endObject(); + .endMap().endMap(); break; } case RANGE_LESS_THAN: { String field = arg1.toString(); String value = arg2 != null ? arg2.toString() : ""; - builder.startObject("range").startObject(field) + builder.beginMap("range").beginMap(field) .field("to", value) .field("include_upper", false) - .endObject().endObject(); + .endMap().endMap(); break; } case RANGE_LESS_OR_EQUALS: { String field = arg1.toString(); String value = arg2 != null ? arg2.toString() : ""; - builder.startObject("range").startObject(field) + builder.beginMap("range").beginMap(field) .field("to", value) .field("include_upper", true) - .endObject().endObject(); + .endMap().endMap(); break; } case RANGE_WITHIN: { String field = arg1.toString(); String value = tok2 != null ? tok2.getString() : ""; String[] s = value.split(" "); - builder.startObject("range").startObject(field). + builder.beginMap("range").beginMap(field). field("from", s[0]) .field("to", s[1]) .field("include_lower", true) .field("include_upper", true) - .endObject().endObject(); + .endMap().endMap(); break; } case AND: { @@ -217,18 +217,18 @@ public class FilterGenerator implements Visitor { arg1.accept(this); } } else { - builder.startObject("bool"); - builder.startArray("must"); + builder.beginMap("bool"); + builder.beginCollection("must"); Node[] args = node.getArgs(); for (int i = 0; i < node.getArgs().length; i++) { if (args[i].isVisible()) { - builder.startObject(); + builder.beginMap(); args[i].accept(this); - builder.endObject(); + builder.endMap(); } } - builder.endArray(); - builder.endObject(); + builder.endCollection(); + builder.endMap(); } break; } @@ -238,51 +238,51 @@ public class FilterGenerator implements Visitor { arg1.accept(this); } } else { - builder.startObject("bool"); - builder.startArray("should"); + builder.beginMap("bool"); + builder.beginCollection("should"); Node[] args = node.getArgs(); for (int i = 0; i < node.getArgs().length; i++) { if (args[i].isVisible()) { - builder.startObject(); + builder.beginMap(); args[i].accept(this); - builder.endObject(); + builder.endMap(); } } - builder.endArray(); - builder.endObject(); + builder.endCollection(); + builder.endMap(); } break; } case OR_FILTER: { - builder.startObject("bool"); - builder.startArray("should"); + builder.beginMap("bool"); + builder.beginCollection("should"); Node[] args = node.getArgs(); for (int i = 0; i < args.length; i += 2) { if (args[i].isVisible()) { - builder.startObject().startObject("term"); + builder.beginMap().beginMap("term"); args[i].accept(this); args[i + 1].accept(this); - builder.endObject().endObject(); + builder.endMap().endMap(); } } - builder.endArray(); - builder.endObject(); + builder.endCollection(); + builder.endMap(); break; } case AND_FILTER: { - builder.startObject("bool"); - builder.startArray("must"); + builder.beginMap("bool"); + builder.beginCollection("must"); Node[] args = node.getArgs(); for (int i = 0; i < args.length; i += 2) { if (args[i].isVisible()) { - builder.startObject().startObject("term"); + builder.beginMap().beginMap("term"); args[i].accept(this); args[i + 1].accept(this); - builder.endObject().endObject(); + builder.endMap().endMap(); } } - builder.endArray(); - builder.endObject(); + builder.endCollection(); + builder.endMap(); break; } case ANDNOT: { @@ -291,18 +291,18 @@ public class FilterGenerator implements Visitor { arg1.accept(this); } } else { - builder.startObject("bool"); - builder.startArray("must_not"); + builder.beginMap("bool"); + builder.beginCollection("must_not"); Node[] args = node.getArgs(); for (int i = 0; i < node.getArgs().length; i++) { if (args[i].isVisible()) { - builder.startObject(); + builder.beginMap(); args[i].accept(this); - builder.endObject(); + builder.endMap(); } } - builder.endArray(); - builder.endObject(); + builder.endCollection(); + builder.endMap(); } break; } @@ -310,19 +310,19 @@ public class FilterGenerator implements Visitor { String field = arg1.toString(); // we assume a default of 10 words is enough for proximity String value = arg2 != null ? arg2.toString() + "~10" : ""; - builder.startObject("field").field(field, value).endObject(); + builder.beginMap("field").field(field, value).endMap(); break; } case TERM_FILTER: { String field = arg1.toString(); String value = arg2 != null ? arg2.toString() : ""; - builder.startObject("term").field(field, value).endObject(); + builder.beginMap("term").field(field, value).endMap(); break; } case QUERY_FILTER: { - builder.startObject("query"); + builder.beginMap("query"); arg1.accept(this); - builder.endObject(); + builder.endMap(); break; } default: diff --git a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/QueryGenerator.java b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/QueryGenerator.java index 3e3e6c2..6a108ac 100644 --- a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/QueryGenerator.java +++ b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/QueryGenerator.java @@ -1,7 +1,5 @@ package org.xbib.cql.elasticsearch; -import org.xbib.content.XContentBuilder; -import org.xbib.content.json.JsonXContent; import org.xbib.cql.SyntaxException; import org.xbib.cql.elasticsearch.ast.Expression; import org.xbib.cql.elasticsearch.ast.Modifier; @@ -9,53 +7,54 @@ import org.xbib.cql.elasticsearch.ast.Name; import org.xbib.cql.elasticsearch.ast.Node; import org.xbib.cql.elasticsearch.ast.Operator; import org.xbib.cql.elasticsearch.ast.Token; +import org.xbib.datastructures.json.tiny.JsonBuilder; import java.io.IOException; - +import java.util.Collections; /** * Build Elasticsearch query from abstract syntax tree. */ public class QueryGenerator implements Visitor { - private final XContentBuilder builder; + private final JsonBuilder builder; - public QueryGenerator() throws IOException { - this.builder = JsonXContent.contentBuilder(); + public QueryGenerator() { + this.builder = new JsonBuilder(); } public void start() throws IOException { - builder.startObject(); + builder.beginMap(); } public void end() throws IOException { - builder.endObject(); + builder.endMap(); } public void startFiltered() throws IOException { - builder.startObject("filtered").startObject("query"); + builder.beginMap("filtered").beginMap("query"); } public void endFiltered() throws IOException { - builder.endObject(); + builder.endMap(); } public void startBoost(String boostField, String modifier, Float factor, String boostMode) throws IOException { - builder.startObject("function_score") - .startObject("field_value_factor") + builder.beginMap("function_score") + .beginMap("field_value_factor") .field("field", boostField) .field("modifier", modifier != null ? modifier : "log1p") .field("factor", factor != null ? factor : 1.0f) - .endObject() + .endMap() .field("boost_mode", boostMode != null ? boostMode : "multiply") - .startObject("query"); + .beginMap("query"); } public void endBoost() throws IOException { - builder.endObject().endObject(); + builder.endMap().endMap(); } - public XContentBuilder getResult() { + public JsonBuilder getResult() { return builder; } @@ -64,19 +63,19 @@ public class QueryGenerator implements Visitor { try { switch (token.getType()) { case BOOL: - builder.value(token.getBoolean()); + builder.buildValue(token.getBoolean()); break; case INT: - builder.value(token.getInteger()); + builder.buildValue(token.getInteger()); break; case FLOAT: - builder.value(token.getFloat()); + builder.buildValue(token.getFloat()); break; case DATETIME: - builder.value(token.getDate()); + builder.buildValue(token.getDate()); break; case STRING: - builder.value(token.getString()); + builder.buildValue(token.getString()); break; default: throw new IOException("unknown token type: " + token); @@ -89,7 +88,7 @@ public class QueryGenerator implements Visitor { @Override public void visit(Name node) { try { - builder.field(node.toString()); + builder.buildKey(node.toString()); } catch (IOException e) { throw new SyntaxException(e.getMessage(), e); } @@ -98,7 +97,7 @@ public class QueryGenerator implements Visitor { @Override public void visit(Modifier node) { try { - builder.value(node.toString()); + builder.buildValue(node.toString()); } catch (IOException e) { throw new SyntaxException(e.getMessage(), e); } @@ -107,7 +106,7 @@ public class QueryGenerator implements Visitor { @Override public void visit(Operator node) { try { - builder.value(node.toString()); + builder.buildValue(node.toString()); } catch (IOException e) { throw new SyntaxException(e.getMessage(), e); } @@ -123,7 +122,7 @@ public class QueryGenerator implements Visitor { switch (op.getArity()) { case 0: { if (op == Operator.MATCH_ALL) { - builder.startObject("match_all").endObject(); + builder.beginMap("match_all").endMap(); } break; } @@ -148,91 +147,91 @@ public class QueryGenerator implements Visitor { String field = arg1.toString(); String value = arg2 != null ? arg2.toString() : ""; // with quote // with phrase boost - builder.startObject("bool") - .startArray("should") - .startObject() - .startObject("simple_query_string") + builder.beginMap("bool") + .beginCollection("should") + .beginMap() + .beginMap("simple_query_string") .field("query", value) - .field("fields", new String[]{field}) + .field("fields", Collections.singletonList(field)) .field("analyze_wildcard", true) .field("default_operator", "and") - .endObject() - .endObject() - .startObject() - .startObject("simple_query_string") + .endMap() + .endMap() + .beginMap() + .beginMap("simple_query_string") .field("query", "\"" + value + "\"") - .field("fields", new String[]{ field + "^2"}) + .field("fields", Collections.singletonList(field + "^2")) .field("default_operator", "and") - .endObject() - .endObject() - .endArray() + .endMap() + .endMap() + .endCollection() .field("minimum_should_match", "1") - .endObject(); + .endMap(); break; } case NOT_EQUALS: { String field = arg1.toString(); String value = arg2 != null ? arg2.toString() : ""; // with quote - builder.startObject("bool") - .startObject("must_not") - .startObject("simple_query_string") + builder.beginMap("bool") + .beginMap("must_not") + .beginMap("simple_query_string") .field("query", value) - .field("fields", new String[]{field}) + .field("fields", Collections.singletonList(field)) .field("analyze_wildcard", true) .field("default_operator", "and") - .endObject() - .endObject() - .endObject(); + .endMap() + .endMap() + .endMap(); break; } case ALL: { String field = arg1.toString(); String value = tok2 != null ? tok2.getString() : ""; // always unquoted // with phrase boost - builder.startObject("bool") - .startArray("should") - .startObject() - .startObject("simple_query_string") + builder.beginMap("bool") + .beginCollection("should") + .beginMap() + .beginMap("simple_query_string") .field("query", value) - .field("fields", new String[]{field}) + .field("fields", Collections.singletonList(field)) .field("analyze_wildcard", true) .field("default_operator", "and") - .endObject() - .endObject() - .startObject() - .startObject("simple_query_string") + .endMap() + .endMap() + .beginMap() + .beginMap("simple_query_string") .field("query", "\"" + value + "\"") - .field("fields", new String[]{ field + "^2"}) + .field("fields", Collections.singletonList(field + "^2")) .field("default_operator", "and") - .endObject() - .endObject() - .endArray() + .endMap() + .endMap() + .endCollection() .field("minimum_should_match", "1") - .endObject(); + .endMap(); break; } case ANY: { String field = arg1.toString(); String value = tok2 != null ? tok2.getString() : ""; // always unquoted // with phrase boost - builder.startObject("bool") - .startArray("should") - .startObject() - .startObject("simple_query_string") + builder.beginMap("bool") + .beginCollection("should") + .beginMap() + .beginMap("simple_query_string") .field("query", value) - .field("fields", new String[]{field}) + .field("fields", Collections.singletonList(field)) .field("analyze_wildcard", true) - .endObject() - .endObject() - .startObject() - .startObject("simple_query_string") + .endMap() + .endMap() + .beginMap() + .beginMap("simple_query_string") .field("query", "\"" + value + "\"") - .field("fields", new String[]{ field + "^2"}) - .endObject() - .endObject() - .endArray() + .field("fields", Collections.singletonList(field + "^2")) + .endMap() + .endMap() + .endCollection() .field("minimum_should_match", "1") - .endObject(); + .endMap(); break; } case PHRASE: { @@ -240,18 +239,18 @@ public class QueryGenerator implements Visitor { String field = arg1.toString(); String value = tok2.isQuoted() ? tok2.getString() : arg2.toString(); if (tok2.isAll()) { - builder.startObject("match_all").endObject(); + builder.beginMap("match_all").endMap(); } else if (tok2.isWildcard()) { - builder.startObject("wildcard").field(field, value).endObject(); + builder.beginMap("wildcard").field(field, value).endMap(); } else if (tok2.isBoundary()) { - builder.startObject("prefix").field(field, value).endObject(); + builder.beginMap("prefix").field(field, value).endMap(); } else { - builder.startObject("simple_query_string") + builder.beginMap("simple_query_string") .field("query", value) - .field("fields", new String[]{field}) + .field("fields",Collections.singletonList(field)) .field("analyze_wildcard", true) .field("default_operator", "and") - .endObject(); + .endMap(); } } break; @@ -259,37 +258,37 @@ public class QueryGenerator implements Visitor { case RANGE_GREATER_THAN: { String field = arg1.toString(); String value = arg2 != null ? arg2.toString() : ""; - builder.startObject("range").startObject(field) + builder.beginMap("range").beginMap(field) .field("from", value) .field("include_lower", false) - .endObject().endObject(); + .endMap().endMap(); break; } case RANGE_GREATER_OR_EQUAL: { String field = arg1.toString(); String value = arg2 != null ? arg2.toString() : ""; - builder.startObject("range").startObject(field) + builder.beginMap("range").beginMap(field) .field("from", value) .field("include_lower", true) - .endObject().endObject(); + .endMap().endMap(); break; } case RANGE_LESS_THAN: { String field = arg1.toString(); String value = arg2 != null ? arg2.toString() : ""; - builder.startObject("range").startObject(field) + builder.beginMap("range").beginMap(field) .field("to", value) .field("include_upper", false) - .endObject().endObject(); + .endMap().endMap(); break; } case RANGE_LESS_OR_EQUALS: { String field = arg1.toString(); String value = arg2 != null ? arg2.toString() : ""; - builder.startObject("range").startObject(field) + builder.beginMap("range").beginMap(field) .field("to", value) .field("include_upper", true) - .endObject().endObject(); + .endMap().endMap(); break; } case RANGE_WITHIN: { @@ -310,12 +309,12 @@ public class QueryGenerator implements Visitor { from = tok2.getStringList().get(0); to = tok2.getStringList().get(1); } - builder.startObject("range").startObject(field) + builder.beginMap("range").beginMap(field) .field("from", from) .field("to", to) .field("include_lower", true) .field("include_upper", true) - .endObject().endObject(); + .endMap().endMap(); break; } case AND: { @@ -324,23 +323,23 @@ public class QueryGenerator implements Visitor { arg1.accept(this); } } else { - builder.startObject("bool"); + builder.beginMap("bool"); if (arg1.isVisible() && arg2.isVisible()) { - builder.startArray("must").startObject(); + builder.beginCollection("must").beginMap(); arg1.accept(this); - builder.endObject().startObject(); + builder.endMap().beginMap(); arg2.accept(this); - builder.endObject().endArray(); + builder.endMap().endCollection(); } else if (arg1.isVisible()) { - builder.startObject("must"); + builder.beginMap("must"); arg1.accept(this); - builder.endObject(); + builder.endMap(); } else if (arg2.isVisible()) { - builder.startObject("must"); + builder.beginMap("must"); arg2.accept(this); - builder.endObject(); + builder.endMap(); } - builder.endObject(); + builder.endMap(); } break; } @@ -351,23 +350,23 @@ public class QueryGenerator implements Visitor { arg1.accept(this); } } else { - builder.startObject("bool"); + builder.beginMap("bool"); if (arg1.isVisible() && arg2.isVisible()) { - builder.startArray("should").startObject(); + builder.beginCollection("should").beginMap(); arg1.accept(this); - builder.endObject().startObject(); + builder.endMap().beginMap(); arg2.accept(this); - builder.endObject().endArray(); + builder.endMap().endCollection(); } else if (arg1.isVisible()) { - builder.startObject("should"); + builder.beginMap("should"); arg1.accept(this); - builder.endObject(); + builder.endMap(); } else if (arg2.isVisible()) { - builder.startObject("should"); + builder.beginMap("should"); arg2.accept(this); - builder.endObject(); + builder.endMap(); } - builder.endObject(); + builder.endMap(); } break; } @@ -377,23 +376,23 @@ public class QueryGenerator implements Visitor { arg1.accept(this); } } else { - builder.startObject("bool"); + builder.beginMap("bool"); if (arg1.isVisible() && arg2.isVisible()) { - builder.startArray("must_not").startObject(); + builder.beginCollection("must_not").beginMap(); arg1.accept(this); - builder.endObject().startObject(); + builder.endMap().beginMap(); arg2.accept(this); - builder.endObject().endArray(); + builder.endMap().endCollection(); } else if (arg1.isVisible()) { - builder.startObject("must_not"); + builder.beginMap("must_not"); arg1.accept(this); - builder.endObject(); + builder.endMap(); } else if (arg2.isVisible()) { - builder.startObject("must_not"); + builder.beginMap("must_not"); arg2.accept(this); - builder.endObject(); + builder.endMap(); } - builder.endObject(); + builder.endMap(); } break; } @@ -401,7 +400,7 @@ public class QueryGenerator implements Visitor { String field = arg1.toString(); // we assume a default of 10 words is enough for proximity String value = arg2 != null ? arg2 + "~10" : ""; - builder.startObject("field").field(field, value).endObject(); + builder.beginMap("field").field(field, value).endMap(); break; } default: diff --git a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/SortGenerator.java b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/SortGenerator.java index 284d523..ffa8613 100644 --- a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/SortGenerator.java +++ b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/SortGenerator.java @@ -1,7 +1,5 @@ package org.xbib.cql.elasticsearch; -import org.xbib.content.XContentBuilder; -import org.xbib.content.json.JsonXContent; import org.xbib.cql.SyntaxException; import org.xbib.cql.elasticsearch.ast.Expression; import org.xbib.cql.elasticsearch.ast.Modifier; @@ -9,6 +7,7 @@ import org.xbib.cql.elasticsearch.ast.Name; import org.xbib.cql.elasticsearch.ast.Node; import org.xbib.cql.elasticsearch.ast.Operator; import org.xbib.cql.elasticsearch.ast.Token; +import org.xbib.datastructures.json.tiny.JsonBuilder; import java.io.IOException; import java.util.Stack; @@ -18,24 +17,24 @@ import java.util.Stack; */ public class SortGenerator implements Visitor { - private final XContentBuilder builder; + private final JsonBuilder builder; private final Stack modifiers; - public SortGenerator() throws IOException { - this.builder = JsonXContent.contentBuilder(); + public SortGenerator() { + this.builder = new JsonBuilder(); this.modifiers = new Stack<>(); } public void start() throws IOException { - builder.startArray(); + builder.beginCollection(); } public void end() throws IOException { - builder.endArray(); + builder.endCollection(); } - public XContentBuilder getResult() { + public JsonBuilder getResult() { return builder; } @@ -47,15 +46,15 @@ public class SortGenerator implements Visitor { public void visit(Name node) { try { if (modifiers.isEmpty()) { - builder.startObject() - .field(node.getName()) - .startObject() + builder.beginMap() + .buildKey(node.getName()) + .beginMap() .field("unmapped_type", "string") .field("missing", "_last") - .endObject() - .endObject(); + .endMap() + .endMap(); } else { - builder.startObject().field(node.getName()).startObject(); + builder.beginMap().buildKey(node.getName()).beginMap(); while (!modifiers.isEmpty()) { Modifier mod = modifiers.pop(); String s = mod.getName().toString(); @@ -71,15 +70,15 @@ public class SortGenerator implements Visitor { break; } default: { - builder.field(s, mod.getTerm()); + builder.field(s, mod.getTerm().toString()); break; } } } builder.field("unmapped_type", "string"); builder.field("missing", "_last"); - builder.endObject(); - builder.endObject(); + builder.endMap(); + builder.endMap(); } } catch (IOException e) { throw new SyntaxException(e.getMessage(), e); diff --git a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/SourceGenerator.java b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/SourceGenerator.java index 2151bdc..7518cfc 100644 --- a/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/SourceGenerator.java +++ b/cql-elasticsearch/src/main/java/org/xbib/cql/elasticsearch/SourceGenerator.java @@ -1,17 +1,15 @@ package org.xbib.cql.elasticsearch; -import org.xbib.content.XContentBuilder; -import org.xbib.content.core.DefaultXContentBuilder; -import org.xbib.content.json.JsonXContent; +import org.xbib.datastructures.json.tiny.JsonBuilder; import java.io.IOException; public class SourceGenerator { - private final XContentBuilder builder; + private final JsonBuilder builder; public SourceGenerator() throws IOException { - this.builder = JsonXContent.contentBuilder(); + this.builder = new JsonBuilder(); } public void build(QueryGenerator query) throws IOException { @@ -23,8 +21,8 @@ public class SourceGenerator { } public void build(QueryGenerator query, Integer from, Integer size, - XContentBuilder sort, XContentBuilder facets) throws IOException { - builder.startObject(); + JsonBuilder sort, JsonBuilder facets) throws IOException { + builder.beginMap(); if (query != null) { if (from != null) { builder.field("from", from); @@ -32,25 +30,23 @@ public class SourceGenerator { if (size != null) { builder.field("size", size); } - copy(builder, "query", query.getResult()); - copy(builder, "sort", sort); - copy(builder, "aggregations", facets); - } - builder.endObject(); - builder.close(); - } - - public XContentBuilder getResult() { - return builder; - } - - private void copy(XContentBuilder builder, String rawFieldName, XContentBuilder anotherBuilder) throws IOException { - if (anotherBuilder != null) { - DefaultXContentBuilder contentBuilder = (DefaultXContentBuilder) anotherBuilder; - if (contentBuilder.bytes().length() > 0) { - byte[] b = contentBuilder.bytes().toBytes(); - builder.rawField(rawFieldName, b, 0, b.length); + if (query.getResult() != null) { + builder.buildKey("query"); + builder.copy(query.getResult()); + } + if (sort != null && sort.build().length() > 0) { + builder.buildKey("sort"); + builder.copy(sort); + } + if (facets != null && facets.build().length() > 0) { + builder.buildKey("aggregations"); + builder.copy(facets); } } + builder.endMap(); + } + + public JsonBuilder getResult() { + return builder; } } diff --git a/cql-elasticsearch/src/test/java/org/xbib/cql/elasticsearch/ElasticsearchQueryTest.java b/cql-elasticsearch/src/test/java/org/xbib/cql/elasticsearch/ElasticsearchQueryTest.java index fed6859..c57bb04 100644 --- a/cql-elasticsearch/src/test/java/org/xbib/cql/elasticsearch/ElasticsearchQueryTest.java +++ b/cql-elasticsearch/src/test/java/org/xbib/cql/elasticsearch/ElasticsearchQueryTest.java @@ -7,12 +7,11 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.nio.charset.StandardCharsets; +import java.util.logging.Level; +import java.util.logging.Logger; import static org.junit.jupiter.api.Assertions.assertEquals; -/** - * - */ class ElasticsearchQueryTest { @Test @@ -27,7 +26,7 @@ class ElasticsearchQueryTest { parser.parse(); ElasticsearchFilterGenerator generator = new ElasticsearchFilterGenerator("cql.allIndexes"); parser.getCQLQuery().accept(generator); - String json = generator.getResult().string(); + String json = generator.getResult().build(); assertEquals(json, "{\"term\":{\"cql.allIndexes\":\"Jörg\"}}"); } @@ -38,7 +37,7 @@ class ElasticsearchQueryTest { parser.parse(); ElasticsearchFilterGenerator generator = new ElasticsearchFilterGenerator("cql.allIndexes"); parser.getCQLQuery().accept(generator); - String json = generator.getResult().string(); + String json = generator.getResult().build(); assertEquals(json, "{\"query\":{\"term\":{\"dc.type\":\"electronic\"}}}"); } @@ -49,7 +48,7 @@ class ElasticsearchQueryTest { parser.parse(); ElasticsearchFilterGenerator generator = new ElasticsearchFilterGenerator("cql.allIndexes"); parser.getCQLQuery().accept(generator); - String json = generator.getResult().string(); + String json = generator.getResult().build(); assertEquals( "{\"query\":{\"bool\":{\"must\":[{\"term\":{\"dc.type\":\"electronic\"}},{\"term\":{\"dc.date\":\"2013\"}}]}}}", json @@ -63,7 +62,7 @@ class ElasticsearchQueryTest { parser.parse(); ElasticsearchFilterGenerator generator = new ElasticsearchFilterGenerator("cql.allIndexes"); parser.getCQLQuery().accept(generator); - String json = generator.getResult().string(); + String json = generator.getResult().build(); assertEquals( "{\"query\":{\"bool\":{\"must\":[{\"bool\":{\"must\":[{\"term\":{\"dc.format\":\"online\"}}," + "{\"term\":{\"dc.type\":\"electronic\"}}]}},{\"term\":{\"dc.date\":\"2013\"}}]}}}", @@ -112,6 +111,8 @@ class ElasticsearchQueryTest { } } catch (Exception e) { errors++; + Logger.getAnonymousLogger().log(Level.SEVERE, line); + Logger.getAnonymousLogger().log(Level.SEVERE, e.getMessage(), e); } count++; } diff --git a/gradle.properties b/gradle.properties index 6f18578..e697720 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ group = org.xbib name = cql -version = 3.1.4 +version = 4.0.0 gradle.wrapper.version = 6.6.1 -xbib-content.version = 3.0.0 +xbib-datastructures.version = 1.0.0