blob: 5b408731c7025d93987af44284f72d690bcea76a [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
Akron98b78542015-08-06 21:43:08 +020066 private final ObjectMapper mapper = new ObjectMapper();
67
Nils Diewaldbbd39a52015-02-23 19:56:57 +000068 // Logger
69 private final static Logger log = LoggerFactory.getLogger(Krill.class);
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000070
Nils Diewaldbb33da22015-03-04 16:24:25 +000071
Nils Diewald3aa9e692015-02-20 22:20:11 +000072 /**
73 * Construct a new Krill object.
74 */
Nils Diewaldbbd39a52015-02-23 19:56:57 +000075 public Krill () {};
Nils Diewald3aa9e692015-02-20 22:20:11 +000076
77
78 /**
79 * Construct a new Krill object,
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000080 * consuming a KoralQuery json string.
Nils Diewaldbb33da22015-03-04 16:24:25 +000081 *
82 * @param query
83 * The KoralQuery json string.
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000084 */
85 public Krill (String query) {
Akron850b46e2016-06-08 10:08:55 +020086 this.fromKoral(query);
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000087 };
88
89
90 /**
91 * Construct a new Krill object,
92 * consuming a KoralQuery {@link JsonNode} object.
Nils Diewaldbb33da22015-03-04 16:24:25 +000093 *
94 * @param query
95 * The KoralQuery {@link JsonNode} object.
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000096 */
97 public Krill (JsonNode query) {
Akron850b46e2016-06-08 10:08:55 +020098 this.fromKoral(query);
Nils Diewaldf5ab4b22015-02-25 20:55:16 +000099 };
100
101
102 /**
103 * Construct a new Krill object,
Nils Diewald3aa9e692015-02-20 22:20:11 +0000104 * consuming a {@link SpanQueryWrapper} object.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000105 *
106 * @param query
107 * The {@link SpanQueryWrapper} object.
Nils Diewald3aa9e692015-02-20 22:20:11 +0000108 */
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000109 public Krill (SpanQueryWrapper query) {
Nils Diewaldafab8f32015-01-26 19:11:32 +0000110 try {
Nils Diewald3aa9e692015-02-20 22:20:11 +0000111 this.spanQuery = query.toQuery();
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000112 }
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000113
114 // Add the error to the KoralQuery response
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000115 catch (QueryException q) {
116 this.addError(q.getErrorCode(), q.getMessage());
117 };
Nils Diewald7cf8c6d2014-05-28 18:37:38 +0000118 };
Nils Diewaldbb33da22015-03-04 16:24:25 +0000119
Nils Diewald3aa9e692015-02-20 22:20:11 +0000120
121 /**
122 * Construct a new Krill object,
123 * consuming a {@link SpanQuery} object.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000124 *
125 * @param query
126 * The {@link SpanQuery} object.
Nils Diewald3aa9e692015-02-20 22:20:11 +0000127 */
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000128 public Krill (SpanQuery query) {
Nils Diewald3aa9e692015-02-20 22:20:11 +0000129 this.spanQuery = query;
Nils Diewald7cf8c6d2014-05-28 18:37:38 +0000130 };
Nils Diewaldc925b492013-12-03 23:56:10 +0000131
Nils Diewald3aa9e692015-02-20 22:20:11 +0000132
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000133 /**
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000134 * Parse KoralQuery as a json string.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000135 *
136 * @param query
137 * The KoralQuery json string.
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000138 * @return The {@link Krill} object for chaining.
139 * @throws QueryException
140 */
Akron850b46e2016-06-08 10:08:55 +0200141 public Krill fromKoral (final String query) {
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000142 // Parse query string
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000143 try {
144 this.request = mapper.readTree(query);
Akron850b46e2016-06-08 10:08:55 +0200145 this.fromKoral(this.request);
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000146 }
147
148 // Unable to parse JSON
149 catch (IOException e) {
150 this.addError(621, "Unable to parse JSON");
151 };
152
153 return this;
154 };
155
156
157 /**
158 * Parse KoralQuery as a {@link JsonNode} object.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000159 *
160 * @param query
161 * The KoralQuery {@link JsonNode} object.
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000162 * @return The {@link Krill} object for chaining.
163 * @throws QueryException
164 */
Akron850b46e2016-06-08 10:08:55 +0200165 public Krill fromKoral (JsonNode json) {
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000166
Nils Diewald3aa9e692015-02-20 22:20:11 +0000167 // Parse "query" attribute
168 if (json.has("query")) {
169 try {
Akron98b78542015-08-06 21:43:08 +0200170 final KrillQuery kq = new KrillQuery("tokens");
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000171 this.setQuery(kq);
Nils Diewald3aa9e692015-02-20 22:20:11 +0000172
Akron850b46e2016-06-08 10:08:55 +0200173 final SpanQueryWrapper qw = kq.fromKoral(json.get("query"));
Akron001dab32015-07-02 12:30:15 +0200174
Akron352dae82016-08-05 17:57:51 +0200175 // Koral messages are moved to the Krill object
176 this.moveNotificationsFrom(kq);
177
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000178 // Throw an error, in case the query matches everywhere
Akrondfc93572016-08-10 19:01:34 +0200179 if (qw.isEmpty()) {
Nils Diewald3aa9e692015-02-20 22:20:11 +0000180 this.addError(780, "This query matches everywhere");
Akronf9def5e2016-10-10 21:26:46 +0200181 }
182 else if (qw.isNull()) {
183 this.addError(783, "This query can't match anywhere");
184 }
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000185
Nils Diewald3aa9e692015-02-20 22:20:11 +0000186 else {
Nils Diewald21914ff2015-02-28 02:09:47 +0000187
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000188 // Serialize a Lucene SpanQuery based on the SpanQueryWrapper
Nils Diewald3aa9e692015-02-20 22:20:11 +0000189 this.spanQuery = qw.toQuery();
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000190
Akrona7b936d2016-03-04 13:40:54 +0100191 // TODO: Make these information query rewrites
Akron0f3607d2016-02-23 22:16:20 +0100192
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000193 // Throw a warning in case the root object is optional
Nils Diewald3aa9e692015-02-20 22:20:11 +0000194 if (qw.isOptional())
195 this.addWarning(781, "Optionality of query is ignored");
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000196
197 // Throw a warning in case the root object is negative
Nils Diewald3aa9e692015-02-20 22:20:11 +0000198 if (qw.isNegative())
199 this.addWarning(782, "Exclusivity of query is ignored");
200 };
Nils Diewald3aa9e692015-02-20 22:20:11 +0000201 }
202 catch (QueryException q) {
203 this.addError(q.getErrorCode(), q.getMessage());
204 };
205 }
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000206 else
Nils Diewald3aa9e692015-02-20 22:20:11 +0000207 this.addError(700, "No query given");
208
209 // <legacycode>
Nils Diewaldbb33da22015-03-04 16:24:25 +0000210 if (json.has("warning") && json.get("warning").asText().length() > 0) {
Nils Diewald3aa9e692015-02-20 22:20:11 +0000211 this.addWarning(799, json.get("warning").asText());
212 };
213 // </legacycode>
214
Nils Diewald3aa9e692015-02-20 22:20:11 +0000215 // Copy notifications from request
216 this.copyNotificationsFrom(json);
Nils Diewaldbb33da22015-03-04 16:24:25 +0000217
Nils Diewald21914ff2015-02-28 02:09:47 +0000218 // Parse "collection" or "collections" attribute
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000219 try {
220 if (json.has("collection")) {
Akron98b78542015-08-06 21:43:08 +0200221 final JsonNode collNode = json.get("collection");
Akronbb5d1732015-06-22 01:22:40 +0200222
Akronc63697c2015-06-17 22:32:02 +0200223 // TODO: Temporary
Akronbb5d1732015-06-22 01:22:40 +0200224 if (collNode.fieldNames().hasNext()) {
Eliza Margaretha6f989202016-10-14 21:48:29 +0200225 this.setCollection(
226 new KrillCollection().fromKoral(collNode));
Akronbb5d1732015-06-22 01:22:40 +0200227 };
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000228 }
Nils Diewald3aa9e692015-02-20 22:20:11 +0000229
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000230 else if (json.has("collections")) {
Akron40550172015-08-04 03:06:12 +0200231 this.addError(899,
232 "Collections are not supported anymore in favour of a single collection");
Nils Diewald3aa9e692015-02-20 22:20:11 +0000233 };
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000234 }
235 catch (QueryException q) {
236 this.addError(q.getErrorCode(), q.getMessage());
Nils Diewald3aa9e692015-02-20 22:20:11 +0000237 };
Nils Diewald3aa9e692015-02-20 22:20:11 +0000238
Nils Diewald21914ff2015-02-28 02:09:47 +0000239 // Parse "meta" attribute
Akron001dab32015-07-02 12:30:15 +0200240 // !this.hasErrors() &&
241 if (json.has("meta"))
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000242 this.setMeta(new KrillMeta(json.get("meta")));
Nils Diewald3aa9e692015-02-20 22:20:11 +0000243
Nils Diewald3aa9e692015-02-20 22:20:11 +0000244 return this;
245 };
246
Nils Diewald3aa9e692015-02-20 22:20:11 +0000247
248 /**
Nils Diewalda14ecd62015-02-26 21:00:20 +0000249 * Get the associated {@link KrillIndex} object.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000250 *
Nils Diewalda14ecd62015-02-26 21:00:20 +0000251 * @return The associated {@link KrillIndex} object.
Nils Diewald3aa9e692015-02-20 22:20:11 +0000252 */
Nils Diewalda14ecd62015-02-26 21:00:20 +0000253 public KrillIndex getIndex () {
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000254 return this.index;
255 };
256
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000257
258 /**
Nils Diewald21914ff2015-02-28 02:09:47 +0000259 * Set the {@link KrillIndex} object.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000260 *
261 * @param index
262 * The associated {@link KrillIndex} object.
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000263 * @return The {@link Krill} object for chaining.
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000264 */
Nils Diewalda14ecd62015-02-26 21:00:20 +0000265 public Krill setIndex (KrillIndex index) {
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000266 this.index = index;
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000267 return this;
268 };
269
270
Nils Diewald3aa9e692015-02-20 22:20:11 +0000271 /**
272 * Apply the KoralQuery to an index.
Nils Diewald21914ff2015-02-28 02:09:47 +0000273 * This may invoke different actions depending
Nils Diewaldbb33da22015-03-04 16:24:25 +0000274 * on the meta information, like {@link KrillIndex#search} or
275 * {@link KrillIndex#collect}.
276 *
277 * @param index
278 * The {@link KrillIndex} the search should be applyied
279 * to.
Nils Diewald884dbcf2015-02-27 17:02:28 +0000280 * @return The result as a {@link Result} object.
Nils Diewald3aa9e692015-02-20 22:20:11 +0000281 */
Nils Diewald884dbcf2015-02-27 17:02:28 +0000282 public Result apply (KrillIndex index) {
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000283 return this.setIndex(index).apply();
284 };
Nils Diewaldea28b622014-10-01 16:01:31 +0000285
Nils Diewaldc925b492013-12-03 23:56:10 +0000286
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000287 /**
288 * Apply the KoralQuery to an index.
Nils Diewald21914ff2015-02-28 02:09:47 +0000289 * This may invoke different actions depending
Nils Diewaldbb33da22015-03-04 16:24:25 +0000290 * on the meta information, like {@link KrillIndex#search} or
291 * {@link KrillIndex#collect}.
292 *
Nils Diewald884dbcf2015-02-27 17:02:28 +0000293 * @return The result as a {@link Result} object.
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000294 */
Nils Diewald884dbcf2015-02-27 17:02:28 +0000295 public Result apply () {
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000296
Nils Diewald884dbcf2015-02-27 17:02:28 +0000297 // Create new Result object to return
298 Result kr = new Result();
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000299
300 // There were errors
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000301 if (this.hasErrors()) {
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000302 kr.copyNotificationsFrom(this);
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000303 }
304
305 // There was no index
306 else if (this.index == null) {
307 kr.addError(601, "Unable to find index");
308 }
309
310 // Apply search
311 else {
Akronbb5d1732015-06-22 01:22:40 +0200312
313 // This contains meta and matches
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000314 kr = this.index.search(this);
Akronbb5d1732015-06-22 01:22:40 +0200315 // this.getCollection().setIndex(this.index);
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000316 kr.copyNotificationsFrom(this);
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000317 };
Nils Diewaldc6b78752013-12-05 19:05:12 +0000318
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000319 kr.setQuery(this.getQuery());
Akronbb5d1732015-06-22 01:22:40 +0200320 kr.setCollection(this.getCollection());
Akronb1166442015-06-27 00:34:19 +0200321 kr.setMeta(this.getMeta());
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000322
Nils Diewaldd75e6f62015-01-28 23:44:56 +0000323 return kr;
Nils Diewald7cf8c6d2014-05-28 18:37:38 +0000324 };
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000325
Nils Diewaldf5ab4b22015-02-25 20:55:16 +0000326
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000327 /**
328 * Get the associated {@link SpanQuery} deserialization
Nils Diewaldbb33da22015-03-04 16:24:25 +0000329 * (i.e. the internal correspandence to KoralQuery's query
330 * object).
331 *
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000332 * <strong>Warning</strong>: SpanQueries may be lazy deserialized
333 * in future versions of Krill, rendering this API obsolete.
Nils Diewaldbb33da22015-03-04 16:24:25 +0000334 *
Nils Diewaldd37f7e42015-02-27 21:08:22 +0000335 * @return The deserialized {@link SpanQuery} object.
336 */
Nils Diewaldbbd39a52015-02-23 19:56:57 +0000337 @Deprecated
338 public SpanQuery getSpanQuery () {
339 return this.spanQuery;
340 };
Nils Diewald9f310832013-12-06 22:38:55 +0000341};