blob: 01102a1e02d267abe73ee1b7baff3475bcc64d27 [file] [log] [blame]
package de.ids_mannheim.korap.response;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import de.ids_mannheim.korap.index.PositionsToOffset;
// Remove:
import de.ids_mannheim.korap.response.SearchContext;
import de.ids_mannheim.korap.response.Response;
import de.ids_mannheim.korap.response.Match;
import de.ids_mannheim.korap.Krill;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/*
TODO: Reuse the Krill code for data serialization!
*/
/**
* Response class for search results.
*
* TODO: Synopsis and let it base on KoralQuery
*
* @author diewald
* @see Response
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public final class Result extends Krill {
ObjectMapper mapper = new ObjectMapper();
@JsonIgnore
public static final short ITEMS_PER_PAGE = 25;
public static final short ITEMS_PER_PAGE_MAX = 100;
private int startIndex = 0;
private String serialQuery;
private List<Match> matches;
private SearchContext context;
private short itemsPerPage = ITEMS_PER_PAGE, itemsPerResource = 0;
private JsonNode request;
// Logger
// This is Match instead of Result!
private final static Logger log = LoggerFactory.getLogger(Match.class);
/**
* Construct a new Result object.
*/
public Result () {
mapper.enable(SerializationFeature.INDENT_OUTPUT);
};
/**
* Construct a new Result object.
*
* @param serialQuery
* Query representation as a string.
* @param startIndex
* Offset position in match array.
* @param itemsPerPage
* Number of matches per page.
* @param context
* Requested {@link SearchContext}
*/
public Result (String query, int startIndex, short itemsPerPage,
SearchContext context) {
mapper.enable(SerializationFeature.INDENT_OUTPUT);
// mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// mapper.disable(SerializationFeature.WRITE_NULL_MAP_VALUES);
this.matches = new ArrayList<>(itemsPerPage);
this.serialQuery = query;
this.startIndex = startIndex;
this.itemsPerPage = (itemsPerPage > ITEMS_PER_PAGE_MAX || itemsPerPage < 1) ? ITEMS_PER_PAGE
: itemsPerPage;
this.context = context;
};
/**
* Add a new match to the result set.
*
* @param match
* A {@link Match} to add.
*/
public void add (Match km) {
this.matches.add(km);
};
/**
* Get the number of items (documents) shown per page.
*
* @return Number of items shown per page.
*/
public short getItemsPerPage () {
return this.itemsPerPage;
};
/**
* Set the number of items (documents) shown per page.
*
* @param count
* Number of items shown per page.
* @return {@link Result} object for chaining.
*/
public Result setItemsPerPage (short count) {
this.itemsPerPage = count;
return this;
};
/**
* Get serialized query as a {@link JsonNode}.
*
* @return {@link JsonNode} representation of the query object.
*/
public JsonNode getRequest () {
return this.request;
};
/**
* Set serialized query as a {@link JsonNode}.
*
* @param request
* {@link JsonNode} representation of the query object.
* @return {@link Result} object for chaining.
*/
public Result setRequest (JsonNode request) {
this.request = request;
return this;
};
/**
* Get the number of items shown per resource (document).
* Defaults to <tt>0</tt>, which is infinite.
*
* @return The number of items shown per resource.
*/
public short getItemsPerResource () {
return this.itemsPerResource;
};
/**
* Set the number of items (matches) shown per resource (text).
* Defaults to <tt>0</tt>, which is infinite.
*
* @param value
* The number of items shown per resource.
* @return {@link Result} object for chaining.
*/
public Result setItemsPerResource (short value) {
this.itemsPerResource = value;
return this;
};
/**
* Set the number of items (matches) shown per resource
* (document).
* Defaults to <tt>0</tt>, which is infinite.
*
* @param value
* The number of items shown per resource.
* @return {@link Result} object for chaining.
*/
public Result setItemsPerResource (int value) {
this.itemsPerResource = (short) value;
return this;
};
/**
* Get the string representation of the search query.
*
* @return The string representation of the search query.
*/
public String getSerialQuery () {
return this.serialQuery;
};
/**
* Get a certain {@link Match} by index.
*
* @param index
* The numerical index of the match,
* starts with <tt>0</tt>.
* @return The {@link Match} object.
*/
@JsonIgnore
public Match getMatch (int index) {
return this.matches.get(index);
};
/**
* Get the list of {@link Match} matches.
*
* @return The list of {@link Match} objects.
*/
public List<Match> getMatches () {
return this.matches;
};
/**
* Get the number of the first match in the result set
* (<i>aka</i> the offset). Starts with <tt>0</tt>.
*
* @return The index number of the first match in the result set.
*/
public int getStartIndex () {
return startIndex;
};
/**
* Get the context parameters of the search by means of a
* {@link SearchContext} object.
*
* @return The {@link SearchContext} object.
*/
public SearchContext getContext () {
return this.context;
};
/**
* Set the context parameters of the search by means of a
* {@link SearchContext} object.
*
* @param context
* The {@link SearchContext} object providing
* search context parameters.
* @return {@link Result} object for chaining.
*/
public Result setContext (SearchContext context) {
this.context = context;
return this;
};
/**
* Serialize the result set as a {@link JsonNode}.
*
* @return {@link JsonNode} representation of the search results.
*/
public JsonNode toJsonNode () {
ObjectNode json = (ObjectNode) mapper.valueToTree(super.toJsonNode());
this._addMeta(json);
// Add matches
if (this.matches != null)
json.putPOJO("matches", this.getMatches());
return json;
};
/**
* Stringifies the matches to give a brief overview on
* the result. Mainly used for testing.
*
* @return The stringified matches
*/
public String getOverview () {
StringBuilder sb = new StringBuilder();
sb.append("Search for: ").append(this.serialQuery).append("\n");
int i = 1;
// Add matches as bracket strings
for (Match km : this.getMatches())
sb.append(i++).append(": ").append(km.getSnippetBrackets())
.append(" (Doc ").append(km.getLocalDocID()).append(")\n");
return sb.toString();
};
// For Collocation Analysis API
@Deprecated
public String toTokenListJsonString () {
ObjectNode json = (ObjectNode) mapper.valueToTree(super.toJsonNode());
// Meta data for paging
ObjectNode meta = json.has("meta") ? (ObjectNode) json.get("meta")
: (ObjectNode) json.putObject("meta");
if (this.context != null)
meta.put("context", this.getContext().toJsonNode());
meta.put("itemsPerPage", this.itemsPerPage);
meta.put("startIndex", this.startIndex);
if (this.itemsPerResource > 0)
meta.put("itemsPerResource", this.itemsPerResource);
if (this.serialQuery != null)
meta.put("serialQuery", this.serialQuery);
ArrayNode array = json.putArray("matches");
// Add matches as token lists
for (Match km : this.getMatches())
array.add(km.toTokenList());
try {
return mapper.writeValueAsString(json);
}
catch (Exception e) {
log.warn(e.getLocalizedMessage());
};
return "{}";
};
private void _addMeta (ObjectNode json) {
ObjectNode meta = (ObjectNode) this.getMeta().toJsonNode();
// Lucene query for debugging purposes
if (this.serialQuery != null)
meta.put("serialQuery", this.serialQuery);
// This may override count
meta.put("itemsPerPage", this.itemsPerPage);
if (json.has("meta")) {
((ObjectNode) json.get("meta")).putAll(meta);
}
else {
json.put("meta", meta);
};
};
};