blob: d12e47353587681fd87e423bf969fe5c7843e77b [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
Akron626dd9f2016-06-08 10:08:55 +020047 * KrillQuery("tokens").fromKoral("{... 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");
Akron626dd9f2016-06-08 10:08:55 +0200145 * SpanQueryWrapper sqw = kq.fromKoral(
Nils Diewaldbb33da22015-03-04 16:24:25 +0000146 * "{\"@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 */
Akron626dd9f2016-06-08 10:08:55 +0200162 public SpanQueryWrapper fromKoral (String json) throws QueryException {
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000163 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
Akron626dd9f2016-06-08 10:08:55 +0200181 return this.fromKoral(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!
Akron626dd9f2016-06-08 10:08:55 +0200198 public SpanQueryWrapper fromKoral (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;
Akron626dd9f2016-06-08 10:08:55 +0200202 return this._fromKoral(json);
Akrond504f212015-06-20 00:27:54 +0200203 };
204
205
Akron626dd9f2016-06-08 10:08:55 +0200206 private SpanQueryWrapper _fromKoral (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
Akron626dd9f2016-06-08 10:08:55 +0200281 SpanQueryWrapper sqw = this._fromKoral(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(
Akron626dd9f2016-06-08 10:08:55 +0200292 this._fromKoral(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);
Akron626dd9f2016-06-08 10:08:55 +0200479 return new SpanReferenceQueryWrapper(this._fromKoral(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
Akron626dd9f2016-06-08 10:08:55 +0200542 SpanQueryWrapper operand1 = this._fromKoral(operands.get(0));
543 SpanQueryWrapper operand2 = this._fromKoral(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) {
Akron626dd9f2016-06-08 10:08:55 +0200573 ssaq.or(this._fromKoral(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
Akron626dd9f2016-06-08 10:08:55 +0200665 return new SpanWithinQueryWrapper(this._fromKoral(operands.get(0)),
666 this._fromKoral(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
Akron626dd9f2016-06-08 10:08:55 +0200715 SpanQueryWrapper sqw = this._fromKoral(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
Akron626dd9f2016-06-08 10:08:55 +0200753 return new SpanFocusQueryWrapper(this._fromKoral(operands.get(0)),
Akronbb5d1732015-06-22 01:22:40 +0200754 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
Akron626dd9f2016-06-08 10:08:55 +0200807 SpanQueryWrapper sqw = this._fromKoral(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)
Akron626dd9f2016-06-08 10:08:55 +0200826 return this._fromKoral(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!
Akronc12567c2016-06-03 00:40:52 +0200917 else if (unit.equals("s") || unit.equals("p")
918 || unit.equals("t")) {
Akrond2926902016-02-13 18:37:36 +0100919 StringBuilder value = new StringBuilder();
920 unit = value.append("base/s:").append(unit).toString();
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000921 };
Nils Diewaldbb33da22015-03-04 16:24:25 +0000922
Akron4e2bc0e2016-02-17 12:23:46 +0100923 // Workaround for koral:distance vs cosmas:distance
924 if (constraint.get("@type").asText().equals("koral:distance")) {
925 min++;
926 max++;
927 };
928
Akron4d5fe192016-02-16 00:22:14 +0100929 // Set distance exclusion
930 Boolean exclusion = false;
931 if (constraint.has("exclude"))
932 exclusion = constraint.get("exclude").asBoolean();
933
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000934 // Sanitize boundary
Nils Diewaldbb33da22015-03-04 16:24:25 +0000935 if (max < min)
936 max = min;
937
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000938 if (DEBUG)
Nils Diewaldbb33da22015-03-04 16:24:25 +0000939 log.trace("Add distance constraint of '{}': {}-{}", unit,
940 min, max);
941
Akron4d5fe192016-02-16 00:22:14 +0100942 sseqqw.withConstraint(min, max, unit, exclusion);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000943 };
944 };
945
946 // Add segments to sequence
947 for (JsonNode operand : operands) {
Akron626dd9f2016-06-08 10:08:55 +0200948 sseqqw.append(this._fromKoral(operand));
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000949 };
950
951 // inOrder was set to false without a distance constraint
952 if (!sseqqw.isInOrder() && !sseqqw.hasConstraints()) {
Akron784d13d2015-06-20 15:13:16 +0200953 if (DEBUG)
954 log.trace("Add distance constraint - for the normal inorder case");
955
Nils Diewald6409a922015-01-29 20:50:42 +0000956 sseqqw.withConstraint(1, 1, "w");
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000957 };
958
959 return sseqqw;
960 };
961
962
Nils Diewaldcec40f92015-02-19 22:20:02 +0000963 // Deserialize koral:token
Eliza Margarethab1a5ed42015-01-26 10:37:12 +0000964 private SpanQueryWrapper _segFromJson (JsonNode json) throws QueryException {
Akronc63697c2015-06-17 22:32:02 +0200965
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000966 if (!json.has("@type"))
Nils Diewaldbb33da22015-03-04 16:24:25 +0000967 throw new QueryException(701,
968 "JSON-LD group has no @type attribute");
Nils Diewald33fcb5d2014-11-07 23:27:03 +0000969
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000970 String type = json.get("@type").asText();
Nils Diewald33fcb5d2014-11-07 23:27:03 +0000971
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000972 if (DEBUG)
973 log.trace("Wrap new token definition by {}", type);
Nils Diewald1455e1e2014-08-01 16:12:43 +0000974
Nils Diewald6409a922015-01-29 20:50:42 +0000975 // Branch on type
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000976 switch (type) {
Nils Diewaldbb33da22015-03-04 16:24:25 +0000977 case "koral:term":
978 // String match = "match:eq";
979 // if (json.has("match"))
980 // match = json.get("match").asText();
981 //
982 // switch (match) {
983 //
984 // case "match:ne":
985 // if (DEBUG)
986 // log.trace("Term is negated");
987 //
988 // SpanSegmentQueryWrapper ssqw =
989 // (SpanSegmentQueryWrapper) this._termFromJson(json);
990 //
991 // ssqw.makeNegative();
992 //
993 // return this.seg().without(ssqw);
994 //
995 // case "match:eq":
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000996 return this._termFromJson(json);
Nils Diewaldbb33da22015-03-04 16:24:25 +0000997 // };
998 //
999 // throw new QueryException(741, "Match relation unknown");
Nils Diewald26087ea2013-12-05 16:51:30 +00001000
Nils Diewaldbb33da22015-03-04 16:24:25 +00001001 case "koral:termGroup":
Nils Diewald26087ea2013-12-05 16:51:30 +00001002
Nils Diewaldbb33da22015-03-04 16:24:25 +00001003 if (!json.has("operands"))
1004 throw new QueryException(742,
1005 "Term group needs operand list");
Nils Diewald26087ea2013-12-05 16:51:30 +00001006
Nils Diewaldbb33da22015-03-04 16:24:25 +00001007 // Get operands
1008 JsonNode operands = json.get("operands");
Nils Diewaldc86aa482014-02-12 16:58:05 +00001009
Nils Diewaldbb33da22015-03-04 16:24:25 +00001010 SpanSegmentQueryWrapper ssegqw = this.builder().seg();
Nils Diewaldc86aa482014-02-12 16:58:05 +00001011
Nils Diewaldbb33da22015-03-04 16:24:25 +00001012 if (!json.has("relation"))
1013 throw new QueryException(743,
1014 "Term group expects a relation");
Nils Diewald1220e3e2014-11-08 03:18:58 +00001015
Nils Diewaldbb33da22015-03-04 16:24:25 +00001016 switch (json.get("relation").asText()) {
1017 case "relation:and":
Eliza Margarethab1a5ed42015-01-26 10:37:12 +00001018
Nils Diewaldbb33da22015-03-04 16:24:25 +00001019 for (JsonNode operand : operands) {
1020 SpanQueryWrapper part = this._segFromJson(operand);
1021 if (part instanceof SpanAlterQueryWrapper) {
1022 ssegqw.with((SpanAlterQueryWrapper) part);
1023 }
1024 else if (part instanceof SpanRegexQueryWrapper) {
1025 ssegqw.with((SpanRegexQueryWrapper) part);
1026 }
1027 else if (part instanceof SpanSegmentQueryWrapper) {
1028 ssegqw.with((SpanSegmentQueryWrapper) part);
1029 }
1030 else {
1031 throw new QueryException(744,
1032 "Operand not supported in term group");
1033 };
1034 }
1035 ;
1036 return ssegqw;
Eliza Margarethab1a5ed42015-01-26 10:37:12 +00001037
Nils Diewaldbb33da22015-03-04 16:24:25 +00001038 case "relation:or":
1039
1040 SpanAlterQueryWrapper ssaq = new SpanAlterQueryWrapper(
1041 this.field);
1042 for (JsonNode operand : operands) {
1043 ssaq.or(this._segFromJson(operand));
1044 }
1045 ;
1046 return ssaq;
1047 }
1048 ;
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001049 };
Nils Diewaldbb33da22015-03-04 16:24:25 +00001050 throw new QueryException(745, "Token type is not supported");
Nils Diewald4d183ea2013-12-05 02:51:38 +00001051 };
Nils Diewald4d183ea2013-12-05 02:51:38 +00001052
margarethab097bac2015-04-15 11:37:02 +02001053
margaretha5c26de42015-04-14 12:47:58 +02001054 private SpanQueryWrapper _termFromJson (JsonNode json)
1055 throws QueryException {
1056 return _termFromJson(json, null);
1057 }
Nils Diewald4d183ea2013-12-05 02:51:38 +00001058
margarethab097bac2015-04-15 11:37:02 +02001059
Nils Diewaldcec40f92015-02-19 22:20:02 +00001060 // Deserialize koral:term
Akron907d3d72015-06-30 11:32:42 +02001061 // TODO: Not optimal as it does not respect non-term
margarethab097bac2015-04-15 11:37:02 +02001062 private SpanQueryWrapper _termFromJson (JsonNode json, String direction)
Nils Diewaldbb33da22015-03-04 16:24:25 +00001063 throws QueryException {
Akrond2ddac92016-02-13 16:56:21 +01001064
Nils Diewald6409a922015-01-29 20:50:42 +00001065 if (!json.has("key") || json.get("key").asText().length() < 1) {
Nils Diewaldbb33da22015-03-04 16:24:25 +00001066 if (!json.has("attr"))
1067 throw new QueryException(740,
1068 "Key definition is missing in term or span");
Nils Diewald6409a922015-01-29 20:50:42 +00001069 };
Nils Diewald33fcb5d2014-11-07 23:27:03 +00001070
Nils Diewaldbb33da22015-03-04 16:24:25 +00001071 if (!json.has("@type")) {
1072 throw new QueryException(701,
1073 "JSON-LD group has no @type attribute");
1074 };
1075
1076 Boolean isTerm = json.get("@type").asText().equals("koral:term") ? true
1077 : false;
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001078 Boolean isCaseInsensitive = false;
Nils Diewaldc86aa482014-02-12 16:58:05 +00001079
Akron907d3d72015-06-30 11:32:42 +02001080
1081 // Ugly direction hack
1082 if (direction != null && direction.equals("<>:")) {
1083 isTerm = false;
1084 direction = null;
1085 };
1086
Akrond504f212015-06-20 00:27:54 +02001087 // <legacy>
Nils Diewaldbb33da22015-03-04 16:24:25 +00001088 if (json.has("caseInsensitive")
Akronbb5d1732015-06-22 01:22:40 +02001089 && json.get("caseInsensitive").asBoolean()) {
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001090 isCaseInsensitive = true;
Akron738ed002015-06-18 12:07:50 +02001091 }
Akrond504f212015-06-20 00:27:54 +02001092 // </legacy>
Akron738ed002015-06-18 12:07:50 +02001093
1094 // Flags
1095 else if (json.has("flags") && json.get("flags").isArray()) {
1096 Iterator<JsonNode> flags = json.get("flags").elements();
1097 while (flags.hasNext()) {
Akrond504f212015-06-20 00:27:54 +02001098 String flag = flags.next().asText();
1099 if (flag.equals("flags:caseInsensitive")) {
Akron738ed002015-06-18 12:07:50 +02001100 isCaseInsensitive = true;
Akrond504f212015-06-20 00:27:54 +02001101 }
1102 else {
1103 this.addWarning(748, "Flag is unknown", flag);
Akron738ed002015-06-18 12:07:50 +02001104 };
1105 };
1106 };
Nils Diewaldc86aa482014-02-12 16:58:05 +00001107
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001108 StringBuilder value = new StringBuilder();
Nils Diewaldbb33da22015-03-04 16:24:25 +00001109
Akrona883aa52015-06-18 22:07:31 +02001110 if (direction != null)
margaretha5c26de42015-04-14 12:47:58 +02001111 value.append(direction);
margaretha5c26de42015-04-14 12:47:58 +02001112
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001113 // expect orth? expect lemma?
1114 // s:den | i:den | cnx/l:die | mate/m:mood:ind | cnx/syn:@PREMOD |
1115 // mate/m:number:sg | opennlp/p:ART
Nils Diewaldc86aa482014-02-12 16:58:05 +00001116
Akrona883aa52015-06-18 22:07:31 +02001117 if (json.has("foundry") && json.get("foundry").asText().length() > 0) {
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001118 value.append(json.get("foundry").asText()).append('/');
Akrona883aa52015-06-18 22:07:31 +02001119 };
Nils Diewaldc86aa482014-02-12 16:58:05 +00001120
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001121 // No default foundry defined
1122 if (json.has("layer") && json.get("layer").asText().length() > 0) {
1123 String layer = json.get("layer").asText();
1124 switch (layer) {
Nils Diewaldc86aa482014-02-12 16:58:05 +00001125
Nils Diewaldbb33da22015-03-04 16:24:25 +00001126 case "lemma":
1127 layer = "l";
1128 break;
Nils Diewaldc86aa482014-02-12 16:58:05 +00001129
Nils Diewaldbb33da22015-03-04 16:24:25 +00001130 case "pos":
1131 layer = "p";
1132 break;
Nils Diewald164f8be2014-02-13 02:43:16 +00001133
Nils Diewaldbb33da22015-03-04 16:24:25 +00001134 case "orth":
Akrond2ddac92016-02-13 16:56:21 +01001135 // TODO: THIS IS AN UGLY HACK! AND SHOULD BE NAMED "SURFACE" or . OR *
1136 layer = ".";
Nils Diewaldbb33da22015-03-04 16:24:25 +00001137 break;
Nils Diewald164f8be2014-02-13 02:43:16 +00001138
Nils Diewaldbb33da22015-03-04 16:24:25 +00001139 case "struct":
1140 layer = "s";
1141 break;
Nils Diewald164f8be2014-02-13 02:43:16 +00001142
Nils Diewaldbb33da22015-03-04 16:24:25 +00001143 case "const":
1144 layer = "c";
1145 break;
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001146 };
Nils Diewaldc471b182014-11-19 22:51:15 +00001147
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001148 if (isCaseInsensitive && isTerm) {
Akrond2ddac92016-02-13 16:56:21 +01001149 if (layer.equals("."))
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001150 layer = "i";
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001151 else {
Nils Diewaldbb33da22015-03-04 16:24:25 +00001152 this.addWarning(767,
1153 "Case insensitivity is currently not supported for this layer");
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001154 };
1155 };
Nils Diewaldea7239a2014-11-14 14:01:56 +00001156
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001157 // Ignore foundry for orth layer
Akrond2ddac92016-02-13 16:56:21 +01001158 if (layer.equals(".")) {
Akron13db6152016-02-19 14:08:38 +01001159 layer = "s";
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001160 value.setLength(0);
Akrond2ddac92016-02-13 16:56:21 +01001161 }
1162 else if (layer.equals("i")) {
1163 value.setLength(0);
1164 };
Nils Diewaldc86aa482014-02-12 16:58:05 +00001165
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001166 value.append(layer).append(':');
1167 };
Nils Diewaldc86aa482014-02-12 16:58:05 +00001168
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001169 if (json.has("key") && json.get("key").asText().length() > 0) {
1170 String key = json.get("key").asText();
1171 value.append(isCaseInsensitive ? key.toLowerCase() : key);
1172 };
Nils Diewald3430aa22014-11-07 13:39:03 +00001173
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001174 if (json.has("value") && json.get("value").asText().length() > 0)
1175 value.append(':').append(json.get("value").asText());
Nils Diewaldc86aa482014-02-12 16:58:05 +00001176
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001177 // Regular expression or wildcard
1178 if (isTerm && json.has("type")) {
Nils Diewald6409a922015-01-29 20:50:42 +00001179
Nils Diewald8904c1d2015-02-26 16:13:18 +00001180 QueryBuilder qb = this.builder();
1181
Nils Diewald6409a922015-01-29 20:50:42 +00001182 // Branch on type
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001183 switch (json.get("type").asText()) {
Nils Diewaldbb33da22015-03-04 16:24:25 +00001184 case "type:regex":
1185 return qb.seg(qb.re(value.toString(), isCaseInsensitive));
Nils Diewald6409a922015-01-29 20:50:42 +00001186
Nils Diewaldbb33da22015-03-04 16:24:25 +00001187 case "type:wildcard":
1188 return qb.seq(qb.wc(value.toString(), isCaseInsensitive));
Nils Diewald6409a922015-01-29 20:50:42 +00001189
Nils Diewaldbb33da22015-03-04 16:24:25 +00001190 case "type:string":
1191 break;
Nils Diewald6409a922015-01-29 20:50:42 +00001192
Nils Diewaldbb33da22015-03-04 16:24:25 +00001193 default:
1194 this.addWarning(746,
1195 "Term type is not supported - treated as a string");
Nils Diewaldd75e6f62015-01-28 23:44:56 +00001196 };
1197 };
Nils Diewaldc86aa482014-02-12 16:58:05 +00001198
Nils Diewaldbb33da22015-03-04 16:24:25 +00001199 if (isTerm) {
Eliza Margaretha641b20a2015-02-16 17:23:13 +00001200
Nils Diewaldbb33da22015-03-04 16:24:25 +00001201 String match = "match:eq";
1202 if (json.has("match")) {
1203 match = json.get("match").asText();
1204 }
Eliza Margaretha641b20a2015-02-16 17:23:13 +00001205
Nils Diewaldbb33da22015-03-04 16:24:25 +00001206 SpanSegmentQueryWrapper ssqw = this.builder().seg(value.toString());
1207 if (match.equals("match:ne")) {
1208 if (DEBUG)
1209 log.trace("Term is negated");
1210 ssqw.makeNegative();
1211 return this.builder().seg().without(ssqw);
1212 }
1213 else if (match.equals("match:eq")) {
1214 return ssqw;
1215 }
1216 else {
1217 throw new QueryException(741, "Match relation unknown");
1218 }
Eliza Margaretha641b20a2015-02-16 17:23:13 +00001219 }
Nils Diewaldc471b182014-11-19 22:51:15 +00001220
Eliza Margaretha221349f2015-01-29 17:27:41 +00001221 if (json.has("attr")) {
Nils Diewaldbb33da22015-03-04 16:24:25 +00001222 JsonNode attrNode = json.get("attr");
1223 if (!attrNode.has("@type")) {
1224 throw new QueryException(701,
1225 "JSON-LD group has no @type attribute");
1226 }
Eliza Margaretha221349f2015-01-29 17:27:41 +00001227
Nils Diewaldbb33da22015-03-04 16:24:25 +00001228 if (value.toString().isEmpty()) {
1229 return _createElementAttrFromJson(null, json, attrNode);
1230 // this.addWarning(771,
1231 // "Arbitraty elements with attributes are currently not supported.");
1232 }
1233 else {
1234 SpanQueryWrapper elementWithIdWrapper = this.builder().tag(
1235 value.toString());
1236 if (elementWithIdWrapper == null) {
1237 return null;
1238 }
1239 return _createElementAttrFromJson(elementWithIdWrapper, json,
1240 attrNode);
1241 }
Nils Diewald6409a922015-01-29 20:50:42 +00001242 };
Nils Diewald8904c1d2015-02-26 16:13:18 +00001243 return this.builder().tag(value.toString());
Nils Diewaldc86aa482014-02-12 16:58:05 +00001244 };
1245
Nils Diewald8904c1d2015-02-26 16:13:18 +00001246
1247 // Deserialize elements with attributes
Nils Diewaldbb33da22015-03-04 16:24:25 +00001248 private SpanQueryWrapper _createElementAttrFromJson (
1249 SpanQueryWrapper elementWithIdWrapper, JsonNode json,
1250 JsonNode attrNode) throws QueryException {
Eliza Margaretha1969c2b2015-02-23 15:13:03 +00001251
Nils Diewaldbb33da22015-03-04 16:24:25 +00001252 if (attrNode.get("@type").asText().equals("koral:term")) {
1253 SpanQueryWrapper attrWrapper = _attrFromJson(json.get("attr"));
1254 if (attrWrapper != null) {
1255 if (elementWithIdWrapper != null) {
1256 return new SpanWithAttributeQueryWrapper(
1257 elementWithIdWrapper, attrWrapper);
1258 }
1259 else {
1260 return new SpanWithAttributeQueryWrapper(attrWrapper);
1261 }
1262 }
1263 else {
1264 throw new QueryException(747, "Attribute is null");
1265 }
1266 }
Nils Diewald8904c1d2015-02-26 16:13:18 +00001267 else if (attrNode.get("@type").asText().equals("koral:termGroup")) {
Nils Diewaldbb33da22015-03-04 16:24:25 +00001268 return _handleAttrGroup(elementWithIdWrapper, attrNode);
1269 }
1270 else {
1271 this.addWarning(715, "Attribute type is not supported");
1272 }
1273 return elementWithIdWrapper;
1274 }
Nils Diewald8904c1d2015-02-26 16:13:18 +00001275
1276
1277 // Deserialize attribute groups
Nils Diewaldbb33da22015-03-04 16:24:25 +00001278 private SpanQueryWrapper _handleAttrGroup (
1279 SpanQueryWrapper elementWithIdWrapper, JsonNode attrNode)
1280 throws QueryException {
1281 if (!attrNode.has("relation")) {
1282 throw new QueryException(743, "Term group expects a relation");
1283 }
1284 if (!attrNode.has("operands")) {
1285 throw new QueryException(742, "Term group needs operand list");
1286 }
Eliza Margaretha1969c2b2015-02-23 15:13:03 +00001287
Nils Diewaldbb33da22015-03-04 16:24:25 +00001288 String relation = attrNode.get("relation").asText();
1289 JsonNode operands = attrNode.get("operands");
Eliza Margaretha1969c2b2015-02-23 15:13:03 +00001290
Nils Diewaldbb33da22015-03-04 16:24:25 +00001291 SpanQueryWrapper attrWrapper;
1292 if ("relation:and".equals(relation)) {
1293 List<SpanQueryWrapper> wrapperList = new ArrayList<SpanQueryWrapper>();
1294 for (JsonNode operand : operands) {
1295 attrWrapper = _termFromJson(operand);
1296 if (attrWrapper == null) {
1297 throw new QueryException(747, "Attribute is null");
1298 }
1299 wrapperList.add(attrWrapper);
1300 }
Eliza Margaretha1969c2b2015-02-23 15:13:03 +00001301
Nils Diewaldbb33da22015-03-04 16:24:25 +00001302 if (elementWithIdWrapper != null) {
1303 return new SpanWithAttributeQueryWrapper(elementWithIdWrapper,
1304 wrapperList);
1305 }
1306 else {
1307 return new SpanWithAttributeQueryWrapper(wrapperList);
1308 }
1309 }
1310 else if ("relation:or".equals(relation)) {
1311 SpanAlterQueryWrapper saq = new SpanAlterQueryWrapper(field);
1312 SpanWithAttributeQueryWrapper saqw;
1313 for (JsonNode operand : operands) {
1314 attrWrapper = _termFromJson(operand);
1315 if (attrWrapper == null) {
1316 throw new QueryException(747, "Attribute is null");
1317 }
1318 if (elementWithIdWrapper != null) {
1319 saqw = new SpanWithAttributeQueryWrapper(
1320 elementWithIdWrapper, attrWrapper);
1321 }
1322 else {
1323 saqw = new SpanWithAttributeQueryWrapper(attrWrapper);
1324 }
1325 saq.or(saqw);
1326 }
1327 return saq;
1328 }
1329 else {
1330 throw new QueryException(716, "Unknown relation");
1331 }
1332 }
1333
Nils Diewald6409a922015-01-29 20:50:42 +00001334
Nils Diewald83c9b162015-02-03 21:05:07 +00001335 // Get attributes from a json termgroup
Nils Diewald6409a922015-01-29 20:50:42 +00001336 private SpanQueryWrapper _attrFromJson (JsonNode attrNode)
Nils Diewaldbb33da22015-03-04 16:24:25 +00001337 throws QueryException {
Eliza Margaretha221349f2015-01-29 17:27:41 +00001338
Nils Diewaldbb33da22015-03-04 16:24:25 +00001339 if (attrNode.has("key")) {
1340 return _termFromJson(attrNode);
1341 }
1342 else if (attrNode.has("tokenarity") || attrNode.has("arity")) {
1343 this.addWarning(770, "Arity attributes are currently not supported"
1344 + " - results may not be correct");
1345 }
1346 else if (attrNode.has("root")) {
1347 String rootValue = attrNode.get("root").asText();
1348 if (rootValue.equals("true") || rootValue.equals("false")) {
Akronc63697c2015-06-17 22:32:02 +02001349
1350 // TODO: Here do not refer to 'tokens'!!!
margaretha29521f92016-04-18 15:20:02 +02001351 // EM: what should it be? property?
Nils Diewaldbb33da22015-03-04 16:24:25 +00001352 return new SpanAttributeQueryWrapper(
1353 new SpanSimpleQueryWrapper("tokens", "@root",
1354 Boolean.valueOf(rootValue)));
Eliza Margaretha221349f2015-01-29 17:27:41 +00001355 }
1356 }
Nils Diewaldbb33da22015-03-04 16:24:25 +00001357 return null;
Nils Diewaldbbd39a52015-02-23 19:56:57 +00001358 };
Nils Diewaldf399a672013-11-18 17:55:22 +00001359};