blob: 811c2279c8bd00b47426eb775814e5c6aafb4c90 [file] [log] [blame]
Michael Hanl173ba4b2016-05-24 20:25:05 +02001package de.ids_mannheim.korap.utils;
2
3import com.fasterxml.jackson.databind.JsonNode;
4import com.fasterxml.jackson.databind.node.ArrayNode;
5import com.fasterxml.jackson.databind.node.ObjectNode;
6import de.ids_mannheim.korap.query.serialize.CollectionQueryProcessor;
7import edu.emory.mathcs.backport.java.util.Arrays;
8
Michael Hanl9a242162016-06-03 11:18:06 +02009import java.io.IOError;
Michael Hanl173ba4b2016-05-24 20:25:05 +020010import java.util.Map;
11
12/**
13 * convenience builder class for collection query
14 *
15 * @author hanl
16 * @date 16/09/2014
17 */
18public class KoralCollectionQueryBuilder {
19
20 public enum EQ {
21 EQUAL, UNEQUAL
22 }
23
24 private boolean verbose;
25 private JsonNode base;
26 private StringBuilder builder;
Michael Hanlcedf7212016-05-28 10:43:09 +020027 private String mergeOperator;
Michael Hanl173ba4b2016-05-24 20:25:05 +020028
29
Michael Hanl33829ec2016-05-28 17:03:38 +020030 public KoralCollectionQueryBuilder () {
Michael Hanl173ba4b2016-05-24 20:25:05 +020031 this(false);
32 }
33
34
Michael Hanl33829ec2016-05-28 17:03:38 +020035 public KoralCollectionQueryBuilder (boolean verbose) {
Michael Hanl173ba4b2016-05-24 20:25:05 +020036 this.verbose = verbose;
37 this.builder = new StringBuilder();
38 this.base = null;
Michael Hanlcedf7212016-05-28 10:43:09 +020039 this.mergeOperator = null;
Michael Hanl173ba4b2016-05-24 20:25:05 +020040 }
41
42
43 /**
44 * raw method for field - value pair adding. Supports all
45 * operators (leq, geq, contains, etc.)
46 *
47 * @param field
48 * @param op
49 * @param value
50 * @return
51 */
Michael Hanl9a242162016-06-03 11:18:06 +020052 public KoralCollectionQueryBuilder with (String field, String op,
Michael Hanl33829ec2016-05-28 17:03:38 +020053 String value) {
Michael Hanl9a242162016-06-03 11:18:06 +020054 //String end = this.builder.substring(this.builder.length() - 4,
55 // this.builder.length() - 1);
56 //if (this.builder.length() != 0
57 // && (!end.contains("&") | !end.contains("|")))
58 // throw new RuntimeException("no join operator given!");
59 this.with(field + op + value);
Michael Hanl173ba4b2016-05-24 20:25:05 +020060 return this;
61 }
62
63
64 /**
65 * element can be a more complex sub query like
66 * (textClass=freizeit & Attributes.CORPUS_SIGLE=WPD)
67 *
68 * @param query
69 * will be enclosed by parenthesis in order to make sub
70 * query
71 * element
72 * @return
73 */
Michael Hanl33829ec2016-05-28 17:03:38 +020074 public KoralCollectionQueryBuilder with (String query) {
Michael Hanl173ba4b2016-05-24 20:25:05 +020075 if (!query.startsWith("(") && !query.endsWith(")"))
76 query = "(" + query + ")";
77 this.builder.append(query);
78 return this;
79 }
80
81
82
Michael Hanl173ba4b2016-05-24 20:25:05 +020083 public KoralCollectionQueryBuilder and () {
Michael Hanlcedf7212016-05-28 10:43:09 +020084 if (this.builder.length() != 0)
85 this.builder.append(" & ");
86 if (this.base != null && this.mergeOperator == null)
87 this.mergeOperator = "AND";
Michael Hanl173ba4b2016-05-24 20:25:05 +020088 return this;
89 }
90
91
92 public KoralCollectionQueryBuilder or () {
Michael Hanlcedf7212016-05-28 10:43:09 +020093 if (this.builder.length() != 0)
94 this.builder.append(" | ");
95 if (this.base != null && this.mergeOperator == null)
96 this.mergeOperator = "OR";
Michael Hanl173ba4b2016-05-24 20:25:05 +020097 return this;
98 }
99
100
Michael Hanl9a242162016-06-03 11:18:06 +0200101 public Object rebaseCollection () {
Michael Hanlcedf7212016-05-28 10:43:09 +0200102 if (this.builder.length() == 0 && this.base == null)
Michael Hanl173ba4b2016-05-24 20:25:05 +0200103 return null;
104
105 JsonNode request = null;
Michael Hanlcedf7212016-05-28 10:43:09 +0200106 if (this.builder.length() != 0) {
Michael Hanl173ba4b2016-05-24 20:25:05 +0200107 CollectionQueryProcessor tree = new CollectionQueryProcessor(
108 this.verbose);
109 tree.process(this.builder.toString());
110 request = JsonUtils.valueToTree(tree.getRequestMap());
111 }
112
Michael Hanlcedf7212016-05-28 10:43:09 +0200113 if (this.base != null) {
Michael Hanl173ba4b2016-05-24 20:25:05 +0200114 // check that collection non empty
Michael Hanlcedf7212016-05-28 10:43:09 +0200115 JsonNode tmp = this.base.deepCopy();
Michael Hanl9a242162016-06-03 11:18:06 +0200116 if (request != null)
117 request = mergeWith(request);
Michael Hanl33829ec2016-05-28 17:03:38 +0200118 else
Michael Hanlcedf7212016-05-28 10:43:09 +0200119 request = tmp;
Michael Hanl173ba4b2016-05-24 20:25:05 +0200120 }
121 return request;
122 }
123
Michael Hanl33829ec2016-05-28 17:03:38 +0200124
125 public JsonNode mergeWith (JsonNode node) {
Michael Hanlcedf7212016-05-28 10:43:09 +0200126 if (this.base != null) {
Michael Hanl173ba4b2016-05-24 20:25:05 +0200127 if (node != null) {
128 JsonNode tobase = node.at("/collection");
129 JsonNode base = this.base.deepCopy();
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200130 JsonNode result = base.at("/collection");
131
132 if (result.isMissingNode() && !tobase.isMissingNode())
133 result = tobase;
134 else if (result.isMissingNode() && tobase.isMissingNode())
135 return base;
136 else {
137 result = JsonBuilder.buildDocGroup(
138 this.mergeOperator != null ? this.mergeOperator
139 .toLowerCase() : "and", result, tobase);
140 }
Michael Hanl173ba4b2016-05-24 20:25:05 +0200141 ((ObjectNode) base).put("collection", result);
142 return base;
143 }
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200144 return this.base;
Michael Hanl173ba4b2016-05-24 20:25:05 +0200145 }
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200146 throw new RuntimeException("no query found to merge with!");
Michael Hanl173ba4b2016-05-24 20:25:05 +0200147 }
148
149
150 /**
151 * sets base query. All consequent queries are added to the first
152 * koral:docGroup within the collection base query
153 * If no group in base query, consequent queries are skipped.
154 *
155 * @param query
156 */
157 public KoralCollectionQueryBuilder setBaseQuery (String query) {
158 this.base = JsonUtils.readTree(query);
159 return this;
160 }
Michael Hanl33829ec2016-05-28 17:03:38 +0200161
162
Michael Hanlcedf7212016-05-28 10:43:09 +0200163 public KoralCollectionQueryBuilder setBaseQuery (JsonNode query) {
164 this.base = query;
165 return this;
166 }
Michael Hanl173ba4b2016-05-24 20:25:05 +0200167
168
169 public String toJSON () {
Michael Hanl9a242162016-06-03 11:18:06 +0200170 return JsonUtils.toJSON(rebaseCollection());
Michael Hanl173ba4b2016-05-24 20:25:05 +0200171 }
172
173
174 @Override
175 public String toString () {
176 return this.builder.toString();
177 }
178
179
Michael Hanl173ba4b2016-05-24 20:25:05 +0200180 private static class JsonBuilder {
181
182 public static ObjectNode buildDoc (String key, String value) {
183 ObjectNode node = JsonUtils.createObjectNode();
184 node.put("@type", "koral:doc");
185 // eq.equals(EQ.EQUAL) ? "match:eq" : "match:ne"
186 node.put("match", "match:eq");
187 node.put("key", key);
188 node.put("value", value);
189 return node;
190 }
191
192
193 public static ObjectNode buildDocGroup (String op, JsonNode ... groups) {
Michael Hanl173ba4b2016-05-24 20:25:05 +0200194 ObjectNode node = JsonUtils.createObjectNode();
195 node.put("@type", "koral:docGroup");
196 node.put("operation", "operation:" + op);
197 ArrayNode ops = JsonUtils.createArrayNode();
198 ops.addAll(Arrays.asList(groups));
199 node.put("operands", ops);
200 return node;
201 }
202
203 }
204
205}