blob: a6260cbcf611e655f42f50fa6c4040ffce2bac8a [file] [log] [blame]
Michael Hanl173ba4b2016-05-24 20:25:05 +02001package de.ids_mannheim.korap.utils;
2
margaretha2558a7c2019-02-18 16:48:54 +01003import java.util.HashMap;
margarethab7864112017-06-29 16:36:13 +02004import java.util.List;
margaretha2558a7c2019-02-18 16:48:54 +01005import java.util.Map;
margaretha894a7d72017-11-08 19:24:20 +01006
Michael Hanl173ba4b2016-05-24 20:25:05 +02007import com.fasterxml.jackson.databind.JsonNode;
8import com.fasterxml.jackson.databind.node.ArrayNode;
9import com.fasterxml.jackson.databind.node.ObjectNode;
Michael Hanl173ba4b2016-05-24 20:25:05 +020010
margaretha894a7d72017-11-08 19:24:20 +010011import de.ids_mannheim.korap.exceptions.KustvaktException;
margarethab7864112017-06-29 16:36:13 +020012import de.ids_mannheim.korap.exceptions.StatusCodes;
13import de.ids_mannheim.korap.query.serialize.CollectionQueryProcessor;
14import de.ids_mannheim.korap.response.Notifications;
margarethab7864112017-06-29 16:36:13 +020015import edu.emory.mathcs.backport.java.util.Arrays;
Michael Hanl173ba4b2016-05-24 20:25:05 +020016
17/**
18 * convenience builder class for collection query
19 *
margarethab7864112017-06-29 16:36:13 +020020 * @author hanl, margaretha
21 * @date 29/06/2017
Michael Hanl173ba4b2016-05-24 20:25:05 +020022 */
23public class KoralCollectionQueryBuilder {
24
25 public enum EQ {
26 EQUAL, UNEQUAL
27 }
28
29 private boolean verbose;
30 private JsonNode base;
31 private StringBuilder builder;
Michael Hanlcedf7212016-05-28 10:43:09 +020032 private String mergeOperator;
Michael Hanl173ba4b2016-05-24 20:25:05 +020033
Michael Hanl33829ec2016-05-28 17:03:38 +020034 public KoralCollectionQueryBuilder () {
Michael Hanl173ba4b2016-05-24 20:25:05 +020035 this(false);
36 }
37
38
Michael Hanl33829ec2016-05-28 17:03:38 +020039 public KoralCollectionQueryBuilder (boolean verbose) {
Michael Hanl173ba4b2016-05-24 20:25:05 +020040 this.verbose = verbose;
41 this.builder = new StringBuilder();
42 this.base = null;
Michael Hanlcedf7212016-05-28 10:43:09 +020043 this.mergeOperator = null;
Michael Hanl173ba4b2016-05-24 20:25:05 +020044 }
45
46
47 /**
48 * raw method for field - value pair adding. Supports all
49 * operators (leq, geq, contains, etc.)
50 *
51 * @param field
52 * @param op
53 * @param value
54 * @return
55 */
Michael Hanl9a242162016-06-03 11:18:06 +020056 public KoralCollectionQueryBuilder with (String field, String op,
Michael Hanl33829ec2016-05-28 17:03:38 +020057 String value) {
Michael Hanl9a242162016-06-03 11:18:06 +020058 //String end = this.builder.substring(this.builder.length() - 4,
59 // this.builder.length() - 1);
60 //if (this.builder.length() != 0
61 // && (!end.contains("&") | !end.contains("|")))
62 // throw new RuntimeException("no join operator given!");
63 this.with(field + op + value);
Michael Hanl173ba4b2016-05-24 20:25:05 +020064 return this;
65 }
66
67
68 /**
69 * element can be a more complex sub query like
70 * (textClass=freizeit & Attributes.CORPUS_SIGLE=WPD)
71 *
72 * @param query
73 * will be enclosed by parenthesis in order to make sub
74 * query
75 * element
76 * @return
77 */
Michael Hanl33829ec2016-05-28 17:03:38 +020078 public KoralCollectionQueryBuilder with (String query) {
Michael Hanl173ba4b2016-05-24 20:25:05 +020079 if (!query.startsWith("(") && !query.endsWith(")"))
80 query = "(" + query + ")";
81 this.builder.append(query);
82 return this;
83 }
84
85
86
Michael Hanl173ba4b2016-05-24 20:25:05 +020087 public KoralCollectionQueryBuilder and () {
margaretha894a7d72017-11-08 19:24:20 +010088 if (this.builder.length() != 0) this.builder.append(" & ");
Michael Hanlcedf7212016-05-28 10:43:09 +020089 if (this.base != null && this.mergeOperator == null)
90 this.mergeOperator = "AND";
Michael Hanl173ba4b2016-05-24 20:25:05 +020091 return this;
92 }
93
94
95 public KoralCollectionQueryBuilder or () {
margaretha894a7d72017-11-08 19:24:20 +010096 if (this.builder.length() != 0) this.builder.append(" | ");
Michael Hanlcedf7212016-05-28 10:43:09 +020097 if (this.base != null && this.mergeOperator == null)
98 this.mergeOperator = "OR";
Michael Hanl173ba4b2016-05-24 20:25:05 +020099 return this;
100 }
101
102
margaretha894a7d72017-11-08 19:24:20 +0100103 public JsonNode rebaseCollection () throws KustvaktException {
104 if (this.builder.length() == 0 && this.base == null) return null;
Michael Hanl173ba4b2016-05-24 20:25:05 +0200105
106 JsonNode request = null;
Michael Hanlcedf7212016-05-28 10:43:09 +0200107 if (this.builder.length() != 0) {
margaretha894a7d72017-11-08 19:24:20 +0100108 CollectionQueryProcessor tree =
109 new CollectionQueryProcessor(this.verbose);
Michael Hanl173ba4b2016-05-24 20:25:05 +0200110 tree.process(this.builder.toString());
margaretha894a7d72017-11-08 19:24:20 +0100111 if (tree.getErrors().size() > 0) {
margaretha2558a7c2019-02-18 16:48:54 +0100112 // legacy support
margaretha894a7d72017-11-08 19:24:20 +0100113 for (List<Object> e : tree.getErrors()) {
margaretha2558a7c2019-02-18 16:48:54 +0100114 if (e.get(1) instanceof String[]) {
115 Notifications notif = new Notifications();
116 int code = (int) e.get(0);
margarethab7864112017-06-29 16:36:13 +0200117 notif.addError(code, (String[]) e.get(1));
margaretha2558a7c2019-02-18 16:48:54 +0100118 String notificationStr = notif.toJsonString();
119 throw new KustvaktException(StatusCodes.SERIALIZATION_FAILED,
120 notificationStr, true);
121 }
122 else{
123 break;
margarethab7864112017-06-29 16:36:13 +0200124 }
125 }
margaretha2558a7c2019-02-18 16:48:54 +0100126 // -- end of legacy support
127
128 Map<String, Object> map = new HashMap<>();
129 map.put("errors", tree.getErrors());
130 String errors = JsonUtils.toJSON(map);
margaretha894a7d72017-11-08 19:24:20 +0100131 throw new KustvaktException(StatusCodes.SERIALIZATION_FAILED,
margaretha2558a7c2019-02-18 16:48:54 +0100132 errors, true);
margarethab7864112017-06-29 16:36:13 +0200133 }
Michael Hanl173ba4b2016-05-24 20:25:05 +0200134 request = JsonUtils.valueToTree(tree.getRequestMap());
135 }
136
Michael Hanlcedf7212016-05-28 10:43:09 +0200137 if (this.base != null) {
Michael Hanl173ba4b2016-05-24 20:25:05 +0200138 // check that collection non empty
Michael Hanlcedf7212016-05-28 10:43:09 +0200139 JsonNode tmp = this.base.deepCopy();
Michael Hanl9a242162016-06-03 11:18:06 +0200140 if (request != null)
141 request = mergeWith(request);
Michael Hanl33829ec2016-05-28 17:03:38 +0200142 else
Michael Hanlcedf7212016-05-28 10:43:09 +0200143 request = tmp;
Michael Hanl173ba4b2016-05-24 20:25:05 +0200144 }
145 return request;
146 }
147
Michael Hanl33829ec2016-05-28 17:03:38 +0200148
149 public JsonNode mergeWith (JsonNode node) {
Michael Hanlcedf7212016-05-28 10:43:09 +0200150 if (this.base != null) {
Michael Hanl173ba4b2016-05-24 20:25:05 +0200151 if (node != null) {
152 JsonNode tobase = node.at("/collection");
153 JsonNode base = this.base.deepCopy();
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200154 JsonNode result = base.at("/collection");
155
156 if (result.isMissingNode() && !tobase.isMissingNode())
157 result = tobase;
158 else if (result.isMissingNode() && tobase.isMissingNode())
159 return base;
160 else {
161 result = JsonBuilder.buildDocGroup(
margaretha894a7d72017-11-08 19:24:20 +0100162 this.mergeOperator != null
163 ? this.mergeOperator.toLowerCase() : "and",
164 result, tobase);
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200165 }
Michael Hanl173ba4b2016-05-24 20:25:05 +0200166 ((ObjectNode) base).put("collection", result);
167 return base;
168 }
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200169 return this.base;
Michael Hanl173ba4b2016-05-24 20:25:05 +0200170 }
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200171 throw new RuntimeException("no query found to merge with!");
Michael Hanl173ba4b2016-05-24 20:25:05 +0200172 }
173
174
175 /**
176 * sets base query. All consequent queries are added to the first
177 * koral:docGroup within the collection base query
178 * If no group in base query, consequent queries are skipped.
179 *
180 * @param query
margaretha894a7d72017-11-08 19:24:20 +0100181 * @throws KustvaktException
Michael Hanl173ba4b2016-05-24 20:25:05 +0200182 */
margaretha894a7d72017-11-08 19:24:20 +0100183 public KoralCollectionQueryBuilder setBaseQuery (String query) throws KustvaktException {
Michael Hanl173ba4b2016-05-24 20:25:05 +0200184 this.base = JsonUtils.readTree(query);
185 return this;
186 }
Michael Hanl33829ec2016-05-28 17:03:38 +0200187
188
Michael Hanlcedf7212016-05-28 10:43:09 +0200189 public KoralCollectionQueryBuilder setBaseQuery (JsonNode query) {
190 this.base = query;
191 return this;
192 }
Michael Hanl173ba4b2016-05-24 20:25:05 +0200193
194
margaretha894a7d72017-11-08 19:24:20 +0100195 public String toJSON () throws KustvaktException {
Michael Hanl9a242162016-06-03 11:18:06 +0200196 return JsonUtils.toJSON(rebaseCollection());
Michael Hanl173ba4b2016-05-24 20:25:05 +0200197 }
198
199
200 @Override
201 public String toString () {
202 return this.builder.toString();
203 }
204
205
Michael Hanl173ba4b2016-05-24 20:25:05 +0200206 private static class JsonBuilder {
207
208 public static ObjectNode buildDoc (String key, String value) {
209 ObjectNode node = JsonUtils.createObjectNode();
210 node.put("@type", "koral:doc");
211 // eq.equals(EQ.EQUAL) ? "match:eq" : "match:ne"
212 node.put("match", "match:eq");
213 node.put("key", key);
214 node.put("value", value);
215 return node;
216 }
217
218
margaretha894a7d72017-11-08 19:24:20 +0100219 public static ObjectNode buildDocGroup (String op,
220 JsonNode ... groups) {
Michael Hanl173ba4b2016-05-24 20:25:05 +0200221 ObjectNode node = JsonUtils.createObjectNode();
222 node.put("@type", "koral:docGroup");
223 node.put("operation", "operation:" + op);
224 ArrayNode ops = JsonUtils.createArrayNode();
225 ops.addAll(Arrays.asList(groups));
226 node.put("operands", ops);
227 return node;
228 }
229
230 }
231
232}