From e2ed2d5abd8065ae91d9e3d8b6cecbc4bdf14fda Mon Sep 17 00:00:00 2001 From: triplem <160079+triplem@users.noreply.github.com> Date: Wed, 9 Oct 2019 20:50:56 +0200 Subject: [PATCH] fix test, add first naive draft for mangoquery (helun/#286) --- src/main/java/org/ektorp/Status.java | 2 +- .../org/ektorp/impl/StdCouchDbConnector.java | 2 +- .../org/ektorp/mongoquery/Expression.java | 36 +++++++++ .../org/ektorp/mongoquery/MangoQuery.java | 74 +++++++++++++++++++ .../ektorp/mongoquery/MangoQueryBuilder.java | 52 +++++++++++++ .../java/org/ektorp/mongoquery/Operator.java | 68 +++++++++++++++++ src/main/java/org/ektorp/mongoquery/Sort.java | 43 +++++++++++ .../support/StdDesignDocumentFactory.java | 2 +- .../org/ektorp/impl/StdCouchDbInstanceIT.java | 69 ++++++++++++++--- .../org/ektorp/mongoquery/FormatTest.java | 60 +++++++++++++++ 10 files changed, 393 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/ektorp/mongoquery/Expression.java create mode 100644 src/main/java/org/ektorp/mongoquery/MangoQuery.java create mode 100644 src/main/java/org/ektorp/mongoquery/MangoQueryBuilder.java create mode 100644 src/main/java/org/ektorp/mongoquery/Operator.java create mode 100644 src/main/java/org/ektorp/mongoquery/Sort.java create mode 100644 src/test/java/org/ektorp/mongoquery/FormatTest.java diff --git a/src/main/java/org/ektorp/Status.java b/src/main/java/org/ektorp/Status.java index 7c8db1e9..225748e5 100644 --- a/src/main/java/org/ektorp/Status.java +++ b/src/main/java/org/ektorp/Status.java @@ -24,7 +24,7 @@ public boolean isOk() { private Map unknown() { if (unknownFields == null) { - unknownFields = new HashMap(); + unknownFields = new HashMap<>(); } return unknownFields; } diff --git a/src/main/java/org/ektorp/impl/StdCouchDbConnector.java b/src/main/java/org/ektorp/impl/StdCouchDbConnector.java index be72c3e9..73bc7080 100644 --- a/src/main/java/org/ektorp/impl/StdCouchDbConnector.java +++ b/src/main/java/org/ektorp/impl/StdCouchDbConnector.java @@ -475,7 +475,7 @@ public Page queryForPage(ViewQuery query, PageRequest pr, Class type) query.dbPath(dbURI.toString()); LOG.debug("startKey: {}", pr.getStartKey()); LOG.debug("startDocId: {}", pr.getStartKeyDocId()); - PageResponseHandler ph = new PageResponseHandler(pr, type, objectMapper, + PageResponseHandler ph = new PageResponseHandler<>(pr, type, objectMapper, query.isIgnoreNotFound()); query = PageRequest.applyPagingParameters(query, pr); diff --git a/src/main/java/org/ektorp/mongoquery/Expression.java b/src/main/java/org/ektorp/mongoquery/Expression.java new file mode 100644 index 00000000..7d81e735 --- /dev/null +++ b/src/main/java/org/ektorp/mongoquery/Expression.java @@ -0,0 +1,36 @@ +package org.ektorp.mongoquery; + +import com.fasterxml.jackson.annotation.JsonValue; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +public class Expression implements Serializable { + + private String left; + private Object right; + + public String getLeft() { + return left; + } + + public void setLeft(String left) { + this.left = left; + } + + public Object getRight() { + return right; + } + + public void setRight(Object right) { + this.right = right; + } + + @JsonValue + public Map toJson() { + Map result = new HashMap<>(); + result.put(getLeft(), getRight()); + return result; + } +} diff --git a/src/main/java/org/ektorp/mongoquery/MangoQuery.java b/src/main/java/org/ektorp/mongoquery/MangoQuery.java new file mode 100644 index 00000000..78ec7c8d --- /dev/null +++ b/src/main/java/org/ektorp/mongoquery/MangoQuery.java @@ -0,0 +1,74 @@ +package org.ektorp.mongoquery; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; + +@JsonInclude(Include.NON_NULL) +public class MangoQuery { + + public enum Stale { + OK("ok"), + FALSE("false"); + + private String jsonName; + + Stale(String jsonName) { + this.jsonName = jsonName; + } + + public String getJsonName() { + return jsonName; + } + } + + // object describing criteria used to select documents. + private Expression selector; + + // Maximum number of results returned. Default is 25. Optional + private Integer limit; + + // Skip the first ‘n’ results, where ‘n’ is the value specified. Optional + private Integer skip; + + // contains a list of field name and direction pairs + private List sort; + + // specifying which fields of each object should be returned. If it is omitted, the entire + // object is returned. + private List fields; + + // Instruct a query to use a specific index. + @JsonProperty("use_index") + private String useIndex; + + // Read quorum needed for the result. This defaults to 1, in which case the document found in + // the index is returned. If set to a higher value, each document is read from at least that + // many replicas before it is returned in the results. This is likely to take more time than + // using only the document stored locally with the index. + @JsonProperty("r") + private Integer readQuorum; + + // A string that enables you to specify which page of results you require. Used for paging + // through result sets. Every query returns an opaque string under the bookmark key that can + // then be passed back in a query to get the next page of results. If any part of the selector + // query changes between requests, the results are undefined. + private String bookmark; + + // Whether to update the index prior to returning the result. Default is true. + private Boolean update; + + // Whether or not the view results should be returned from a “stable” set of shards + private Boolean stable; + + // Combination of update=false and stable=true options. (Default: false) + private Stale stale; + + // Include execution statistics in the query response. (Default: false) + @JsonProperty("execution_stats") + private Boolean executionStats; + + + +} diff --git a/src/main/java/org/ektorp/mongoquery/MangoQueryBuilder.java b/src/main/java/org/ektorp/mongoquery/MangoQueryBuilder.java new file mode 100644 index 00000000..11e08451 --- /dev/null +++ b/src/main/java/org/ektorp/mongoquery/MangoQueryBuilder.java @@ -0,0 +1,52 @@ +package org.ektorp.mongoquery; + +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class MangoQueryBuilder { + + private Expression expression; + + public MangoQueryBuilder or(Operator... operators) { + return combinationOperators("$or", operators); + } + + public MangoQueryBuilder and(Operator... operators) { + return combinationOperators("$and", operators); + } + + private MangoQueryBuilder combinationOperators(String oper, Operator... operators) { + expression = new Expression(); + expression.setLeft(oper); + + if (operators.length > 1) { + List expressions = new LinkedList<>(); + for (Operator operator : operators) { + Expression e = new Expression(); + e.setLeft(operator.getFieldName()); + e.setRight(operator.getExpression()); + + expressions.add(e); + } + expression.setRight(expressions); + } else if (operators.length == 1){ + Expression e = new Expression(); + e.setLeft(operators[0].getFieldName()); + e.setRight(operators[0].getExpression()); + + expression = e; + } + + return this; + } + + @JsonValue + public Map toJson() { + return this.expression.toJson(); + } + + +} diff --git a/src/main/java/org/ektorp/mongoquery/Operator.java b/src/main/java/org/ektorp/mongoquery/Operator.java new file mode 100644 index 00000000..1d1f45a3 --- /dev/null +++ b/src/main/java/org/ektorp/mongoquery/Operator.java @@ -0,0 +1,68 @@ +package org.ektorp.mongoquery; + +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.HashMap; +import java.util.Map; + +public class Operator { + + private String fieldName; + private Expression expression; + + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + @JsonValue + public Map toJson() { + Map outerMap = new HashMap<>(); + outerMap.put(this.fieldName, this.expression); + return outerMap; + } + + public static class SimpleCompareOperator extends Operator { + + private String comparator; + private Object value; + + public SimpleCompareOperator(String fieldName, String comparator, Object value) { + this.setFieldName(fieldName); + Expression exp = new Expression(); + exp.setLeft(comparator); + exp.setRight(value); + this.setExpression(exp); + } + } + + public static class EqualOperator extends SimpleCompareOperator { + + private static final String COMPARATOR = "$eq"; + + public EqualOperator(String fieldName, Object value) { + super(fieldName, COMPARATOR, value); + } + } + + public static class ExistsOperator extends SimpleCompareOperator { + + private static final String COMPARATOR = "$in"; + + public ExistsOperator(String fieldName, Boolean value) { + super(fieldName, COMPARATOR, value); + } + } + + + public String getFieldName() { + return fieldName; + } + + public Expression getExpression() { + return expression; + } +} diff --git a/src/main/java/org/ektorp/mongoquery/Sort.java b/src/main/java/org/ektorp/mongoquery/Sort.java new file mode 100644 index 00000000..09f464f3 --- /dev/null +++ b/src/main/java/org/ektorp/mongoquery/Sort.java @@ -0,0 +1,43 @@ +package org.ektorp.mongoquery; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +public class Sort implements Serializable { + + public enum SortOrder { + DESC("desc"), + ASC("asc"); + + private String jsonName; + + SortOrder(String jsonName) { + this.jsonName = jsonName; + } + + public String getJsonName() { + return this.jsonName; + } + } + + private String fieldName; + + private SortOrder sortOrder; + + public Sort(String fieldName, SortOrder sortOrder) { + this.fieldName = fieldName; + this.sortOrder = sortOrder; + } + + public Sort(String fieldName) { + this(fieldName, SortOrder.ASC); + } + + public Map toJson() { + Map result = new HashMap<>(); + result.put(fieldName, sortOrder.getJsonName()); + return result; + } + +} diff --git a/src/main/java/org/ektorp/support/StdDesignDocumentFactory.java b/src/main/java/org/ektorp/support/StdDesignDocumentFactory.java index aed69986..f6f6aac4 100644 --- a/src/main/java/org/ektorp/support/StdDesignDocumentFactory.java +++ b/src/main/java/org/ektorp/support/StdDesignDocumentFactory.java @@ -121,7 +121,7 @@ public boolean apply(UpdateHandlers input) { } private Map createShowFunctions(final Class metaDataClass) { - final Map shows = new HashMap(); + final Map shows = new HashMap<>(); ReflectionUtils .eachAnnotation(metaDataClass, ShowFunction.class, new Predicate() { diff --git a/src/test/java/org/ektorp/impl/StdCouchDbInstanceIT.java b/src/test/java/org/ektorp/impl/StdCouchDbInstanceIT.java index 3887e5a3..fbedaf0f 100644 --- a/src/test/java/org/ektorp/impl/StdCouchDbInstanceIT.java +++ b/src/test/java/org/ektorp/impl/StdCouchDbInstanceIT.java @@ -1,16 +1,26 @@ package org.ektorp.impl; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import java.io.FileInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Scanner; +import java.util.stream.Stream; +import jdk.internal.util.xml.impl.Input; +import org.ektorp.AttachmentInputStream; import org.ektorp.CouchDbConnector; import org.ektorp.CouchDbContainer; import org.ektorp.CouchDbInstance; import org.ektorp.DocumentNotFoundException; +import org.ektorp.Options; import org.ektorp.http.HttpClient; import org.ektorp.http.StdHttpClient; -import org.ektorp.impl.MembershipInfo; -import org.ektorp.impl.StdCouchDbConnector; -import org.ektorp.impl.StdCouchDbInstance; +import org.ektorp.support.CouchDbDocument; import org.junit.ClassRule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; @@ -32,15 +42,50 @@ public void testDescribeCluster() throws Exception { connector.createDatabaseIfNotExists(); - try { - MembershipInfo info = dbInstance.describeCluster(); - assertEquals(1, info.getAllNodes().size()); - assertEquals("nonode@nodhost", info.getAllNodes().get(0)); - assertEquals(1, info.getClusterNodes().size()); - assertEquals("nonode@nodhost", info.getClusterNodes().get(0)); - } catch (DocumentNotFoundException e) { - e.printStackTrace(); - } + MembershipInfo info = dbInstance.describeCluster(); + assertEquals(1, info.getAllNodes().size()); + assertEquals("nonode@nodhost", info.getAllNodes().get(0)); + assertEquals(1, info.getClusterNodes().size()); + assertEquals("nonode@nodhost", info.getClusterNodes().get(0)); } + @Test + public void testCreateAttachement() throws Exception { + HttpClient httpClient = new StdHttpClient.Builder() + .url(((CouchDbContainer)couchDb).getCouchDbUrl()) + .build(); + + CouchDbInstance dbInstance = new StdCouchDbInstance(httpClient); + CouchDbConnector connector = new StdCouchDbConnector("test", dbInstance); + + connector.createDatabaseIfNotExists(); + + try(InputStream is = getClass().getResourceAsStream("all_docs_result.json")) { + String contentType = "application/text"; + + AttachmentInputStream ais = new AttachmentInputStream("1", is, contentType); + String revision = connector.createAttachment("1", ais); + assertNotNull(revision); + + Options options = new Options(); + options.includeConflicts(); + options.includeRevisions(); + options.revision(revision); + CouchDbDocument document = connector.get(CouchDbDocument.class, "1", options); + assertNotNull(document); + assertFalse(document.getAttachments().isEmpty()); + + AttachmentInputStream rs = connector.getAttachment("1", + document.getAttachments().keySet().iterator().next(), revision); + assertNotNull(rs); + + String result; + try (Scanner scan = new Scanner(rs, StandardCharsets.UTF_8.name())) { + result = scan.useDelimiter("\\A").next(); + } + + assertTrue(result.contains("1-2842770487")); + } + + } } diff --git a/src/test/java/org/ektorp/mongoquery/FormatTest.java b/src/test/java/org/ektorp/mongoquery/FormatTest.java new file mode 100644 index 00000000..354cee8e --- /dev/null +++ b/src/test/java/org/ektorp/mongoquery/FormatTest.java @@ -0,0 +1,60 @@ +package org.ektorp.mongoquery; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class FormatTest { + + @Test + public void testFormat() throws Exception { + ObjectMapper om = new ObjectMapper(); + + Expression op = new Expression(); + op.setLeft("$eq"); + + op.setRight("value"); + String result = om.writeValueAsString(op); + assertEquals("{\"$eq\":\"value\"}", result); + + op.setRight(4711); + result = om.writeValueAsString(op); + assertEquals("{\"$eq\":4711}", result); + + op.setLeft("director"); + Expression opIn = new Expression(); + opIn.setLeft("$eq"); + opIn.setRight("Lars von Trier"); + op.setRight(opIn); + result = om.writeValueAsString(op); + assertEquals("{\"director\":{\"$eq\":\"Lars von Trier\"}}", result); + + op.setLeft("genre"); + opIn.setLeft("$elemMatch"); + Expression opInIn = new Expression(); + opInIn.setLeft("$eq"); + opInIn.setRight("Horror"); + opIn.setRight(opInIn); + op.setRight(opIn); + result = om.writeValueAsString(op); + assertEquals("{\"genre\":{\"$elemMatch\":{\"$eq\":\"Horror\"}}}", result); + } + + @Test + public void testMangoQueryBuilder() throws Exception { + ObjectMapper om = new ObjectMapper(); + MangoQueryBuilder mb = new MangoQueryBuilder(); + + Operator op1 = new Operator.EqualOperator("title", "Der Herr der Ringe"); + Operator op2 = new Operator.ExistsOperator("genre", Boolean.TRUE); + + mb.and(op1); + String result = om.writeValueAsString(mb); + assertEquals("{\"title\":{\"$eq\":\"Der Herr der Ringe\"}}", result); + + mb.and(op1, op2); + result = om.writeValueAsString(mb); + assertEquals("{\"$and\":[{\"title\":{\"$eq\":\"Der Herr der Ringe\"}},{\"genre\":{\"$in\":true}}]}", result); + } +}