blob: 6dfed6bbd5ce667ec7775f6bc38d4bbe6d40570a [file] [log] [blame]
Nils Diewaldc925b492013-12-03 23:56:10 +00001package de.ids_mannheim.korap;
2
Nils Diewald7cf8c6d2014-05-28 18:37:38 +00003import java.io.*;
Nils Diewaldc7d08d92014-11-05 21:30:05 +00004import java.util.*;
Michael Hanl7edaa552014-05-23 18:48:50 +00005
Nils Diewald7cf8c6d2014-05-28 18:37:38 +00006import org.apache.lucene.search.spans.SpanQuery;
Nils Diewald92729ce2014-10-06 16:00:17 +00007import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
Nils Diewalda14ecd62015-02-26 21:00:20 +00008import de.ids_mannheim.korap.KrillIndex;
Nils Diewald884dbcf2015-02-27 17:02:28 +00009import de.ids_mannheim.korap.response.Result;
Nils Diewald7cf8c6d2014-05-28 18:37:38 +000010import de.ids_mannheim.korap.util.QueryException;
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000011import de.ids_mannheim.korap.response.Notifications;
Nils Diewald0881e242015-02-27 17:31:01 +000012import de.ids_mannheim.korap.response.Response;
Nils Diewald7cf8c6d2014-05-28 18:37:38 +000013
14import com.fasterxml.jackson.databind.ObjectMapper;
15import com.fasterxml.jackson.databind.JsonNode;
Nils Diewaldc925b492013-12-03 23:56:10 +000016
Nils Diewaldbbd39a52015-02-23 19:56:57 +000017import org.slf4j.Logger;
18import org.slf4j.LoggerFactory;
19
Nils Diewald3aa9e692015-02-20 22:20:11 +000020/**
Nils Diewaldbb33da22015-03-04 16:24:25 +000021 * <p>Krill is a corpus data retrieval index using Lucene for
22 * Look-Ups.</p>
23 *
Nils Diewald21914ff2015-02-28 02:09:47 +000024 * <p>
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000025 * It is the reference implementation for KoralQuery consumption,
Nils Diewald21914ff2015-02-28 02:09:47 +000026 * and this class acts as the central point for consuming and
27 * responding to KoralQuery requests.
28 * </p>
Nils Diewaldbb33da22015-03-04 16:24:25 +000029 *
Nils Diewald21914ff2015-02-28 02:09:47 +000030 * <p>
Nils Diewaldbb33da22015-03-04 16:24:25 +000031 * The processing of the collection section of the request is
32 * delegated
33 * to {@link KrillCollection}, the query section to {@link KrillQuery}
34 * ,
Nils Diewald21914ff2015-02-28 02:09:47 +000035 * and the meta section to {@link KrillMeta}.
36 * </p>
Nils Diewaldbb33da22015-03-04 16:24:25 +000037 *
Nils Diewald3aa9e692015-02-20 22:20:11 +000038 * <blockquote><pre>
Nils Diewaldbb33da22015-03-04 16:24:25 +000039 * // Create or receive a KoralQuery JSON string
40 * String koral = "{\"query\":{...}, \"collection\":{...}, ... }";
41 *
42 * // Create a new krill search object by passing the Query
43 * Krill krill = new Krill(koral);
44 *
45 * // Apply the query to an index and receive a search result
46 * // This may invoke different actions depending on the request
47 * Result result = krill.setIndex(new KrillIndex()).apply();
Nils Diewald3aa9e692015-02-20 22:20:11 +000048 * </pre></blockquote>
Nils Diewaldbb33da22015-03-04 16:24:25 +000049 *
Nils Diewald3aa9e692015-02-20 22:20:11 +000050 * @author diewald
51 * @author margaretha
Nils Diewaldbb33da22015-03-04 16:24:25 +000052 *
Nils Diewald2d5f8102015-02-26 21:07:54 +000053 * @see KrillCollection
Nils Diewald0339d462015-02-26 14:53:56 +000054 * @see KrillQuery
Nils Diewaldd37f7e42015-02-27 21:08:22 +000055 * @see KrillMeta
Nils Diewalda14ecd62015-02-26 21:00:20 +000056 * @see KrillIndex
Nils Diewald3aa9e692015-02-20 22:20:11 +000057 */
Akronb1166442015-06-27 00:34:19 +020058// TODO: Use a krill.properties configuration file
59// TODO: Reuse passed JSON object instead of creating a new response!
Nils Diewald0881e242015-02-27 17:31:01 +000060public class Krill extends Response {
Nils Diewalda14ecd62015-02-26 21:00:20 +000061 private KrillIndex index;
Nils Diewaldbbd39a52015-02-23 19:56:57 +000062 private SpanQuery spanQuery;
Nils Diewaldefb9c9a2014-02-20 15:05:18 +000063 private JsonNode request;
Nils Diewald1e5d5942014-05-20 13:29:53 +000064 private String spanContext;
Nils Diewald364eb642013-12-22 15:03:01 +000065
Nils Diewaldbbd39a52015-02-23 19:56:57 +000066 // Logger
67 private final static Logger log = LoggerFactory.getLogger(Krill.class);
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000068
Nils Diewaldbb33da22015-03-04 16:24:25 +000069
Nils Diewald3aa9e692015-02-20 22:20:11 +000070 /**
71 * Construct a new Krill object.
72 */
Nils Diewaldbbd39a52015-02-23 19:56:57 +000073 public Krill () {};
Nils Diewald3aa9e692015-02-20 22:20:11 +000074
75
76 /**
77 * Construct a new Krill object,
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000078 * consuming a KoralQuery json string.
Nils Diewaldbb33da22015-03-04 16:24:25 +000079 *
80 * @param query
81 * The KoralQuery json string.
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000082 */
83 public Krill (String query) {
84 this.fromJson(query);
85 };
86
87
88 /**
89 * Construct a new Krill object,
90 * consuming a KoralQuery {@link JsonNode} object.
Nils Diewaldbb33da22015-03-04 16:24:25 +000091 *
92 * @param query
93 * The KoralQuery {@link JsonNode} object.
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000094 */
95 public Krill (JsonNode query) {
96 this.fromJson(query);
97 };
98
99
100 /**
101 * Construct a new Krill object,
Nils Diewald3aa9e692015-02-20 22:20:11 +0000102 * consuming a {@link SpanQueryWrapper} object.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000103 *
104 * @param query
105 * The {@link SpanQueryWrapper} object.
Nils Diewald3aa9e692015-02-20 22:20:11 +0000106 */
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000107 public Krill (SpanQueryWrapper query) {
Nils Diewaldafab8f32015-01-26 19:11:32 +0000108 try {
Nils Diewald3aa9e692015-02-20 22:20:11 +0000109 this.spanQuery = query.toQuery();
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000110 }
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000111
112 // Add the error to the KoralQuery response
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000113 catch (QueryException q) {
114 this.addError(q.getErrorCode(), q.getMessage());
115 };
Nils Diewald7cf8c6d2014-05-28 18:37:38 +0000116 };
Nils Diewaldbb33da22015-03-04 16:24:25 +0000117
Nils Diewald3aa9e692015-02-20 22:20:11 +0000118
119 /**
120 * Construct a new Krill object,
121 * consuming a {@link SpanQuery} object.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000122 *
123 * @param query
124 * The {@link SpanQuery} object.
Nils Diewald3aa9e692015-02-20 22:20:11 +0000125 */
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000126 public Krill (SpanQuery query) {
Nils Diewald3aa9e692015-02-20 22:20:11 +0000127 this.spanQuery = query;
Nils Diewald7cf8c6d2014-05-28 18:37:38 +0000128 };
Nils Diewaldc925b492013-12-03 23:56:10 +0000129
Nils Diewald3aa9e692015-02-20 22:20:11 +0000130
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000131 /**
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000132 * Parse KoralQuery as a json string.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000133 *
134 * @param query
135 * The KoralQuery json string.
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000136 * @return The {@link Krill} object for chaining.
137 * @throws QueryException
138 */
139 public Krill fromJson (String query) {
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000140 // Parse query string
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000141 try {
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000142 ObjectMapper mapper = new ObjectMapper();
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000143 this.request = mapper.readTree(query);
144 this.fromJson(this.request);
145 }
146
147 // Unable to parse JSON
148 catch (IOException e) {
149 this.addError(621, "Unable to parse JSON");
150 };
151
152 return this;
153 };
154
155
156 /**
157 * Parse KoralQuery as a {@link JsonNode} object.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000158 *
159 * @param query
160 * The KoralQuery {@link JsonNode} object.
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000161 * @return The {@link Krill} object for chaining.
162 * @throws QueryException
163 */
164 public Krill fromJson (JsonNode json) {
165
Nils Diewald3aa9e692015-02-20 22:20:11 +0000166 // Parse "query" attribute
167 if (json.has("query")) {
168 try {
Nils Diewald0339d462015-02-26 14:53:56 +0000169 KrillQuery kq = new KrillQuery("tokens");
Nils Diewald3aa9e692015-02-20 22:20:11 +0000170 SpanQueryWrapper qw = kq.fromJson(json.get("query"));
Akronc63697c2015-06-17 22:32:02 +0200171
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000172 this.setQuery(kq);
Nils Diewald3aa9e692015-02-20 22:20:11 +0000173
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000174 // Throw an error, in case the query matches everywhere
Nils Diewald3aa9e692015-02-20 22:20:11 +0000175 if (qw.isEmpty())
176 this.addError(780, "This query matches everywhere");
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000177
Nils Diewald3aa9e692015-02-20 22:20:11 +0000178 else {
Nils Diewald21914ff2015-02-28 02:09:47 +0000179
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000180 // Serialize a Lucene SpanQuery based on the SpanQueryWrapper
Nils Diewald3aa9e692015-02-20 22:20:11 +0000181 this.spanQuery = qw.toQuery();
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000182
183 // Throw a warning in case the root object is optional
Nils Diewald3aa9e692015-02-20 22:20:11 +0000184 if (qw.isOptional())
185 this.addWarning(781, "Optionality of query is ignored");
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000186
187 // Throw a warning in case the root object is negative
Nils Diewald3aa9e692015-02-20 22:20:11 +0000188 if (qw.isNegative())
189 this.addWarning(782, "Exclusivity of query is ignored");
190 };
Nils Diewald3aa9e692015-02-20 22:20:11 +0000191 }
192 catch (QueryException q) {
193 this.addError(q.getErrorCode(), q.getMessage());
194 };
195 }
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000196 else
Nils Diewald3aa9e692015-02-20 22:20:11 +0000197 this.addError(700, "No query given");
198
199 // <legacycode>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000200 if (json.has("warning") && json.get("warning").asText().length() > 0) {
Nils Diewald3aa9e692015-02-20 22:20:11 +0000201 this.addWarning(799, json.get("warning").asText());
202 };
203 // </legacycode>
204
Nils Diewald3aa9e692015-02-20 22:20:11 +0000205 // Copy notifications from request
206 this.copyNotificationsFrom(json);
Nils Diewaldbb33da22015-03-04 16:24:25 +0000207
Nils Diewald21914ff2015-02-28 02:09:47 +0000208 // Parse "collection" or "collections" attribute
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000209 try {
210 if (json.has("collection")) {
Akronc63697c2015-06-17 22:32:02 +0200211 JsonNode collNode = json.get("collection");
Akronbb5d1732015-06-22 01:22:40 +0200212
Akronc63697c2015-06-17 22:32:02 +0200213 // TODO: Temporary
Akronbb5d1732015-06-22 01:22:40 +0200214 if (collNode.fieldNames().hasNext()) {
215 KrillCollection kc = new KrillCollection()
216 .fromJson(collNode);
217 this.setCollection(kc);
218 };
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000219 }
Nils Diewald3aa9e692015-02-20 22:20:11 +0000220
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000221 // <legacycode>
222 else if (json.has("collections")) {
Nils Diewald2d5f8102015-02-26 21:07:54 +0000223 KrillCollection kc = new KrillCollection();
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000224 for (JsonNode collection : json.get("collections")) {
225 kc.fromJsonLegacy(collection);
226 };
Nils Diewaldbb33da22015-03-04 16:24:25 +0000227
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000228 this.setCollection(kc);
Nils Diewald3aa9e692015-02-20 22:20:11 +0000229 };
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000230 // </legacycode>
231 }
232 catch (QueryException q) {
233 this.addError(q.getErrorCode(), q.getMessage());
Nils Diewald3aa9e692015-02-20 22:20:11 +0000234 };
Nils Diewald3aa9e692015-02-20 22:20:11 +0000235
Nils Diewald21914ff2015-02-28 02:09:47 +0000236 // Parse "meta" attribute
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000237 if (!this.hasErrors() && json.has("meta"))
238 this.setMeta(new KrillMeta(json.get("meta")));
Nils Diewald3aa9e692015-02-20 22:20:11 +0000239
Nils Diewald3aa9e692015-02-20 22:20:11 +0000240 return this;
241 };
242
Nils Diewald3aa9e692015-02-20 22:20:11 +0000243
244 /**
Nils Diewalda14ecd62015-02-26 21:00:20 +0000245 * Get the associated {@link KrillIndex} object.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000246 *
Nils Diewalda14ecd62015-02-26 21:00:20 +0000247 * @return The associated {@link KrillIndex} object.
Nils Diewald3aa9e692015-02-20 22:20:11 +0000248 */
Nils Diewalda14ecd62015-02-26 21:00:20 +0000249 public KrillIndex getIndex () {
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000250 return this.index;
251 };
252
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000253
254 /**
Nils Diewald21914ff2015-02-28 02:09:47 +0000255 * Set the {@link KrillIndex} object.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000256 *
257 * @param index
258 * The associated {@link KrillIndex} object.
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000259 * @return The {@link Krill} object for chaining.
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000260 */
Nils Diewalda14ecd62015-02-26 21:00:20 +0000261 public Krill setIndex (KrillIndex index) {
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000262 this.index = index;
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000263 return this;
264 };
265
266
Nils Diewald3aa9e692015-02-20 22:20:11 +0000267 /**
268 * Apply the KoralQuery to an index.
Nils Diewald21914ff2015-02-28 02:09:47 +0000269 * This may invoke different actions depending
Nils Diewaldbb33da22015-03-04 16:24:25 +0000270 * on the meta information, like {@link KrillIndex#search} or
271 * {@link KrillIndex#collect}.
272 *
273 * @param index
274 * The {@link KrillIndex} the search should be applyied
275 * to.
Nils Diewald884dbcf2015-02-27 17:02:28 +0000276 * @return The result as a {@link Result} object.
Nils Diewald3aa9e692015-02-20 22:20:11 +0000277 */
Nils Diewald884dbcf2015-02-27 17:02:28 +0000278 public Result apply (KrillIndex index) {
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000279 return this.setIndex(index).apply();
280 };
Nils Diewaldea28b622014-10-01 16:01:31 +0000281
Nils Diewaldc925b492013-12-03 23:56:10 +0000282
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000283 /**
284 * Apply the KoralQuery to an index.
Nils Diewald21914ff2015-02-28 02:09:47 +0000285 * This may invoke different actions depending
Nils Diewaldbb33da22015-03-04 16:24:25 +0000286 * on the meta information, like {@link KrillIndex#search} or
287 * {@link KrillIndex#collect}.
288 *
Nils Diewald884dbcf2015-02-27 17:02:28 +0000289 * @return The result as a {@link Result} object.
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000290 */
Nils Diewald884dbcf2015-02-27 17:02:28 +0000291 public Result apply () {
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000292
Nils Diewald884dbcf2015-02-27 17:02:28 +0000293 // Create new Result object to return
294 Result kr = new Result();
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000295
296 // There were errors
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000297 if (this.hasErrors()) {
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000298 kr.copyNotificationsFrom(this);
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000299 }
300
301 // There was no index
302 else if (this.index == null) {
303 kr.addError(601, "Unable to find index");
304 }
305
306 // Apply search
307 else {
Akronbb5d1732015-06-22 01:22:40 +0200308
309 // This contains meta and matches
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000310 kr = this.index.search(this);
Akronbb5d1732015-06-22 01:22:40 +0200311 // this.getCollection().setIndex(this.index);
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000312 kr.copyNotificationsFrom(this);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000313 };
Nils Diewaldc6b78752013-12-05 19:05:12 +0000314
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000315 kr.setQuery(this.getQuery());
Akronbb5d1732015-06-22 01:22:40 +0200316 kr.setCollection(this.getCollection());
Akronb1166442015-06-27 00:34:19 +0200317 kr.setMeta(this.getMeta());
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000318
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000319 return kr;
Nils Diewald7cf8c6d2014-05-28 18:37:38 +0000320 };
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000321
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000322
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000323 /**
324 * Get the associated {@link SpanQuery} deserialization
Nils Diewaldbb33da22015-03-04 16:24:25 +0000325 * (i.e. the internal correspandence to KoralQuery's query
326 * object).
327 *
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000328 * <strong>Warning</strong>: SpanQueries may be lazy deserialized
329 * in future versions of Krill, rendering this API obsolete.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000330 *
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000331 * @return The deserialized {@link SpanQuery} object.
332 */
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000333 @Deprecated
334 public SpanQuery getSpanQuery () {
335 return this.spanQuery;
336 };
337
338
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000339 /**
340 * Set the SpanQuery by means of a {@link SpanQueryWrapper} object
Nils Diewaldbb33da22015-03-04 16:24:25 +0000341 * (i.e. the internal correspandence to KoralQuery's query
342 * object).
343 *
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000344 * <strong>Warning</strong>: SpanQueries may be lazy deserialized
345 * in future versions of Krill, rendering this API obsolete.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000346 *
347 * @param query
348 * The {@link SpanQueryWrapper} to unwrap
349 * the {@link SpanQuery} object.
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000350 * @return The {@link Krill} object for chaining.
351 */
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000352 @Deprecated
353 public Krill setSpanQuery (SpanQueryWrapper sqwi) {
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000354 try {
355 this.spanQuery = sqwi.toQuery();
356 }
357 catch (QueryException q) {
358 this.addError(q.getErrorCode(), q.getMessage());
359 };
360 return this;
361 };
362
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000363
364 /**
365 * Set the {@link SpanQuery} object
Nils Diewaldbb33da22015-03-04 16:24:25 +0000366 * (i.e. the internal correspandence to KoralQuery's query
367 * object).
368 *
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000369 * <strong>Warning</strong>: SpanQueries may be lazy deserialized
370 * in future versions of Krill, rendering this API obsolete.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000371 *
372 * @param query
373 * The {@link SpanQuery} object.
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000374 * @return The {@link Krill} object for chaining.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000375 */
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000376 @Deprecated
377 public Krill setSpanQuery (SpanQuery sq) {
378 this.spanQuery = sq;
379 return this;
380 };
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000381
382
383 // Requests are out - queries will be mirrored completely
384 @Deprecated
385 public JsonNode getRequest () {
386 return this.request;
387 };
Nils Diewald9f310832013-12-06 22:38:55 +0000388};