blob: 72373dbcb48643195f6950ec3360c2ccdfa9b038 [file] [log] [blame]
package de.ids_mannheim.korap;
import java.io.*;
import java.util.*;
import org.apache.lucene.search.spans.SpanQuery;
import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
import de.ids_mannheim.korap.KrillIndex;
import de.ids_mannheim.korap.response.Result;
import de.ids_mannheim.korap.util.QueryException;
import de.ids_mannheim.korap.response.Notifications;
import de.ids_mannheim.korap.response.Response;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>Krill is a corpus data retrieval index using Lucene for
* Look-Ups.</p>
*
* <p>
* It is the reference implementation for KoralQuery consumption,
* and this class acts as the central point for consuming and
* responding to KoralQuery requests.
* </p>
*
* <p>
* The processing of the collection section of the request is
* delegated
* to {@link KrillCollection}, the query section to {@link KrillQuery}
* ,
* and the meta section to {@link KrillMeta}.
* </p>
*
* <blockquote><pre>
* // Create or receive a KoralQuery JSON string
* String koral = "{\"query\":{...}, \"collection\":{...}, ... }";
*
* // Create a new krill search object by passing the Query
* Krill krill = new Krill(koral);
*
* // Apply the query to an index and receive a search result
* // This may invoke different actions depending on the request
* Result result = krill.setIndex(new KrillIndex()).apply();
* </pre></blockquote>
*
* @author diewald
* @author margaretha
*
* @see KrillCollection
* @see KrillQuery
* @see KrillMeta
* @see KrillIndex
*/
// TODO: Use a krill.properties configuration file
// TODO: Reuse passed JSON object instead of creating a new response!
public class Krill extends Response {
private KrillIndex index;
private SpanQuery spanQuery;
private JsonNode request;
private String spanContext;
private final ObjectMapper mapper = new ObjectMapper();
// Logger
private final static Logger log = LoggerFactory.getLogger(Krill.class);
/**
* Construct a new Krill object.
*/
public Krill () {};
/**
* Construct a new Krill object,
* consuming a KoralQuery json string.
*
* @param query
* The KoralQuery json string.
*/
public Krill (String query) {
this.fromJson(query);
};
/**
* Construct a new Krill object,
* consuming a KoralQuery {@link JsonNode} object.
*
* @param query
* The KoralQuery {@link JsonNode} object.
*/
public Krill (JsonNode query) {
this.fromJson(query);
};
/**
* Construct a new Krill object,
* consuming a {@link SpanQueryWrapper} object.
*
* @param query
* The {@link SpanQueryWrapper} object.
*/
public Krill (SpanQueryWrapper query) {
try {
this.spanQuery = query.toQuery();
}
// Add the error to the KoralQuery response
catch (QueryException q) {
this.addError(q.getErrorCode(), q.getMessage());
};
};
/**
* Construct a new Krill object,
* consuming a {@link SpanQuery} object.
*
* @param query
* The {@link SpanQuery} object.
*/
public Krill (SpanQuery query) {
this.spanQuery = query;
};
/**
* Parse KoralQuery as a json string.
*
* @param query
* The KoralQuery json string.
* @return The {@link Krill} object for chaining.
* @throws QueryException
*/
public Krill fromJson (final String query) {
// Parse query string
try {
this.request = mapper.readTree(query);
this.fromJson(this.request);
}
// Unable to parse JSON
catch (IOException e) {
this.addError(621, "Unable to parse JSON");
};
return this;
};
/**
* Parse KoralQuery as a {@link JsonNode} object.
*
* @param query
* The KoralQuery {@link JsonNode} object.
* @return The {@link Krill} object for chaining.
* @throws QueryException
*/
public Krill fromJson (JsonNode json) {
// Parse "query" attribute
if (json.has("query")) {
try {
final KrillQuery kq = new KrillQuery("tokens");
this.setQuery(kq);
final SpanQueryWrapper qw = kq.fromJson(json.get("query"));
// Throw an error, in case the query matches everywhere
if (qw.isEmpty())
this.addError(780, "This query matches everywhere");
else {
// Serialize a Lucene SpanQuery based on the SpanQueryWrapper
this.spanQuery = qw.toQuery();
// Throw a warning in case the root object is optional
if (qw.isOptional())
this.addWarning(781, "Optionality of query is ignored");
// Throw a warning in case the root object is negative
if (qw.isNegative())
this.addWarning(782, "Exclusivity of query is ignored");
};
}
catch (QueryException q) {
this.addError(q.getErrorCode(), q.getMessage());
};
}
else
this.addError(700, "No query given");
// <legacycode>
if (json.has("warning") && json.get("warning").asText().length() > 0) {
this.addWarning(799, json.get("warning").asText());
};
// </legacycode>
// Copy notifications from request
this.copyNotificationsFrom(json);
// Parse "collection" or "collections" attribute
try {
if (json.has("collection")) {
final JsonNode collNode = json.get("collection");
// TODO: Temporary
if (collNode.fieldNames().hasNext()) {
this.setCollection(new KrillCollection().fromJson(collNode));
};
}
else if (json.has("collections")) {
this.addError(899,
"Collections are not supported anymore in favour of a single collection");
};
}
catch (QueryException q) {
this.addError(q.getErrorCode(), q.getMessage());
};
// Parse "meta" attribute
// !this.hasErrors() &&
if (json.has("meta"))
this.setMeta(new KrillMeta(json.get("meta")));
return this;
};
/**
* Get the associated {@link KrillIndex} object.
*
* @return The associated {@link KrillIndex} object.
*/
public KrillIndex getIndex () {
return this.index;
};
/**
* Set the {@link KrillIndex} object.
*
* @param index
* The associated {@link KrillIndex} object.
* @return The {@link Krill} object for chaining.
*/
public Krill setIndex (KrillIndex index) {
this.index = index;
return this;
};
/**
* Apply the KoralQuery to an index.
* This may invoke different actions depending
* on the meta information, like {@link KrillIndex#search} or
* {@link KrillIndex#collect}.
*
* @param index
* The {@link KrillIndex} the search should be applyied
* to.
* @return The result as a {@link Result} object.
*/
public Result apply (KrillIndex index) {
return this.setIndex(index).apply();
};
/**
* Apply the KoralQuery to an index.
* This may invoke different actions depending
* on the meta information, like {@link KrillIndex#search} or
* {@link KrillIndex#collect}.
*
* @return The result as a {@link Result} object.
*/
public Result apply () {
// Create new Result object to return
Result kr = new Result();
// There were errors
if (this.hasErrors()) {
kr.copyNotificationsFrom(this);
}
// There was no index
else if (this.index == null) {
kr.addError(601, "Unable to find index");
}
// Apply search
else {
// This contains meta and matches
kr = this.index.search(this);
// this.getCollection().setIndex(this.index);
kr.copyNotificationsFrom(this);
};
kr.setQuery(this.getQuery());
kr.setCollection(this.getCollection());
kr.setMeta(this.getMeta());
return kr;
};
/**
* Get the associated {@link SpanQuery} deserialization
* (i.e. the internal correspandence to KoralQuery's query
* object).
*
* <strong>Warning</strong>: SpanQueries may be lazy deserialized
* in future versions of Krill, rendering this API obsolete.
*
* @return The deserialized {@link SpanQuery} object.
*/
@Deprecated
public SpanQuery getSpanQuery () {
return this.spanQuery;
};
/**
* Set the SpanQuery by means of a {@link SpanQueryWrapper} object
* (i.e. the internal correspandence to KoralQuery's query
* object).
*
* <strong>Warning</strong>: SpanQueries may be lazy deserialized
* in future versions of Krill, rendering this API obsolete.
*
* @param query
* The {@link SpanQueryWrapper} to unwrap
* the {@link SpanQuery} object.
* @return The {@link Krill} object for chaining.
*/
@Deprecated
public Krill setSpanQuery (SpanQueryWrapper sqwi) {
try {
this.spanQuery = sqwi.toQuery();
}
catch (QueryException q) {
this.addError(q.getErrorCode(), q.getMessage());
};
return this;
};
/**
* Set the {@link SpanQuery} object
* (i.e. the internal correspandence to KoralQuery's query
* object).
*
* <strong>Warning</strong>: SpanQueries may be lazy deserialized
* in future versions of Krill, rendering this API obsolete.
*
* @param query
* The {@link SpanQuery} object.
* @return The {@link Krill} object for chaining.
*/
@Deprecated
public Krill setSpanQuery (SpanQuery sq) {
this.spanQuery = sq;
return this;
};
// Requests are out - queries will be mirrored completely
@Deprecated
public JsonNode getRequest () {
return this.request;
};
};