blob: 28e75e927bb5de3ef96c01d00064b624a0a28529 [file] [log] [blame]
Michael Hanl173ba4b2016-05-24 20:25:05 +02001package de.ids_mannheim.korap.utils;
2
margaretha7677aa42024-01-05 10:26:06 +01003import java.util.Arrays;
margaretha2558a7c2019-02-18 16:48:54 +01004import java.util.HashMap;
margarethab7864112017-06-29 16:36:13 +02005import java.util.List;
margaretha2558a7c2019-02-18 16:48:54 +01006import java.util.Map;
margaretha894a7d72017-11-08 19:24:20 +01007
Michael Hanl173ba4b2016-05-24 20:25:05 +02008import com.fasterxml.jackson.databind.JsonNode;
9import com.fasterxml.jackson.databind.node.ArrayNode;
10import com.fasterxml.jackson.databind.node.ObjectNode;
Michael Hanl173ba4b2016-05-24 20:25:05 +020011
margaretha894a7d72017-11-08 19:24:20 +010012import de.ids_mannheim.korap.exceptions.KustvaktException;
margarethab7864112017-06-29 16:36:13 +020013import de.ids_mannheim.korap.exceptions.StatusCodes;
14import de.ids_mannheim.korap.query.serialize.CollectionQueryProcessor;
15import de.ids_mannheim.korap.response.Notifications;
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
Michael Hanl33829ec2016-05-28 17:03:38 +020038 public KoralCollectionQueryBuilder (boolean verbose) {
Michael Hanl173ba4b2016-05-24 20:25:05 +020039 this.verbose = verbose;
40 this.builder = new StringBuilder();
41 this.base = null;
Michael Hanlcedf7212016-05-28 10:43:09 +020042 this.mergeOperator = null;
Michael Hanl173ba4b2016-05-24 20:25:05 +020043 }
44
Michael Hanl173ba4b2016-05-24 20:25:05 +020045 /**
46 * raw method for field - value pair adding. Supports all
47 * operators (leq, geq, contains, etc.)
48 *
49 * @param field
50 * @param op
51 * @param value
52 * @return
53 */
Michael Hanl9a242162016-06-03 11:18:06 +020054 public KoralCollectionQueryBuilder with (String field, String op,
Michael Hanl33829ec2016-05-28 17:03:38 +020055 String value) {
Michael Hanl9a242162016-06-03 11:18:06 +020056 //String end = this.builder.substring(this.builder.length() - 4,
57 // this.builder.length() - 1);
58 //if (this.builder.length() != 0
59 // && (!end.contains("&") | !end.contains("|")))
60 // throw new RuntimeException("no join operator given!");
61 this.with(field + op + value);
Michael Hanl173ba4b2016-05-24 20:25:05 +020062 return this;
63 }
64
Michael Hanl173ba4b2016-05-24 20:25:05 +020065 /**
66 * element can be a more complex sub query like
67 * (textClass=freizeit & Attributes.CORPUS_SIGLE=WPD)
68 *
69 * @param query
70 * will be enclosed by parenthesis in order to make sub
71 * query
72 * element
73 * @return
74 */
Michael Hanl33829ec2016-05-28 17:03:38 +020075 public KoralCollectionQueryBuilder with (String query) {
Michael Hanl173ba4b2016-05-24 20:25:05 +020076 if (!query.startsWith("(") && !query.endsWith(")"))
77 query = "(" + query + ")";
78 this.builder.append(query);
79 return this;
80 }
81
Michael Hanl173ba4b2016-05-24 20:25:05 +020082 public KoralCollectionQueryBuilder and () {
margaretha35e1ca22023-11-16 22:00:01 +010083 if (this.builder.length() != 0)
84 this.builder.append(" & ");
Michael Hanlcedf7212016-05-28 10:43:09 +020085 if (this.base != null && this.mergeOperator == null)
86 this.mergeOperator = "AND";
Michael Hanl173ba4b2016-05-24 20:25:05 +020087 return this;
88 }
89
Michael Hanl173ba4b2016-05-24 20:25:05 +020090 public KoralCollectionQueryBuilder or () {
margaretha35e1ca22023-11-16 22:00:01 +010091 if (this.builder.length() != 0)
92 this.builder.append(" | ");
Michael Hanlcedf7212016-05-28 10:43:09 +020093 if (this.base != null && this.mergeOperator == null)
94 this.mergeOperator = "OR";
Michael Hanl173ba4b2016-05-24 20:25:05 +020095 return this;
96 }
97
margaretha894a7d72017-11-08 19:24:20 +010098 public JsonNode rebaseCollection () throws KustvaktException {
margaretha35e1ca22023-11-16 22:00:01 +010099 if (this.builder.length() == 0 && this.base == null)
100 return null;
Michael Hanl173ba4b2016-05-24 20:25:05 +0200101
102 JsonNode request = null;
Michael Hanlcedf7212016-05-28 10:43:09 +0200103 if (this.builder.length() != 0) {
margaretha35e1ca22023-11-16 22:00:01 +0100104 CollectionQueryProcessor tree = new CollectionQueryProcessor(
105 this.verbose);
Michael Hanl173ba4b2016-05-24 20:25:05 +0200106 tree.process(this.builder.toString());
margaretha894a7d72017-11-08 19:24:20 +0100107 if (tree.getErrors().size() > 0) {
margaretha2558a7c2019-02-18 16:48:54 +0100108 // legacy support
margaretha894a7d72017-11-08 19:24:20 +0100109 for (List<Object> e : tree.getErrors()) {
margaretha2558a7c2019-02-18 16:48:54 +0100110 if (e.get(1) instanceof String[]) {
111 Notifications notif = new Notifications();
112 int code = (int) e.get(0);
margarethab7864112017-06-29 16:36:13 +0200113 notif.addError(code, (String[]) e.get(1));
margaretha2558a7c2019-02-18 16:48:54 +0100114 String notificationStr = notif.toJsonString();
margaretha35e1ca22023-11-16 22:00:01 +0100115 throw new KustvaktException(
116 StatusCodes.SERIALIZATION_FAILED,
margaretha2558a7c2019-02-18 16:48:54 +0100117 notificationStr, true);
118 }
margaretha35e1ca22023-11-16 22:00:01 +0100119 else {
margaretha2558a7c2019-02-18 16:48:54 +0100120 break;
margarethab7864112017-06-29 16:36:13 +0200121 }
122 }
margaretha2558a7c2019-02-18 16:48:54 +0100123 // -- end of legacy support
margaretha35e1ca22023-11-16 22:00:01 +0100124
margaretha2558a7c2019-02-18 16:48:54 +0100125 Map<String, Object> map = new HashMap<>();
126 map.put("errors", tree.getErrors());
127 String errors = JsonUtils.toJSON(map);
margaretha894a7d72017-11-08 19:24:20 +0100128 throw new KustvaktException(StatusCodes.SERIALIZATION_FAILED,
margaretha2558a7c2019-02-18 16:48:54 +0100129 errors, true);
margarethab7864112017-06-29 16:36:13 +0200130 }
Michael Hanl173ba4b2016-05-24 20:25:05 +0200131 request = JsonUtils.valueToTree(tree.getRequestMap());
132 }
133
Michael Hanlcedf7212016-05-28 10:43:09 +0200134 if (this.base != null) {
Michael Hanl173ba4b2016-05-24 20:25:05 +0200135 // check that collection non empty
Michael Hanlcedf7212016-05-28 10:43:09 +0200136 JsonNode tmp = this.base.deepCopy();
Michael Hanl9a242162016-06-03 11:18:06 +0200137 if (request != null)
138 request = mergeWith(request);
Michael Hanl33829ec2016-05-28 17:03:38 +0200139 else
Michael Hanlcedf7212016-05-28 10:43:09 +0200140 request = tmp;
Michael Hanl173ba4b2016-05-24 20:25:05 +0200141 }
142 return request;
143 }
144
Michael Hanl33829ec2016-05-28 17:03:38 +0200145 public JsonNode mergeWith (JsonNode node) {
Michael Hanlcedf7212016-05-28 10:43:09 +0200146 if (this.base != null) {
Michael Hanl173ba4b2016-05-24 20:25:05 +0200147 if (node != null) {
148 JsonNode tobase = node.at("/collection");
149 JsonNode base = this.base.deepCopy();
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200150 JsonNode result = base.at("/collection");
151
152 if (result.isMissingNode() && !tobase.isMissingNode())
153 result = tobase;
154 else if (result.isMissingNode() && tobase.isMissingNode())
155 return base;
156 else {
margaretha35e1ca22023-11-16 22:00:01 +0100157 result = JsonBuilder
158 .buildDocGroup(this.mergeOperator != null
159 ? this.mergeOperator.toLowerCase()
160 : "and", result, tobase);
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200161 }
Michael Hanl173ba4b2016-05-24 20:25:05 +0200162 ((ObjectNode) base).put("collection", result);
163 return base;
164 }
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200165 return this.base;
Michael Hanl173ba4b2016-05-24 20:25:05 +0200166 }
Michael Hanlcb2d3f92016-06-02 17:34:06 +0200167 throw new RuntimeException("no query found to merge with!");
Michael Hanl173ba4b2016-05-24 20:25:05 +0200168 }
169
Michael Hanl173ba4b2016-05-24 20:25:05 +0200170 /**
171 * sets base query. All consequent queries are added to the first
172 * koral:docGroup within the collection base query
173 * If no group in base query, consequent queries are skipped.
174 *
175 * @param query
margaretha35e1ca22023-11-16 22:00:01 +0100176 * @throws KustvaktException
Michael Hanl173ba4b2016-05-24 20:25:05 +0200177 */
margaretha35e1ca22023-11-16 22:00:01 +0100178 public KoralCollectionQueryBuilder setBaseQuery (String query)
179 throws KustvaktException {
Michael Hanl173ba4b2016-05-24 20:25:05 +0200180 this.base = JsonUtils.readTree(query);
181 return this;
182 }
Michael Hanl33829ec2016-05-28 17:03:38 +0200183
Michael Hanlcedf7212016-05-28 10:43:09 +0200184 public KoralCollectionQueryBuilder setBaseQuery (JsonNode query) {
185 this.base = query;
186 return this;
187 }
Michael Hanl173ba4b2016-05-24 20:25:05 +0200188
margaretha894a7d72017-11-08 19:24:20 +0100189 public String toJSON () throws KustvaktException {
Michael Hanl9a242162016-06-03 11:18:06 +0200190 return JsonUtils.toJSON(rebaseCollection());
Michael Hanl173ba4b2016-05-24 20:25:05 +0200191 }
192
Michael Hanl173ba4b2016-05-24 20:25:05 +0200193 @Override
194 public String toString () {
195 return this.builder.toString();
196 }
197
Michael Hanl173ba4b2016-05-24 20:25:05 +0200198 private static class JsonBuilder {
199
200 public static ObjectNode buildDoc (String key, String value) {
201 ObjectNode node = JsonUtils.createObjectNode();
202 node.put("@type", "koral:doc");
203 // eq.equals(EQ.EQUAL) ? "match:eq" : "match:ne"
204 node.put("match", "match:eq");
205 node.put("key", key);
206 node.put("value", value);
207 return node;
208 }
209
margaretha894a7d72017-11-08 19:24:20 +0100210 public static ObjectNode buildDocGroup (String op,
211 JsonNode ... groups) {
Michael Hanl173ba4b2016-05-24 20:25:05 +0200212 ObjectNode node = JsonUtils.createObjectNode();
213 node.put("@type", "koral:docGroup");
214 node.put("operation", "operation:" + op);
215 ArrayNode ops = JsonUtils.createArrayNode();
216 ops.addAll(Arrays.asList(groups));
217 node.put("operands", ops);
218 return node;
219 }
220
221 }
222
223}