blob: 4b1b6427f8cb8f4b55eba2934c1a472d2d76868e [file] [log] [blame]
package de.ids_mannheim.korap;
import java.io.IOException;
import java.util.List;
import org.apache.lucene.search.spans.SpanQuery;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.ids_mannheim.korap.cache.VirtualCorpusCache;
import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
import de.ids_mannheim.korap.response.Response;
import de.ids_mannheim.korap.response.Result;
import de.ids_mannheim.korap.response.VirtualCorpusResponse;
import de.ids_mannheim.korap.util.QueryException;
/**
* <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 final ObjectMapper mapper = new ObjectMapper();
/**
* 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.fromKoral(query);
};
/**
* Construct a new Krill object,
* consuming a KoralQuery {@link JsonNode} object.
*
* @param query
* The KoralQuery {@link JsonNode} object.
*/
public Krill (JsonNode query) {
this.fromKoral(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 fromKoral (final String query) {
// Parse query string
try {
this.request = mapper.readTree(query);
this.fromKoral(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 fromKoral (JsonNode json) {
// Parse "query" attribute
if (json.has("query")) {
try {
final KrillQuery kq = new KrillQuery("tokens");
this.setQuery(kq);
final SpanQueryWrapper qw = kq.fromKoral(json.get("query"));
// Koral messages are moved to the Krill object
this.moveNotificationsFrom(kq);
// Throw an error, in case the query matches everywhere
if (qw.isEmpty()) {
this.addError(780, "This query matches everywhere");
}
else if (qw.isNull()) {
this.addError(783, "This query can't match anywhere");
}
else {
// Serialize a Lucene SpanQuery based on the SpanQueryWrapper
this.spanQuery = qw.toQuery();
// TODO: Make these information query rewrites
// 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().fromKoral(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;
VirtualCorpusCache.setIndexInfo(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) {
VirtualCorpusCache.setIndexInfo(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;
}
//EM
public void setSpanQuery (SpanQuery sq) {
this.spanQuery = sq;
}
public JsonNode retrieveFieldValues (String corpusQuery, KrillIndex index,
String fieldName) {
KrillCollection kc = new KrillCollection(corpusQuery);
List<String> fieldValues = index.getFieldVector(fieldName, kc);
VirtualCorpusResponse r = new VirtualCorpusResponse();
return r.createKoralQueryForField(fieldName, fieldValues);
}
};