blob: af1db1d8ab6c837250ea8e1b7cf18905ba7e6d27 [file] [log] [blame]
Nils Diewaldf399a672013-11-18 17:55:22 +00001package de.ids_mannheim.korap;
2
Eliza Margaretha221349f2015-01-29 17:27:41 +00003import java.io.IOException;
Eliza Margaretha641b20a2015-02-16 17:23:13 +00004import java.util.ArrayList;
5import java.util.List;
Akron738ed002015-06-18 12:07:50 +02006import java.util.Iterator;
Eliza Margarethaafe98122015-01-23 17:37:57 +00007
Eliza Margarethab1a5ed42015-01-26 10:37:12 +00008import org.slf4j.Logger;
9import org.slf4j.LoggerFactory;
10
Eliza Margaretha221349f2015-01-29 17:27:41 +000011import com.fasterxml.jackson.databind.JsonNode;
12import com.fasterxml.jackson.databind.ObjectMapper;
margaretha144983d2015-05-07 11:52:17 +020013import com.fasterxml.jackson.databind.node.ArrayNode;
14import com.fasterxml.jackson.databind.node.ObjectNode;
Eliza Margaretha221349f2015-01-29 17:27:41 +000015
Nils Diewald8904c1d2015-02-26 16:13:18 +000016import de.ids_mannheim.korap.query.QueryBuilder;
margaretha5c26de42015-04-14 12:47:58 +020017import de.ids_mannheim.korap.query.SpanWithinQuery;
18import de.ids_mannheim.korap.query.wrap.SpanAlterQueryWrapper;
19import de.ids_mannheim.korap.query.wrap.SpanAttributeQueryWrapper;
20import de.ids_mannheim.korap.query.wrap.SpanClassQueryWrapper;
21import de.ids_mannheim.korap.query.wrap.SpanFocusQueryWrapper;
22import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
margaretha144983d2015-05-07 11:52:17 +020023import de.ids_mannheim.korap.query.wrap.SpanReferenceQueryWrapper;
margaretha5c26de42015-04-14 12:47:58 +020024import de.ids_mannheim.korap.query.wrap.SpanRegexQueryWrapper;
25import de.ids_mannheim.korap.query.wrap.SpanRelationWrapper;
26import de.ids_mannheim.korap.query.wrap.SpanRepetitionQueryWrapper;
27import de.ids_mannheim.korap.query.wrap.SpanSegmentQueryWrapper;
28import de.ids_mannheim.korap.query.wrap.SpanSequenceQueryWrapper;
29import de.ids_mannheim.korap.query.wrap.SpanSimpleQueryWrapper;
30import de.ids_mannheim.korap.query.wrap.SpanSubspanQueryWrapper;
31import de.ids_mannheim.korap.query.wrap.SpanWithAttributeQueryWrapper;
32import de.ids_mannheim.korap.query.wrap.SpanWithinQueryWrapper;
Eliza Margaretha221349f2015-01-29 17:27:41 +000033import de.ids_mannheim.korap.response.Notifications;
34import de.ids_mannheim.korap.util.QueryException;
35
Nils Diewaldf399a672013-11-18 17:55:22 +000036/**
Nils Diewald21914ff2015-02-28 02:09:47 +000037 * <p>
Nils Diewald8904c1d2015-02-26 16:13:18 +000038 * KrillQuery provides deserialization methods
39 * for KoralQuery query objects.
Nils Diewald21914ff2015-02-28 02:09:47 +000040 * </p>
Nils Diewaldbb33da22015-03-04 16:24:25 +000041 *
Nils Diewaldd75e6f62015-01-28 23:44:56 +000042 * <blockquote><pre>
Nils Diewaldbb33da22015-03-04 16:24:25 +000043 * // Create or receive a KoralQuery JSON string
44 * String koral = "{\"@type\":"koral:group", ... }";
45 *
46 * SpanQueryWrapper sqw = new
47 * KrillQuery("tokens").fromJson("{... JsonString ...}");
Nils Diewaldd75e6f62015-01-28 23:44:56 +000048 * </pre></blockquote>
Nils Diewaldbb33da22015-03-04 16:24:25 +000049 *
Nils Diewaldd75e6f62015-01-28 23:44:56 +000050 * @author diewald
Nils Diewaldf399a672013-11-18 17:55:22 +000051 */
Nils Diewaldcec40f92015-02-19 22:20:02 +000052/*
Nils Diewald6535c522015-02-26 17:45:24 +000053 TODO: Merge this with SpanQueryWrapper
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000054
Nils Diewald6535c522015-02-26 17:45:24 +000055 TODO: Use full-blown jsonld processor
56
57 TODO: All queries with a final right expansion
Nils Diewaldcec40f92015-02-19 22:20:02 +000058 e.g. der alte []
59 should be wrapped in a contains(<base/s=t>) to ensure
60 they are not outside the text.
Nils Diewald21914ff2015-02-28 02:09:47 +000061
Nils Diewaldcec40f92015-02-19 22:20:02 +000062 TODO: Create Pre-filter while preparing a Query.
63 The pre-filter will contain a boolena query with all
64 necessary terms, supporting boolean OR, ignoring
65 negation terms (and negation subqueries), like
66 [base=Der]([base=alte]|[base=junge])[base=Mann & p!=ADJA]![base=war | base=lag]
67 Search for all documents containing "s:Der" and ("s:alte" or "s:junge") and "s:Mann"
68*/
Akron98b78542015-08-06 21:43:08 +020069public final class KrillQuery extends Notifications {
Nils Diewald8904c1d2015-02-26 16:13:18 +000070 private QueryBuilder builder;
Nils Diewald21914ff2015-02-28 02:09:47 +000071 private String field;
Nils Diewaldbbd39a52015-02-23 19:56:57 +000072 private JsonNode json;
Nils Diewaldf399a672013-11-18 17:55:22 +000073
74 // Logger
Nils Diewald0339d462015-02-26 14:53:56 +000075 private final static Logger log = LoggerFactory.getLogger(KrillQuery.class);
Nils Diewaldf399a672013-11-18 17:55:22 +000076
Nils Diewald8c543432014-02-27 18:25:38 +000077 // This advices the java compiler to ignore all loggings
78 public static final boolean DEBUG = false;
79
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000080 // <legacy>
Nils Diewaldbb33da22015-03-04 16:24:25 +000081 public static final byte OVERLAP = SpanWithinQuery.OVERLAP,
82 REAL_OVERLAP = SpanWithinQuery.REAL_OVERLAP,
83 WITHIN = SpanWithinQuery.WITHIN,
84 REAL_WITHIN = SpanWithinQuery.REAL_WITHIN,
85 ENDSWITH = SpanWithinQuery.ENDSWITH,
86 STARTSWITH = SpanWithinQuery.STARTSWITH,
87 MATCH = SpanWithinQuery.MATCH;
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000088 // </legacy>
Nils Diewald6802acd2014-03-18 18:29:30 +000089
Nils Diewald56dc2582014-11-04 21:33:46 +000090 private static final int MAX_CLASS_NUM = 255; // 127;
Nils Diewald8c543432014-02-27 18:25:38 +000091
Nils Diewaldcec40f92015-02-19 22:20:02 +000092 // Private class for koral:boundary objects
Nils Diewaldd75e6f62015-01-28 23:44:56 +000093 private class Boundary {
94 public int min, max;
95
Nils Diewaldbb33da22015-03-04 16:24:25 +000096
Nils Diewaldd75e6f62015-01-28 23:44:56 +000097 // Constructor for boundaries
98 public Boundary (JsonNode json, int defaultMin, int defaultMax)
Nils Diewaldbb33da22015-03-04 16:24:25 +000099 throws QueryException {
100
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000101 // No @type defined
102 if (!json.has("@type")) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000103 throw new QueryException(701,
104 "JSON-LD group has no @type attribute");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000105 };
106
107 // Wrong @type defined
Nils Diewaldcec40f92015-02-19 22:20:02 +0000108 if (!json.get("@type").asText().equals("koral:boundary"))
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000109 throw new QueryException(702, "Boundary definition is invalid");
110
111 // Set min boundary
Nils Diewaldbb33da22015-03-04 16:24:25 +0000112 this.min = json.has("min") ? json.get("min").asInt(defaultMin)
113 : defaultMin;
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000114
115 // Set max boundary
Nils Diewaldbb33da22015-03-04 16:24:25 +0000116 this.max = json.has("max") ? json.get("max").asInt(defaultMax)
117 : defaultMax;
118
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000119 if (DEBUG)
Nils Diewaldcec40f92015-02-19 22:20:02 +0000120 log.trace("Found koral:boundary with {}:{}", min, max);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000121 };
122 };
123
Akronbb5d1732015-06-22 01:22:40 +0200124
Nils Diewald21914ff2015-02-28 02:09:47 +0000125 /**
126 * Constructs a new object for query deserialization
127 * and building. Expects the name of an index field
128 * to apply the query on (this should normally be
129 * a token stream field).
Nils Diewaldbb33da22015-03-04 16:24:25 +0000130 *
131 * @param field
132 * The specific index field for the query.
Nils Diewald21914ff2015-02-28 02:09:47 +0000133 */
134 public KrillQuery (String field) {
135 this.field = field;
136 };
137
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000138
Nils Diewald47f62e22014-07-24 14:51:38 +0000139 /**
Nils Diewaldbb33da22015-03-04 16:24:25 +0000140 * <p>Deserialize JSON-LD query to a {@link SpanQueryWrapper}
141 * object.</p>
142 *
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000143 * <blockquote><pre>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000144 * KrillQuery kq = new KrillQuery("tokens");
145 * SpanQueryWrapper sqw = kq.fromJson(
146 * "{\"@type\" : \"koral:token\","+
147 * "\"wrap\" : {" +
148 * "\"@type\" : \"koral:term\"," +
149 * "\"foundry\" : \"opennlp\"," +
150 * "\"key\" : \"tree\"," +
151 * "\"layer\" : \"orth\"," +
152 * "\"match\" : \"match:eq\""+
153 * "}}"
154 * );
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000155 * </pre></blockquote>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000156 *
157 * @param json
158 * String representing the JSON query string.
159 * @return {@link SpanQueryWrapper} object.
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000160 * @throws QueryException
Nils Diewald47f62e22014-07-24 14:51:38 +0000161 */
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000162 public SpanQueryWrapper fromJson (String json) throws QueryException {
163 JsonNode jsonN;
164 try {
165 // Read Json string
Nils Diewald8904c1d2015-02-26 16:13:18 +0000166 jsonN = new ObjectMapper().readValue(json, JsonNode.class);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000167 }
Nils Diewald47f62e22014-07-24 14:51:38 +0000168
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000169 // Something went wrong
170 catch (IOException e) {
171 String msg = e.getMessage();
172 log.warn("Unable to parse JSON: " + msg.split("\n")[0]);
173 throw new QueryException(621, "Unable to parse JSON");
174 };
Nils Diewald1455e1e2014-08-01 16:12:43 +0000175
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000176 // The query is nested in a parent query
177 if (!jsonN.has("@type") && jsonN.has("query"))
178 jsonN = jsonN.get("query");
Nils Diewald33fcb5d2014-11-07 23:27:03 +0000179
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000180 // Deserialize from node
181 return this.fromJson(jsonN);
Nils Diewald47f62e22014-07-24 14:51:38 +0000182 };
183
margarethab097bac2015-04-15 11:37:02 +0200184
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000185 /**
Nils Diewald21914ff2015-02-28 02:09:47 +0000186 * <p>Deserialize JSON-LD query as a {@link JsonNode} object
187 * to a {@link SpanQueryWrapper} object.</p>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000188 *
189 * @param json
190 * {@link JsonNode} representing the JSON query string.
191 * @return {@link SpanQueryWrapper} object.
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000192 * @throws QueryException
193 */
Nils Diewald6d50c1f2013-12-04 20:14:08 +0000194 // TODO: Exception messages are horrible!
Nils Diewald21914ff2015-02-28 02:09:47 +0000195 // TODO: Use the shortcuts implemented in the builder
196 // instead of the wrapper constructors
Nils Diewaldc86aa482014-02-12 16:58:05 +0000197 // TODO: Rename this span context!
margarethab097bac2015-04-15 11:37:02 +0200198 public SpanQueryWrapper fromJson (JsonNode json) throws QueryException {
Akrond504f212015-06-20 00:27:54 +0200199
200 // Set this for reserialization - may be changed later on
201 this.json = json;
202 return this._fromJson(json);
203 };
204
205
206 private SpanQueryWrapper _fromJson (JsonNode json) throws QueryException {
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000207 int number = 0;
Nils Diewald21914ff2015-02-28 02:09:47 +0000208
209 // Only accept @typed objects for the moment
210 // TODO: Support @context for cosmas:...
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000211 if (!json.has("@type"))
Nils Diewaldbb33da22015-03-04 16:24:25 +0000212 throw new QueryException(701,
213 "JSON-LD group has no @type attribute");
Nils Diewald6d50c1f2013-12-04 20:14:08 +0000214
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000215 // Get @type for branching
216 String type = json.get("@type").asText();
Nils Diewald1455e1e2014-08-01 16:12:43 +0000217
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000218 switch (type) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000219 case "koral:group":
220 return this._groupFromJson(json);
Nils Diewald6d50c1f2013-12-04 20:14:08 +0000221
Nils Diewaldbb33da22015-03-04 16:24:25 +0000222 case "koral:reference":
223 if (json.has("operation")
224 && !json.get("operation").asText()
225 .equals("operation:focus"))
226 throw new QueryException(712, "Unknown reference operation");
Nils Diewald6d50c1f2013-12-04 20:14:08 +0000227
Nils Diewaldbb33da22015-03-04 16:24:25 +0000228 if (!json.has("operands")) {
229 throw new QueryException(766,
230 "Peripheral references are currently not supported");
231 }
Nils Diewaldc86aa482014-02-12 16:58:05 +0000232
Nils Diewaldbb33da22015-03-04 16:24:25 +0000233 JsonNode operands = json.get("operands");
Nils Diewalddc8dc342014-07-25 13:38:50 +0000234
Nils Diewaldbb33da22015-03-04 16:24:25 +0000235 if (!operands.isArray())
236 throw new QueryException(704,
237 "Operation needs operand list");
Nils Diewaldc86aa482014-02-12 16:58:05 +0000238
Nils Diewaldbb33da22015-03-04 16:24:25 +0000239 if (operands.size() == 0)
240 throw new QueryException(704,
241 "Operation needs operand list");
Nils Diewald164f8be2014-02-13 02:43:16 +0000242
Nils Diewaldbb33da22015-03-04 16:24:25 +0000243 if (operands.size() != 1)
244 throw new QueryException(705,
245 "Number of operands is not acceptable");
Nils Diewald164f8be2014-02-13 02:43:16 +0000246
Nils Diewaldbb33da22015-03-04 16:24:25 +0000247 // Reference based on classes
248 if (json.has("classRef")) {
249 if (json.has("classRefOp")) {
250 throw new QueryException(761,
251 "Class reference operators are currently not supported");
252 };
Nils Diewaldb84e7272014-11-07 01:27:38 +0000253
Nils Diewaldbb33da22015-03-04 16:24:25 +0000254 number = json.get("classRef").get(0).asInt();
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000255
Nils Diewaldbb33da22015-03-04 16:24:25 +0000256 if (number > MAX_CLASS_NUM)
257 throw new QueryException(709,
258 "Valid class numbers exceeded");
259 }
Nils Diewald67331b52014-11-06 21:16:56 +0000260
Nils Diewaldbb33da22015-03-04 16:24:25 +0000261 // Reference based on spans
262 else if (json.has("spanRef")) {
263 JsonNode spanRef = json.get("spanRef");
264 int length = 0;
265 int startOffset = 0;
266 if (!spanRef.isArray() || spanRef.size() == 0) {
267 throw new QueryException(714,
268 "Span references expect a start position"
269 + " and a length parameter");
270 };
Nils Diewald6409a922015-01-29 20:50:42 +0000271
Nils Diewaldbb33da22015-03-04 16:24:25 +0000272 if (spanRef.size() > 1)
273 length = spanRef.get(1).asInt(0);
Nils Diewald6409a922015-01-29 20:50:42 +0000274
Nils Diewaldbb33da22015-03-04 16:24:25 +0000275 startOffset = spanRef.get(0).asInt(0);
Nils Diewaldee4a6b72014-06-30 18:23:12 +0000276
Nils Diewaldbb33da22015-03-04 16:24:25 +0000277 if (DEBUG)
278 log.trace("Wrap span reference {},{}", startOffset,
279 length);
Nils Diewald1455e1e2014-08-01 16:12:43 +0000280
Akrond504f212015-06-20 00:27:54 +0200281 SpanQueryWrapper sqw = this._fromJson(operands.get(0));
Nils Diewaldbb33da22015-03-04 16:24:25 +0000282 SpanSubspanQueryWrapper ssqw = new SpanSubspanQueryWrapper(
283 sqw, startOffset, length);
284 return ssqw;
Akronbb5d1732015-06-22 01:22:40 +0200285 }
286 ;
Nils Diewaldee4a6b72014-06-30 18:23:12 +0000287
Nils Diewaldbb33da22015-03-04 16:24:25 +0000288 if (DEBUG)
289 log.trace("Wrap class reference {}", number);
Nils Diewald92729ce2014-10-06 16:00:17 +0000290
Akronbb5d1732015-06-22 01:22:40 +0200291 return new SpanFocusQueryWrapper(
292 this._fromJson(operands.get(0)), number);
Nils Diewaldc925b492013-12-03 23:56:10 +0000293
Nils Diewaldbb33da22015-03-04 16:24:25 +0000294 case "koral:token":
Akronc63697c2015-06-17 22:32:02 +0200295
Nils Diewaldbb33da22015-03-04 16:24:25 +0000296 // The token is empty and should be treated like []
297 if (!json.has("wrap"))
298 return new SpanRepetitionQueryWrapper();
299
300 // Get wrapped token
301 return this._segFromJson(json.get("wrap"));
302
303 case "koral:span":
Akron2ac6e532015-06-27 18:23:32 +0200304 if (!json.has("wrap"))
305 return this._termFromJson(json);
306
Akron907d3d72015-06-30 11:32:42 +0200307 // This is an ugly hack
308 return this._termFromJson(json.get("wrap"), "<>:");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000309 };
Nils Diewald6d50c1f2013-12-04 20:14:08 +0000310
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000311 // Unknown query type
312 throw new QueryException(713, "Query type is not supported");
313 };
314
Nils Diewald6409a922015-01-29 20:50:42 +0000315
Nils Diewald8904c1d2015-02-26 16:13:18 +0000316 /**
Nils Diewald21914ff2015-02-28 02:09:47 +0000317 * <p>
Nils Diewald8904c1d2015-02-26 16:13:18 +0000318 * Get the associated {@link QueryBuilder} object
319 * for query building.
Nils Diewald21914ff2015-02-28 02:09:47 +0000320 * </p>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000321 *
Nils Diewald21914ff2015-02-28 02:09:47 +0000322 * <blockquote><pre>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000323 * SpanQueryWrapper query = new
324 * KrillQuery("tokens").builder().re("mate/p=N.*");
Nils Diewald21914ff2015-02-28 02:09:47 +0000325 * </pre></blockquote>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000326 *
Nils Diewald21914ff2015-02-28 02:09:47 +0000327 * @return The {@link QueryBuilder}.
Nils Diewald8904c1d2015-02-26 16:13:18 +0000328 */
329 public QueryBuilder builder () {
330 if (this.builder == null)
331 this.builder = new QueryBuilder(this.field);
332 return this.builder;
333 };
334
335
336 /**
337 * Return the associated KoralQuery query object
338 * as a {@link JsonNode}. This won't work,
339 * if the object was build using a {@link QueryBuilder},
340 * therefore it is limited to mirror a deserialized KoralQuery
341 * object.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000342 *
Nils Diewald8904c1d2015-02-26 16:13:18 +0000343 * @return The {@link JsonNode} representing the query object
344 * of a deserialized KoralQuery object.
345 */
346 public JsonNode toJsonNode () {
347 return this.json;
348 };
349
350
351 /**
352 * Return the associated KoralQuery query object
353 * as a JSON string. This won't work,
354 * if the object was build using a {@link QueryBuilder},
355 * therefore it is limited to mirror a deserialized KoralQuery
356 * object.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000357 *
Nils Diewald8904c1d2015-02-26 16:13:18 +0000358 * @return A JSON string representing the query object
359 * of a deserialized KoralQuery object.
360 */
361 public String toJsonString () {
362 if (this.json == null)
363 return "{}";
364 return this.json.toString();
365 };
366
367
Nils Diewaldcec40f92015-02-19 22:20:02 +0000368 // Deserialize koral:group
Nils Diewaldbb33da22015-03-04 16:24:25 +0000369 private SpanQueryWrapper _groupFromJson (JsonNode json)
370 throws QueryException {
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000371
372 // No operation
373 if (!json.has("operation"))
374 throw new QueryException(703, "Group expects operation");
375
376 // Get operation
377 String operation = json.get("operation").asText();
378
Nils Diewaldbb33da22015-03-04 16:24:25 +0000379 if (DEBUG)
380 log.trace("Found {} group", operation);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000381
382 if (!json.has("operands"))
383 throw new QueryException(704, "Operation needs operand list");
384
385 // Get all operands
386 JsonNode operands = json.get("operands");
387
388 if (operands == null || !operands.isArray())
389 throw new QueryException(704, "Operation needs operand list");
390
Nils Diewaldbb33da22015-03-04 16:24:25 +0000391 if (DEBUG)
392 log.trace("Operands are {}", operands);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000393
Akronbb5d1732015-06-22 01:22:40 +0200394 SpanQueryWrapper spanReferenceQueryWrapper = _operationReferenceFromJSON(
395 json, operands);
margaretha144983d2015-05-07 11:52:17 +0200396 if (spanReferenceQueryWrapper != null) {
397 return spanReferenceQueryWrapper;
398 }
399
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000400 // Branch on operation
401 switch (operation) {
Akron40550172015-08-04 03:06:12 +0200402 case "operation:junction":
403 return this._operationJunctionFromJson(operands);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000404
Akron40550172015-08-04 03:06:12 +0200405 case "operation:position":
406 return this._operationPositionFromJson(json, operands);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000407
Akron40550172015-08-04 03:06:12 +0200408 case "operation:sequence":
409 return this._operationSequenceFromJson(json, operands);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000410
Akron40550172015-08-04 03:06:12 +0200411 case "operation:class":
412 return this._operationClassFromJson(json, operands);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000413
Akron40550172015-08-04 03:06:12 +0200414 case "operation:repetition":
415 return this._operationRepetitionFromJson(json, operands);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000416
Akron40550172015-08-04 03:06:12 +0200417 case "operation:relation":
margaretha29521f92016-04-18 15:20:02 +0200418 // if (!json.has("relType")) {
419 // throw new QueryException(717,
420 // "Missing relation node");
421 // }
422 if (json.has("relType"))
423 return _operationRelationFromJson(operands,
424 json.get("relType"));
425 else if (json.has("relation")) {
426 return _operationRelationFromJson(operands,
427 json.get("relation"));
428 }
429 else {
Akron40550172015-08-04 03:06:12 +0200430 throw new QueryException(717, "Missing relation node");
431 }
Akron40550172015-08-04 03:06:12 +0200432 /*throw new QueryException(765,
433 "Relations are currently not supported");*/
Nils Diewaldbb33da22015-03-04 16:24:25 +0000434
Akron40550172015-08-04 03:06:12 +0200435 case "operation:or": // Deprecated in favor of operation:junction
436 return this._operationJunctionFromJson(operands);
437 /*
438 case "operation:submatch": // Deprecated in favor of koral:reference
439 return this._operationSubmatchFromJson(json, operands);
440 */
441 case "operation:disjunction":
442 return this._operationJunctionFromJson(operands);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000443 };
444
445 // Unknown
446 throw new QueryException(711, "Unknown group operation");
Nils Diewaldc925b492013-12-03 23:56:10 +0000447 };
448
Akronbb5d1732015-06-22 01:22:40 +0200449
450 private SpanQueryWrapper _operationReferenceFromJSON (JsonNode node,
451 JsonNode operands) throws QueryException {
margaretha144983d2015-05-07 11:52:17 +0200452 boolean isReference = false;
453 int classNum = -1;
454 int refOperandNum = -1;
455 JsonNode childNode;
456
457 for (int i = 0; i < operands.size(); i++) {
458 childNode = operands.get(i);
459 if (childNode.has("@type")
460 && childNode.get("@type").asText()
461 .equals("koral:reference")
462 && childNode.has("operation")
463 && childNode.get("operation").asText()
464 .equals("operation:focus")
465 && !childNode.has("operands")) {
466
467 if (childNode.has("classRef")) {
468 classNum = childNode.get("classRef").get(0).asInt();
469 refOperandNum = i;
470 isReference = true;
471 break;
472 }
473 }
474 }
475
476 if (isReference) {
477 JsonNode resolvedNode = _resolveReference(node, operands,
478 refOperandNum, classNum);
Akrond504f212015-06-20 00:27:54 +0200479 return new SpanReferenceQueryWrapper(this._fromJson(resolvedNode),
margaretha144983d2015-05-07 11:52:17 +0200480 (byte) classNum);
481 }
482
483 return null;
484 }
485
Akronbb5d1732015-06-22 01:22:40 +0200486
487 private JsonNode _resolveReference (JsonNode node, JsonNode operands,
margaretha144983d2015-05-07 11:52:17 +0200488 int refOperandNum, int classNum) throws QueryException {
489 JsonNode referent = null;
490 ObjectMapper m = new ObjectMapper();
491 ArrayNode newOperands = m.createArrayNode();
492 boolean isReferentFound = false;
493 for (int i = 0; i < operands.size(); i++) {
494 if (i != refOperandNum) {
495 if (!isReferentFound) {
496 referent = _extractReferentClass(operands.get(i), classNum);
Akronbb5d1732015-06-22 01:22:40 +0200497 if (referent != null)
498 isReferentFound = true;
margaretha144983d2015-05-07 11:52:17 +0200499 }
500 newOperands.insert(i, operands.get(i));
501 }
502 }
503
504 if (isReferentFound) {
505 newOperands.insert(refOperandNum, referent);
506 ((ObjectNode) node).set("operands", newOperands);
507 return node;
508 }
509 else
510 throw new QueryException("Referent node is not found");
511
512 }
513
Akronbb5d1732015-06-22 01:22:40 +0200514
515 private JsonNode _extractReferentClass (JsonNode node, int classNum) {
margaretha144983d2015-05-07 11:52:17 +0200516 JsonNode referent;
517 if (node.has("classOut") && node.get("classOut").asInt() == classNum) {
margaretha144983d2015-05-07 11:52:17 +0200518 return node;
519 }
520 else {
521 if (node.has("operands") && node.get("operands").isArray()) {
522 for (JsonNode childOperand : node.get("operands")) {
523 referent = _extractReferentClass(childOperand, classNum);
524 if (referent != null) {
525 return referent;
526 }
527 }
528 }
529 }
530 return null;
531 }
532
margarethab097bac2015-04-15 11:37:02 +0200533
534 private SpanQueryWrapper _operationRelationFromJson (JsonNode operands,
535 JsonNode relation) throws QueryException {
margaretha5c26de42015-04-14 12:47:58 +0200536
537 if (operands.size() < 2) {
538 throw new QueryException(705,
539 "Number of operands is not acceptable");
540 }
541
Akrond504f212015-06-20 00:27:54 +0200542 SpanQueryWrapper operand1 = this._fromJson(operands.get(0));
543 SpanQueryWrapper operand2 = this._fromJson(operands.get(1));
Akronbb5d1732015-06-22 01:22:40 +0200544
margarethaf70addb2015-04-27 13:17:18 +0200545 String direction = ">:";
546 if (operand1.isEmpty() && !operand2.isEmpty()) {
margaretha5c26de42015-04-14 12:47:58 +0200547 direction = "<:";
548 }
margaretha5c26de42015-04-14 12:47:58 +0200549
margarethaf70addb2015-04-27 13:17:18 +0200550 if (!relation.has("@type"))
551 throw new QueryException(701,
552 "JSON-LD group has no @type attribute");
Akronbb5d1732015-06-22 01:22:40 +0200553
margarethaf70addb2015-04-27 13:17:18 +0200554 if (relation.get("@type").asText().equals("koral:relation")) {
555 if (!relation.has("wrap")) {
556 throw new QueryException(718, "Missing relation term");
557 }
558 SpanQueryWrapper relationWrapper = _termFromJson(
Akronbb5d1732015-06-22 01:22:40 +0200559 relation.get("wrap"), direction);
margarethaf70addb2015-04-27 13:17:18 +0200560 return new SpanRelationWrapper(relationWrapper, operand1, operand2);
561 }
562 else {
563 throw new QueryException(713, "Query type is not supported");
564 }
margaretha5c26de42015-04-14 12:47:58 +0200565 }
Nils Diewaldf399a672013-11-18 17:55:22 +0000566
margarethab097bac2015-04-15 11:37:02 +0200567
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000568 // Deserialize operation:junction
569 private SpanQueryWrapper _operationJunctionFromJson (JsonNode operands)
Nils Diewaldbb33da22015-03-04 16:24:25 +0000570 throws QueryException {
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000571 SpanAlterQueryWrapper ssaq = new SpanAlterQueryWrapper(this.field);
572 for (JsonNode operand : operands) {
Akrond504f212015-06-20 00:27:54 +0200573 ssaq.or(this._fromJson(operand));
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000574 };
575 return ssaq;
576 };
Nils Diewaldc86aa482014-02-12 16:58:05 +0000577
Nils Diewald6409a922015-01-29 20:50:42 +0000578
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000579 // Deserialize operation:position
Nils Diewaldbb33da22015-03-04 16:24:25 +0000580 private SpanQueryWrapper _operationPositionFromJson (JsonNode json,
581 JsonNode operands) throws QueryException {
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000582 if (operands.size() != 2)
Nils Diewaldbb33da22015-03-04 16:24:25 +0000583 throw new QueryException(705,
584 "Number of operands is not acceptable");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000585
Nils Diewald93d6d1b2015-02-02 21:47:43 +0000586 String frame = "isAround";
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000587 // Temporary workaround for wrongly set overlaps
588 if (json.has("frames")) {
589 JsonNode frameN = json.get("frames");
590 if (frameN.isArray()) {
591 frameN = json.get("frames").get(0);
592 if (frameN != null && frameN.isValueNode())
593 frame = frameN.asText().substring(7);
594 };
595 }
596 // <legacyCode>
597 else if (json.has("frame")) {
Nils Diewald93d6d1b2015-02-02 21:47:43 +0000598 this.addMessage(0, "Frame is deprecated");
599
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000600 JsonNode frameN = json.get("frame");
601 if (frameN != null && frameN.isValueNode())
602 frame = frameN.asText().substring(6);
603 };
604 // </legacyCode>
605
Nils Diewaldbb33da22015-03-04 16:24:25 +0000606 if (DEBUG)
607 log.trace("Position frame is '{}'", frame);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000608
609 // Byte flag - should cover all 13 cases, i.e. two bytes long
610 byte flag = WITHIN;
611 switch (frame) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000612 case "isAround":
613 break;
614 case "strictlyContains":
615 flag = REAL_WITHIN;
616 break;
617 case "isWithin":
618 break;
619 case "startsWith":
620 flag = STARTSWITH;
621 break;
622 case "endsWith":
623 flag = ENDSWITH;
624 break;
625 case "matches":
626 flag = MATCH;
627 break;
628 case "overlaps":
629 flag = OVERLAP;
630 this.addWarning(769,
631 "Overlap variant currently interpreted as overlap");
632 break;
633 case "overlapsLeft":
634 // Temporary workaround
635 this.addWarning(769,
636 "Overlap variant currently interpreted as overlap");
637 flag = OVERLAP;
638 break;
639 case "overlapsRight":
640 // Temporary workaround
641 this.addWarning(769,
642 "Overlap variant currently interpreted as overlap");
643 flag = OVERLAP;
644 break;
645 case "strictlyOverlaps":
646 flag = REAL_OVERLAP;
647 break;
Nils Diewald93d6d1b2015-02-02 21:47:43 +0000648
649 // alignsLeft
650
Nils Diewaldbb33da22015-03-04 16:24:25 +0000651 default:
652 throw new QueryException(706, "Frame type is unknown");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000653 };
Nils Diewaldbb33da22015-03-04 16:24:25 +0000654
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000655 // The exclusion operator is no longer relevant
656 // <legacyCode>
657 Boolean exclude;
658 if (json.has("exclude") && json.get("exclude").asBoolean()) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000659 throw new QueryException(760,
660 "Exclusion is currently not supported in position operations");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000661 };
662 // </legacyCode>
663
664 // Create SpanWithin Query
Akrond504f212015-06-20 00:27:54 +0200665 return new SpanWithinQueryWrapper(this._fromJson(operands.get(0)),
666 this._fromJson(operands.get(1)), flag);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000667 };
668
Nils Diewald6409a922015-01-29 20:50:42 +0000669
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000670 // Deserialize operation:repetition
Nils Diewaldbb33da22015-03-04 16:24:25 +0000671 private SpanQueryWrapper _operationRepetitionFromJson (JsonNode json,
672 JsonNode operands) throws QueryException {
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000673
674 if (operands.size() != 1)
Nils Diewaldbb33da22015-03-04 16:24:25 +0000675 throw new QueryException(705,
676 "Number of operands is not acceptable");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000677
Nils Diewald6409a922015-01-29 20:50:42 +0000678 int min = 0, max = 100;
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000679
680 if (json.has("boundary")) {
681 Boundary b = new Boundary(json.get("boundary"), 0, 100);
682 min = b.min;
683 max = b.max;
684 }
685 // <legacyCode>
686 else {
687 this.addMessage(0, "Setting boundary by min and max is deprecated");
688
689 // Set minimum value
690 if (json.has("min"))
691 min = json.get("min").asInt(0);
692
693 // Set maximum value
694 if (json.has("max"))
695 max = json.get("max").asInt(100);
696 };
697 // </legacyCode>
698
699 // Sanitize max
700 if (max < 0)
701 max = 100;
702 else if (max > 100)
703 max = 100;
704
705 // Sanitize min
706 if (min < 0)
707 min = 0;
708 else if (min > 100)
709 min = 100;
Nils Diewaldbb33da22015-03-04 16:24:25 +0000710
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000711 // Check relation between min and max
712 if (min > max)
713 max = max;
714
Akrond504f212015-06-20 00:27:54 +0200715 SpanQueryWrapper sqw = this._fromJson(operands.get(0));
Nils Diewaldbb33da22015-03-04 16:24:25 +0000716
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000717 if (sqw.maybeExtension())
718 return sqw.setMin(min).setMax(max);
719
720 return new SpanRepetitionQueryWrapper(sqw, min, max);
721 };
722
723
724 // Deserialize operation:submatch
725 @Deprecated
Nils Diewaldbb33da22015-03-04 16:24:25 +0000726 private SpanQueryWrapper _operationSubmatchFromJson (JsonNode json,
727 JsonNode operands) throws QueryException {
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000728
729 int number = 1;
730
731 this.addMessage(0, "operation:submatch is deprecated");
732
733 if (operands.size() != 1)
Nils Diewaldbb33da22015-03-04 16:24:25 +0000734 throw new QueryException(705,
735 "Number of operands is not acceptable");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000736
Nils Diewald6409a922015-01-29 20:50:42 +0000737 // Use class reference
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000738 if (json.has("classRef")) {
739 if (json.has("classRefOp")) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000740 throw new QueryException(761,
741 "Class reference operators are currently not supported");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000742 };
743
744 number = json.get("classRef").get(0).asInt();
745 }
Nils Diewald6409a922015-01-29 20:50:42 +0000746
747 // Use span reference
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000748 else if (json.has("spanRef")) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000749 throw new QueryException(762,
750 "Span references are currently not supported");
751 };
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000752
Akronbb5d1732015-06-22 01:22:40 +0200753 return new SpanFocusQueryWrapper(this._fromJson(operands.get(0)),
754 number);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000755 };
756
757
758 // Deserialize operation:class
Nils Diewaldbb33da22015-03-04 16:24:25 +0000759 private SpanQueryWrapper _operationClassFromJson (JsonNode json,
760 JsonNode operands) throws QueryException {
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000761 int number = 1;
762
763 // Too many operands
764 if (operands.size() != 1)
Nils Diewaldbb33da22015-03-04 16:24:25 +0000765 throw new QueryException(705,
766 "Number of operands is not acceptable");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000767
768 // Get class number
769 if (json.has("classOut")) {
770 number = json.get("classOut").asInt(0);
771 }
772 // <legacyCode>
773 else if (json.has("class")) {
Nils Diewald93d6d1b2015-02-02 21:47:43 +0000774 this.addMessage(0, "Class is deprecated");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000775 number = json.get("class").asInt(0);
776 };
777 // </legacyCode>
778
779 // Class reference check
780 if (json.has("classRefCheck")) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000781 this.addWarning(764, "Class reference checks are currently "
782 + "not supported - results may not be correct");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000783 };
784
785 // Class reference operation
786 // This has to be done after class ref check
787 if (json.has("classRefOp")) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000788 throw new QueryException(761,
789 "Class reference operators are currently not supported");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000790 };
791
792 // Number is set
793 if (number > 0) {
794 if (operands.size() != 1) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000795 throw new QueryException(705,
796 "Number of operands is not acceptable");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000797 };
Nils Diewaldbb33da22015-03-04 16:24:25 +0000798
799 if (DEBUG)
800 log.trace("Found Class definition for {}", number);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000801
802 if (number > MAX_CLASS_NUM) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000803 throw new QueryException(709, "Valid class numbers exceeded");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000804 };
805
806 // Serialize operand
Akrond504f212015-06-20 00:27:54 +0200807 SpanQueryWrapper sqw = this._fromJson(operands.get(0));
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000808
809 // Problematic
Nils Diewaldbb33da22015-03-04 16:24:25 +0000810 if (sqw.maybeExtension())
811 return sqw.setClassNumber(number);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000812
813 return new SpanClassQueryWrapper(sqw, number);
814 };
815
816 throw new QueryException(710, "Class attribute missing");
817 };
818
819
820 // Deserialize operation:sequence
Nils Diewaldbb33da22015-03-04 16:24:25 +0000821 private SpanQueryWrapper _operationSequenceFromJson (JsonNode json,
822 JsonNode operands) throws QueryException {
Nils Diewald6409a922015-01-29 20:50:42 +0000823
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000824 // Sequence with only one operand
825 if (operands.size() == 1)
Akrond504f212015-06-20 00:27:54 +0200826 return this._fromJson(operands.get(0));
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000827
Nils Diewald8904c1d2015-02-26 16:13:18 +0000828 SpanSequenceQueryWrapper sseqqw = this.builder().seq();
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000829
830 // Say if the operand order is important
831 if (json.has("inOrder"))
832 sseqqw.setInOrder(json.get("inOrder").asBoolean());
833
834 // Introduce distance constraints
835 // ATTENTION: Distances have to be set before segments are added
836 if (json.has("distances")) {
837
838 // THIS IS NO LONGER NECESSARY, AS IT IS COVERED BY FRAMES
839 if (json.has("exclude") && json.get("exclude").asBoolean()) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000840 throw new QueryException(763,
841 "Excluding distance constraints are currently not supported");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000842 };
843
844 if (!json.get("distances").isArray()) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000845 throw new QueryException(707,
846 "Distance Constraints have to be defined as arrays");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000847 };
848
849 // TEMPORARY: Workaround for group distances
850 JsonNode firstDistance = json.get("distances").get(0);
Nils Diewaldbb33da22015-03-04 16:24:25 +0000851
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000852 if (!firstDistance.has("@type")) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000853 throw new QueryException(701,
854 "JSON-LD group has no @type attribute");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000855 };
856
857 JsonNode distances;
Nils Diewaldcec40f92015-02-19 22:20:02 +0000858 if (firstDistance.get("@type").asText().equals("koral:group")) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000859 if (!firstDistance.has("operands")
860 || !firstDistance.get("operands").isArray())
861 throw new QueryException(704,
862 "Operation needs operand list");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000863
864 distances = firstDistance.get("operands");
865 }
866
867 // Support korap distances
Akron784d13d2015-06-20 15:13:16 +0200868 // TODO: Support cosmas distances
Nils Diewaldbb33da22015-03-04 16:24:25 +0000869 else if (firstDistance.get("@type").asText()
870 .equals("koral:distance")
871 || firstDistance.get("@type").asText()
872 .equals("cosmas:distance")) {
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000873 distances = json.get("distances");
874 }
875
876 else
877 throw new QueryException(708, "No valid distances defined");
878
879 // Add all distance constraint to query
880 for (JsonNode constraint : distances) {
881 String unit = "w";
882 if (constraint.has("key"))
883 unit = constraint.get("key").asText();
Nils Diewaldbb33da22015-03-04 16:24:25 +0000884
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000885 // There is a maximum of 100 fix
886 int min = 0, max = 100;
887 if (constraint.has("boundary")) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000888 Boundary b = new Boundary(constraint.get("boundary"), 0,
889 100);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000890 min = b.min;
891 max = b.max;
892 }
Akron784d13d2015-06-20 15:13:16 +0200893
894 // <legacy>
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000895 else {
896 if (constraint.has("min"))
897 min = constraint.get("min").asInt(0);
898 if (constraint.has("max"))
899 max = constraint.get("max").asInt(100);
900 };
Akron784d13d2015-06-20 15:13:16 +0200901 // </legacy>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000902
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000903 // Add foundry and layer to the unit for new indices
Nils Diewaldbb33da22015-03-04 16:24:25 +0000904 if (constraint.has("foundry") && constraint.has("layer")
905 && constraint.get("foundry").asText().length() > 0
906 && constraint.get("layer").asText().length() > 0) {
907
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000908 StringBuilder value = new StringBuilder();
909 value.append(constraint.get("foundry").asText());
910 value.append('/');
911 value.append(constraint.get("layer").asText());
912 value.append(':').append(unit);
913 unit = value.toString();
Akrond2926902016-02-13 18:37:36 +0100914 }
915
916 // Use default foundry and layer - currently only base is supported!
917 else if (unit.equals("s") || unit.equals("p")) {
918 StringBuilder value = new StringBuilder();
919 unit = value.append("base/s:").append(unit).toString();
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000920 };
Nils Diewaldbb33da22015-03-04 16:24:25 +0000921
Akron4e2bc0e2016-02-17 12:23:46 +0100922 // Workaround for koral:distance vs cosmas:distance
923 if (constraint.get("@type").asText().equals("koral:distance")) {
924 min++;
925 max++;
926 };
927
Akron4d5fe192016-02-16 00:22:14 +0100928 // Set distance exclusion
929 Boolean exclusion = false;
930 if (constraint.has("exclude"))
931 exclusion = constraint.get("exclude").asBoolean();
932
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000933 // Sanitize boundary
Nils Diewaldbb33da22015-03-04 16:24:25 +0000934 if (max < min)
935 max = min;
936
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000937 if (DEBUG)
Nils Diewaldbb33da22015-03-04 16:24:25 +0000938 log.trace("Add distance constraint of '{}': {}-{}", unit,
939 min, max);
940
Akron4d5fe192016-02-16 00:22:14 +0100941 sseqqw.withConstraint(min, max, unit, exclusion);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000942 };
943 };
944
945 // Add segments to sequence
946 for (JsonNode operand : operands) {
Akrond504f212015-06-20 00:27:54 +0200947 sseqqw.append(this._fromJson(operand));
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000948 };
949
950 // inOrder was set to false without a distance constraint
951 if (!sseqqw.isInOrder() && !sseqqw.hasConstraints()) {
Akron784d13d2015-06-20 15:13:16 +0200952 if (DEBUG)
953 log.trace("Add distance constraint - for the normal inorder case");
954
Nils Diewald6409a922015-01-29 20:50:42 +0000955 sseqqw.withConstraint(1, 1, "w");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000956 };
957
958 return sseqqw;
959 };
960
961
Nils Diewaldcec40f92015-02-19 22:20:02 +0000962 // Deserialize koral:token
Eliza Margarethab1a5ed42015-01-26 10:37:12 +0000963 private SpanQueryWrapper _segFromJson (JsonNode json) throws QueryException {
Akronc63697c2015-06-17 22:32:02 +0200964
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000965 if (!json.has("@type"))
Nils Diewaldbb33da22015-03-04 16:24:25 +0000966 throw new QueryException(701,
967 "JSON-LD group has no @type attribute");
Nils Diewald33fcb5d2014-11-07 23:27:03 +0000968
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000969 String type = json.get("@type").asText();
Nils Diewald33fcb5d2014-11-07 23:27:03 +0000970
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000971 if (DEBUG)
972 log.trace("Wrap new token definition by {}", type);
Nils Diewald1455e1e2014-08-01 16:12:43 +0000973
Nils Diewald6409a922015-01-29 20:50:42 +0000974 // Branch on type
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000975 switch (type) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000976 case "koral:term":
977 // String match = "match:eq";
978 // if (json.has("match"))
979 // match = json.get("match").asText();
980 //
981 // switch (match) {
982 //
983 // case "match:ne":
984 // if (DEBUG)
985 // log.trace("Term is negated");
986 //
987 // SpanSegmentQueryWrapper ssqw =
988 // (SpanSegmentQueryWrapper) this._termFromJson(json);
989 //
990 // ssqw.makeNegative();
991 //
992 // return this.seg().without(ssqw);
993 //
994 // case "match:eq":
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000995 return this._termFromJson(json);
Nils Diewaldbb33da22015-03-04 16:24:25 +0000996 // };
997 //
998 // throw new QueryException(741, "Match relation unknown");
Nils Diewald26087ea2013-12-05 16:51:30 +0000999
Nils Diewaldbb33da22015-03-04 16:24:25 +00001000 case "koral:termGroup":
Nils Diewald26087ea2013-12-05 16:51:30 +00001001
Nils Diewaldbb33da22015-03-04 16:24:25 +00001002 if (!json.has("operands"))
1003 throw new QueryException(742,
1004 "Term group needs operand list");
Nils Diewald26087ea2013-12-05 16:51:30 +00001005
Nils Diewaldbb33da22015-03-04 16:24:25 +00001006 // Get operands
1007 JsonNode operands = json.get("operands");
Nils Diewaldc86aa482014-02-12 16:58:05 +00001008
Nils Diewaldbb33da22015-03-04 16:24:25 +00001009 SpanSegmentQueryWrapper ssegqw = this.builder().seg();
Nils Diewaldc86aa482014-02-12 16:58:05 +00001010
Nils Diewaldbb33da22015-03-04 16:24:25 +00001011 if (!json.has("relation"))
1012 throw new QueryException(743,
1013 "Term group expects a relation");
Nils Diewald1220e3e2014-11-08 03:18:58 +00001014
Nils Diewaldbb33da22015-03-04 16:24:25 +00001015 switch (json.get("relation").asText()) {
1016 case "relation:and":
Eliza Margarethab1a5ed42015-01-26 10:37:12 +00001017
Nils Diewaldbb33da22015-03-04 16:24:25 +00001018 for (JsonNode operand : operands) {
1019 SpanQueryWrapper part = this._segFromJson(operand);
1020 if (part instanceof SpanAlterQueryWrapper) {
1021 ssegqw.with((SpanAlterQueryWrapper) part);
1022 }
1023 else if (part instanceof SpanRegexQueryWrapper) {
1024 ssegqw.with((SpanRegexQueryWrapper) part);
1025 }
1026 else if (part instanceof SpanSegmentQueryWrapper) {
1027 ssegqw.with((SpanSegmentQueryWrapper) part);
1028 }
1029 else {
1030 throw new QueryException(744,
1031 "Operand not supported in term group");
1032 };
1033 }
1034 ;
1035 return ssegqw;
Eliza Margarethab1a5ed42015-01-26 10:37:12 +00001036
Nils Diewaldbb33da22015-03-04 16:24:25 +00001037 case "relation:or":
1038
1039 SpanAlterQueryWrapper ssaq = new SpanAlterQueryWrapper(
1040 this.field);
1041 for (JsonNode operand : operands) {
1042 ssaq.or(this._segFromJson(operand));
1043 }
1044 ;
1045 return ssaq;
1046 }
1047 ;
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001048 };
Nils Diewaldbb33da22015-03-04 16:24:25 +00001049 throw new QueryException(745, "Token type is not supported");
Nils Diewald4d183ea2013-12-05 02:51:38 +00001050 };
Nils Diewald4d183ea2013-12-05 02:51:38 +00001051
margarethab097bac2015-04-15 11:37:02 +02001052
margaretha5c26de42015-04-14 12:47:58 +02001053 private SpanQueryWrapper _termFromJson (JsonNode json)
1054 throws QueryException {
1055 return _termFromJson(json, null);
1056 }
Nils Diewald4d183ea2013-12-05 02:51:38 +00001057
margarethab097bac2015-04-15 11:37:02 +02001058
Nils Diewaldcec40f92015-02-19 22:20:02 +00001059 // Deserialize koral:term
Akron907d3d72015-06-30 11:32:42 +02001060 // TODO: Not optimal as it does not respect non-term
margarethab097bac2015-04-15 11:37:02 +02001061 private SpanQueryWrapper _termFromJson (JsonNode json, String direction)
Nils Diewaldbb33da22015-03-04 16:24:25 +00001062 throws QueryException {
Akrond2ddac92016-02-13 16:56:21 +01001063
Nils Diewald6409a922015-01-29 20:50:42 +00001064 if (!json.has("key") || json.get("key").asText().length() < 1) {
Nils Diewaldbb33da22015-03-04 16:24:25 +00001065 if (!json.has("attr"))
1066 throw new QueryException(740,
1067 "Key definition is missing in term or span");
Nils Diewald6409a922015-01-29 20:50:42 +00001068 };
Nils Diewald33fcb5d2014-11-07 23:27:03 +00001069
Nils Diewaldbb33da22015-03-04 16:24:25 +00001070 if (!json.has("@type")) {
1071 throw new QueryException(701,
1072 "JSON-LD group has no @type attribute");
1073 };
1074
1075 Boolean isTerm = json.get("@type").asText().equals("koral:term") ? true
1076 : false;
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001077 Boolean isCaseInsensitive = false;
Nils Diewaldc86aa482014-02-12 16:58:05 +00001078
Akron907d3d72015-06-30 11:32:42 +02001079
1080 // Ugly direction hack
1081 if (direction != null && direction.equals("<>:")) {
1082 isTerm = false;
1083 direction = null;
1084 };
1085
Akrond504f212015-06-20 00:27:54 +02001086 // <legacy>
Nils Diewaldbb33da22015-03-04 16:24:25 +00001087 if (json.has("caseInsensitive")
Akronbb5d1732015-06-22 01:22:40 +02001088 && json.get("caseInsensitive").asBoolean()) {
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001089 isCaseInsensitive = true;
Akron738ed002015-06-18 12:07:50 +02001090 }
Akrond504f212015-06-20 00:27:54 +02001091 // </legacy>
Akron738ed002015-06-18 12:07:50 +02001092
1093 // Flags
1094 else if (json.has("flags") && json.get("flags").isArray()) {
1095 Iterator<JsonNode> flags = json.get("flags").elements();
1096 while (flags.hasNext()) {
Akrond504f212015-06-20 00:27:54 +02001097 String flag = flags.next().asText();
1098 if (flag.equals("flags:caseInsensitive")) {
Akron738ed002015-06-18 12:07:50 +02001099 isCaseInsensitive = true;
Akrond504f212015-06-20 00:27:54 +02001100 }
1101 else {
1102 this.addWarning(748, "Flag is unknown", flag);
Akron738ed002015-06-18 12:07:50 +02001103 };
1104 };
1105 };
Nils Diewaldc86aa482014-02-12 16:58:05 +00001106
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001107 StringBuilder value = new StringBuilder();
Nils Diewaldbb33da22015-03-04 16:24:25 +00001108
Akrona883aa52015-06-18 22:07:31 +02001109 if (direction != null)
margaretha5c26de42015-04-14 12:47:58 +02001110 value.append(direction);
margaretha5c26de42015-04-14 12:47:58 +02001111
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001112 // expect orth? expect lemma?
1113 // s:den | i:den | cnx/l:die | mate/m:mood:ind | cnx/syn:@PREMOD |
1114 // mate/m:number:sg | opennlp/p:ART
Nils Diewaldc86aa482014-02-12 16:58:05 +00001115
Akrona883aa52015-06-18 22:07:31 +02001116 if (json.has("foundry") && json.get("foundry").asText().length() > 0) {
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001117 value.append(json.get("foundry").asText()).append('/');
Akrona883aa52015-06-18 22:07:31 +02001118 };
Nils Diewaldc86aa482014-02-12 16:58:05 +00001119
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001120 // No default foundry defined
1121 if (json.has("layer") && json.get("layer").asText().length() > 0) {
1122 String layer = json.get("layer").asText();
1123 switch (layer) {
Nils Diewaldc86aa482014-02-12 16:58:05 +00001124
Nils Diewaldbb33da22015-03-04 16:24:25 +00001125 case "lemma":
1126 layer = "l";
1127 break;
Nils Diewaldc86aa482014-02-12 16:58:05 +00001128
Nils Diewaldbb33da22015-03-04 16:24:25 +00001129 case "pos":
1130 layer = "p";
1131 break;
Nils Diewald164f8be2014-02-13 02:43:16 +00001132
Nils Diewaldbb33da22015-03-04 16:24:25 +00001133 case "orth":
Akrond2ddac92016-02-13 16:56:21 +01001134 // TODO: THIS IS AN UGLY HACK! AND SHOULD BE NAMED "SURFACE" or . OR *
1135 layer = ".";
Nils Diewaldbb33da22015-03-04 16:24:25 +00001136 break;
Nils Diewald164f8be2014-02-13 02:43:16 +00001137
Nils Diewaldbb33da22015-03-04 16:24:25 +00001138 case "struct":
1139 layer = "s";
1140 break;
Nils Diewald164f8be2014-02-13 02:43:16 +00001141
Nils Diewaldbb33da22015-03-04 16:24:25 +00001142 case "const":
1143 layer = "c";
1144 break;
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001145 };
Nils Diewaldc471b182014-11-19 22:51:15 +00001146
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001147 if (isCaseInsensitive && isTerm) {
Akrond2ddac92016-02-13 16:56:21 +01001148 if (layer.equals("."))
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001149 layer = "i";
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001150 else {
Nils Diewaldbb33da22015-03-04 16:24:25 +00001151 this.addWarning(767,
1152 "Case insensitivity is currently not supported for this layer");
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001153 };
1154 };
Nils Diewaldea7239a2014-11-14 14:01:56 +00001155
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001156 // Ignore foundry for orth layer
Akrond2ddac92016-02-13 16:56:21 +01001157 if (layer.equals(".")) {
Akron13db6152016-02-19 14:08:38 +01001158 layer = "s";
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001159 value.setLength(0);
Akrond2ddac92016-02-13 16:56:21 +01001160 }
1161 else if (layer.equals("i")) {
1162 value.setLength(0);
1163 };
Nils Diewaldc86aa482014-02-12 16:58:05 +00001164
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001165 value.append(layer).append(':');
1166 };
Nils Diewaldc86aa482014-02-12 16:58:05 +00001167
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001168 if (json.has("key") && json.get("key").asText().length() > 0) {
1169 String key = json.get("key").asText();
1170 value.append(isCaseInsensitive ? key.toLowerCase() : key);
1171 };
Nils Diewald3430aa22014-11-07 13:39:03 +00001172
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001173 if (json.has("value") && json.get("value").asText().length() > 0)
1174 value.append(':').append(json.get("value").asText());
Nils Diewaldc86aa482014-02-12 16:58:05 +00001175
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001176 // Regular expression or wildcard
1177 if (isTerm && json.has("type")) {
Nils Diewald6409a922015-01-29 20:50:42 +00001178
Nils Diewald8904c1d2015-02-26 16:13:18 +00001179 QueryBuilder qb = this.builder();
1180
Nils Diewald6409a922015-01-29 20:50:42 +00001181 // Branch on type
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001182 switch (json.get("type").asText()) {
Nils Diewaldbb33da22015-03-04 16:24:25 +00001183 case "type:regex":
1184 return qb.seg(qb.re(value.toString(), isCaseInsensitive));
Nils Diewald6409a922015-01-29 20:50:42 +00001185
Nils Diewaldbb33da22015-03-04 16:24:25 +00001186 case "type:wildcard":
1187 return qb.seq(qb.wc(value.toString(), isCaseInsensitive));
Nils Diewald6409a922015-01-29 20:50:42 +00001188
Nils Diewaldbb33da22015-03-04 16:24:25 +00001189 case "type:string":
1190 break;
Nils Diewald6409a922015-01-29 20:50:42 +00001191
Nils Diewaldbb33da22015-03-04 16:24:25 +00001192 default:
1193 this.addWarning(746,
1194 "Term type is not supported - treated as a string");
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001195 };
1196 };
Nils Diewaldc86aa482014-02-12 16:58:05 +00001197
Nils Diewaldbb33da22015-03-04 16:24:25 +00001198 if (isTerm) {
Eliza Margaretha641b20a2015-02-16 17:23:13 +00001199
Nils Diewaldbb33da22015-03-04 16:24:25 +00001200 String match = "match:eq";
1201 if (json.has("match")) {
1202 match = json.get("match").asText();
1203 }
Eliza Margaretha641b20a2015-02-16 17:23:13 +00001204
Nils Diewaldbb33da22015-03-04 16:24:25 +00001205 SpanSegmentQueryWrapper ssqw = this.builder().seg(value.toString());
1206 if (match.equals("match:ne")) {
1207 if (DEBUG)
1208 log.trace("Term is negated");
1209 ssqw.makeNegative();
1210 return this.builder().seg().without(ssqw);
1211 }
1212 else if (match.equals("match:eq")) {
1213 return ssqw;
1214 }
1215 else {
1216 throw new QueryException(741, "Match relation unknown");
1217 }
Eliza Margaretha641b20a2015-02-16 17:23:13 +00001218 }
Nils Diewaldc471b182014-11-19 22:51:15 +00001219
Eliza Margaretha221349f2015-01-29 17:27:41 +00001220 if (json.has("attr")) {
Nils Diewaldbb33da22015-03-04 16:24:25 +00001221 JsonNode attrNode = json.get("attr");
1222 if (!attrNode.has("@type")) {
1223 throw new QueryException(701,
1224 "JSON-LD group has no @type attribute");
1225 }
Eliza Margaretha221349f2015-01-29 17:27:41 +00001226
Nils Diewaldbb33da22015-03-04 16:24:25 +00001227 if (value.toString().isEmpty()) {
1228 return _createElementAttrFromJson(null, json, attrNode);
1229 // this.addWarning(771,
1230 // "Arbitraty elements with attributes are currently not supported.");
1231 }
1232 else {
1233 SpanQueryWrapper elementWithIdWrapper = this.builder().tag(
1234 value.toString());
1235 if (elementWithIdWrapper == null) {
1236 return null;
1237 }
1238 return _createElementAttrFromJson(elementWithIdWrapper, json,
1239 attrNode);
1240 }
Nils Diewald6409a922015-01-29 20:50:42 +00001241 };
Nils Diewald8904c1d2015-02-26 16:13:18 +00001242 return this.builder().tag(value.toString());
Nils Diewaldc86aa482014-02-12 16:58:05 +00001243 };
1244
Nils Diewald8904c1d2015-02-26 16:13:18 +00001245
1246 // Deserialize elements with attributes
Nils Diewaldbb33da22015-03-04 16:24:25 +00001247 private SpanQueryWrapper _createElementAttrFromJson (
1248 SpanQueryWrapper elementWithIdWrapper, JsonNode json,
1249 JsonNode attrNode) throws QueryException {
Eliza Margaretha1969c2b2015-02-23 15:13:03 +00001250
Nils Diewaldbb33da22015-03-04 16:24:25 +00001251 if (attrNode.get("@type").asText().equals("koral:term")) {
1252 SpanQueryWrapper attrWrapper = _attrFromJson(json.get("attr"));
1253 if (attrWrapper != null) {
1254 if (elementWithIdWrapper != null) {
1255 return new SpanWithAttributeQueryWrapper(
1256 elementWithIdWrapper, attrWrapper);
1257 }
1258 else {
1259 return new SpanWithAttributeQueryWrapper(attrWrapper);
1260 }
1261 }
1262 else {
1263 throw new QueryException(747, "Attribute is null");
1264 }
1265 }
Nils Diewald8904c1d2015-02-26 16:13:18 +00001266 else if (attrNode.get("@type").asText().equals("koral:termGroup")) {
Nils Diewaldbb33da22015-03-04 16:24:25 +00001267 return _handleAttrGroup(elementWithIdWrapper, attrNode);
1268 }
1269 else {
1270 this.addWarning(715, "Attribute type is not supported");
1271 }
1272 return elementWithIdWrapper;
1273 }
Nils Diewald8904c1d2015-02-26 16:13:18 +00001274
1275
1276 // Deserialize attribute groups
Nils Diewaldbb33da22015-03-04 16:24:25 +00001277 private SpanQueryWrapper _handleAttrGroup (
1278 SpanQueryWrapper elementWithIdWrapper, JsonNode attrNode)
1279 throws QueryException {
1280 if (!attrNode.has("relation")) {
1281 throw new QueryException(743, "Term group expects a relation");
1282 }
1283 if (!attrNode.has("operands")) {
1284 throw new QueryException(742, "Term group needs operand list");
1285 }
Eliza Margaretha1969c2b2015-02-23 15:13:03 +00001286
Nils Diewaldbb33da22015-03-04 16:24:25 +00001287 String relation = attrNode.get("relation").asText();
1288 JsonNode operands = attrNode.get("operands");
Eliza Margaretha1969c2b2015-02-23 15:13:03 +00001289
Nils Diewaldbb33da22015-03-04 16:24:25 +00001290 SpanQueryWrapper attrWrapper;
1291 if ("relation:and".equals(relation)) {
1292 List<SpanQueryWrapper> wrapperList = new ArrayList<SpanQueryWrapper>();
1293 for (JsonNode operand : operands) {
1294 attrWrapper = _termFromJson(operand);
1295 if (attrWrapper == null) {
1296 throw new QueryException(747, "Attribute is null");
1297 }
1298 wrapperList.add(attrWrapper);
1299 }
Eliza Margaretha1969c2b2015-02-23 15:13:03 +00001300
Nils Diewaldbb33da22015-03-04 16:24:25 +00001301 if (elementWithIdWrapper != null) {
1302 return new SpanWithAttributeQueryWrapper(elementWithIdWrapper,
1303 wrapperList);
1304 }
1305 else {
1306 return new SpanWithAttributeQueryWrapper(wrapperList);
1307 }
1308 }
1309 else if ("relation:or".equals(relation)) {
1310 SpanAlterQueryWrapper saq = new SpanAlterQueryWrapper(field);
1311 SpanWithAttributeQueryWrapper saqw;
1312 for (JsonNode operand : operands) {
1313 attrWrapper = _termFromJson(operand);
1314 if (attrWrapper == null) {
1315 throw new QueryException(747, "Attribute is null");
1316 }
1317 if (elementWithIdWrapper != null) {
1318 saqw = new SpanWithAttributeQueryWrapper(
1319 elementWithIdWrapper, attrWrapper);
1320 }
1321 else {
1322 saqw = new SpanWithAttributeQueryWrapper(attrWrapper);
1323 }
1324 saq.or(saqw);
1325 }
1326 return saq;
1327 }
1328 else {
1329 throw new QueryException(716, "Unknown relation");
1330 }
1331 }
1332
Nils Diewald6409a922015-01-29 20:50:42 +00001333
Nils Diewald83c9b162015-02-03 21:05:07 +00001334 // Get attributes from a json termgroup
Nils Diewald6409a922015-01-29 20:50:42 +00001335 private SpanQueryWrapper _attrFromJson (JsonNode attrNode)
Nils Diewaldbb33da22015-03-04 16:24:25 +00001336 throws QueryException {
Eliza Margaretha221349f2015-01-29 17:27:41 +00001337
Nils Diewaldbb33da22015-03-04 16:24:25 +00001338 if (attrNode.has("key")) {
1339 return _termFromJson(attrNode);
1340 }
1341 else if (attrNode.has("tokenarity") || attrNode.has("arity")) {
1342 this.addWarning(770, "Arity attributes are currently not supported"
1343 + " - results may not be correct");
1344 }
1345 else if (attrNode.has("root")) {
1346 String rootValue = attrNode.get("root").asText();
1347 if (rootValue.equals("true") || rootValue.equals("false")) {
Akronc63697c2015-06-17 22:32:02 +02001348
1349 // TODO: Here do not refer to 'tokens'!!!
margaretha29521f92016-04-18 15:20:02 +02001350 // EM: what should it be? property?
Nils Diewaldbb33da22015-03-04 16:24:25 +00001351 return new SpanAttributeQueryWrapper(
1352 new SpanSimpleQueryWrapper("tokens", "@root",
1353 Boolean.valueOf(rootValue)));
Eliza Margaretha221349f2015-01-29 17:27:41 +00001354 }
1355 }
Nils Diewaldbb33da22015-03-04 16:24:25 +00001356 return null;
Nils Diewaldbbd39a52015-02-23 19:56:57 +00001357 };
Nils Diewaldf399a672013-11-18 17:55:22 +00001358};