redis.clients.jedis.search.Query Maven / Gradle / Ivy
Show all versions of jedis_preview Show documentation
package redis.clients.jedis.search;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import redis.clients.jedis.CommandArguments;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.params.IParams;
import redis.clients.jedis.search.SearchProtocol.SearchKeyword;
import redis.clients.jedis.util.LazyRawable;
import redis.clients.jedis.util.SafeEncoder;
/**
* Query represents query parameters and filters to load results from the engine
*/
public class Query implements IParams {
/**
* Filter represents a filtering rules in a query
*/
public abstract static class Filter implements IParams {
public final String property;
public Filter(String property) {
this.property = property;
}
}
/**
* NumericFilter wraps a range filter on a numeric field. It can be inclusive or exclusive
*/
public static class NumericFilter extends Filter {
private final double min;
private final boolean exclusiveMin;
private final double max;
private final boolean exclusiveMax;
public NumericFilter(String property, double min, boolean exclusiveMin, double max, boolean exclusiveMax) {
super(property);
this.min = min;
this.max = max;
this.exclusiveMax = exclusiveMax;
this.exclusiveMin = exclusiveMin;
}
public NumericFilter(String property, double min, double max) {
this(property, min, false, max, false);
}
private byte[] formatNum(double num, boolean exclude) {
return exclude ? SafeEncoder.encode("(" + num) : Protocol.toByteArray(num);
}
@Override
public void addParams(CommandArguments args) {
args.add(SearchKeyword.FILTER.getRaw());
args.add(SafeEncoder.encode(property));
args.add(formatNum(min, exclusiveMin));
args.add(formatNum(max, exclusiveMax));
}
}
/**
* GeoFilter encapsulates a radius filter on a geographical indexed fields
*/
public static class GeoFilter extends Filter {
public static final String KILOMETERS = "km";
public static final String METERS = "m";
public static final String FEET = "ft";
public static final String MILES = "mi";
private final double lon;
private final double lat;
private final double radius;
private final String unit;
public GeoFilter(String property, double lon, double lat, double radius, String unit) {
super(property);
this.lon = lon;
this.lat = lat;
this.radius = radius;
this.unit = unit;
}
@Override
public void addParams(CommandArguments args) {
args.add(SearchKeyword.GEOFILTER.getRaw());
args.add(SafeEncoder.encode(property));
args.add(Protocol.toByteArray(lon));
args.add(Protocol.toByteArray(lat));
args.add(Protocol.toByteArray(radius));
args.add(SafeEncoder.encode(unit));
}
}
public static class Paging {
int offset;
int num;
public Paging(int offset, int num) {
this.offset = offset;
this.num = num;
}
}
public static class HighlightTags {
private final String open;
private final String close;
public HighlightTags(String open, String close) {
this.open = open;
this.close = close;
}
}
/**
* The query's filter list. We only support AND operation on all those filters
*/
private final List _filters = new LinkedList<>();
/**
* The textual part of the query
*/
private final String _queryString;
/**
* The sorting parameters
*/
private final Paging _paging = new Paging(0, 10);
private boolean _verbatim = false;
private boolean _noContent = false;
private boolean _noStopwords = false;
private boolean _withScores = false;
private String _language = null;
private String[] _fields = null;
private String[] _keys = null;
private String[] _returnFields = null;
private FieldName[] returnFieldNames = null;
private String[] highlightFields = null;
private String[] summarizeFields = null;
private String[] highlightTags = null;
private String summarizeSeparator = null;
private int summarizeNumFragments = -1;
private int summarizeFragmentLen = -1;
private String _sortBy = null;
private boolean _sortAsc = true;
private boolean wantsHighlight = false;
private boolean wantsSummarize = false;
private String _scorer = null;
private Map _params = null;
private Integer _dialect;
private int _slop = -1;
private long _timeout = -1;
private boolean _inOrder = false;
private String _expander = null;
public Query() {
this("*");
}
/**
* Create a new index
*
* @param queryString the textual part of the query
*/
public Query(String queryString) {
_queryString = queryString;
}
@Override
public void addParams(CommandArguments args) {
args.add(SafeEncoder.encode(_queryString));
if (_verbatim) {
args.add(SearchKeyword.VERBATIM.getRaw());
}
if (_noContent) {
args.add(SearchKeyword.NOCONTENT.getRaw());
}
if (_noStopwords) {
args.add(SearchKeyword.NOSTOPWORDS.getRaw());
}
if (_withScores) {
args.add(SearchKeyword.WITHSCORES.getRaw());
}
if (_language != null) {
args.add(SearchKeyword.LANGUAGE.getRaw());
args.add(SafeEncoder.encode(_language));
}
if (_scorer != null) {
args.add(SearchKeyword.SCORER.getRaw());
args.add(SafeEncoder.encode(_scorer));
}
if (_fields != null && _fields.length > 0) {
args.add(SearchKeyword.INFIELDS.getRaw());
args.add(Protocol.toByteArray(_fields.length));
for (String f : _fields) {
args.add(SafeEncoder.encode(f));
}
}
if (_sortBy != null) {
args.add(SearchKeyword.SORTBY.getRaw());
args.add(SafeEncoder.encode(_sortBy));
args.add((_sortAsc ? SearchKeyword.ASC : SearchKeyword.DESC).getRaw());
}
if (_paging.offset != 0 || _paging.num != 10) {
args.add(SearchKeyword.LIMIT.getRaw()).add(Protocol.toByteArray(_paging.offset)).add(Protocol.toByteArray(_paging.num));
}
if (!_filters.isEmpty()) {
_filters.forEach(filter -> filter.addParams(args));
}
if (wantsHighlight) {
args.add(SearchKeyword.HIGHLIGHT.getRaw());
if (highlightFields != null) {
args.add(SearchKeyword.FIELDS.getRaw());
args.add(Protocol.toByteArray(highlightFields.length));
for (String s : highlightFields) {
args.add(SafeEncoder.encode(s));
}
}
if (highlightTags != null) {
args.add(SearchKeyword.TAGS.getRaw());
for (String t : highlightTags) {
args.add(SafeEncoder.encode(t));
}
}
}
if (wantsSummarize) {
args.add(SearchKeyword.SUMMARIZE.getRaw());
if (summarizeFields != null) {
args.add(SearchKeyword.FIELDS.getRaw());
args.add(Protocol.toByteArray(summarizeFields.length));
for (String s : summarizeFields) {
args.add(SafeEncoder.encode(s));
}
}
if (summarizeNumFragments != -1) {
args.add(SearchKeyword.FRAGS.getRaw());
args.add(Protocol.toByteArray(summarizeNumFragments));
}
if (summarizeFragmentLen != -1) {
args.add(SearchKeyword.LEN.getRaw());
args.add(Protocol.toByteArray(summarizeFragmentLen));
}
if (summarizeSeparator != null) {
args.add(SearchKeyword.SEPARATOR.getRaw());
args.add(SafeEncoder.encode(summarizeSeparator));
}
}
if (_keys != null && _keys.length > 0) {
args.add(SearchKeyword.INKEYS.getRaw());
args.add(Protocol.toByteArray(_keys.length));
for (String f : _keys) {
args.add(SafeEncoder.encode(f));
}
}
if (_returnFields != null && _returnFields.length > 0) {
args.add(SearchKeyword.RETURN.getRaw());
args.add(Protocol.toByteArray(_returnFields.length));
for (String f : _returnFields) {
args.add(SafeEncoder.encode(f));
}
} else if (returnFieldNames != null && returnFieldNames.length > 0) {
args.add(SearchKeyword.RETURN.getRaw());
// final int returnCountIndex = args.size();
LazyRawable returnCountObject = new LazyRawable();
// args.add(null); // holding a place for setting the total count later.
args.add(returnCountObject); // holding a place for setting the total count later.
int returnCount = 0;
for (FieldName fn : returnFieldNames) {
returnCount += fn.addCommandArguments(args);
}
// args.set(returnCountIndex, Protocol.toByteArray(returnCount));
returnCountObject.setRaw(Protocol.toByteArray(returnCount));
}
if (_params != null && _params.size() > 0) {
args.add(SearchKeyword.PARAMS.getRaw());
args.add(_params.size() * 2);
for (Map.Entry entry : _params.entrySet()) {
args.add(entry.getKey());
args.add(entry.getValue());
}
}
if (_dialect != null) {
args.add(SearchKeyword.DIALECT.getRaw());
args.add(_dialect);
}
if (_slop >= 0) {
args.add(SearchKeyword.SLOP.getRaw());
args.add(_slop);
}
if (_timeout >= 0) {
args.add(SearchKeyword.TIMEOUT.getRaw());
args.add(_timeout);
}
if (_inOrder) {
args.add(SearchKeyword.INORDER.getRaw());
}
if (_expander != null) {
args.add(SearchKeyword.EXPANDER.getRaw());
args.add(SafeEncoder.encode(_expander));
}
}
/**
* Limit the results to a certain offset and limit
*
* @param offset the first result to show, zero based indexing
* @param limit how many results we want to show
* @return the query itself, for builder-style syntax
*/
public Query limit(Integer offset, Integer limit) {
_paging.offset = offset;
_paging.num = limit;
return this;
}
/**
* Add a filter to the query's filter list
*
* @param f either a numeric or geo filter object
* @return the query itself
*/
public Query addFilter(Filter f) {
_filters.add(f);
return this;
}
/**
* Set the query to verbatim mode, disabling stemming and query expansion
*
* @return the query object
*/
public Query setVerbatim() {
this._verbatim = true;
return this;
}
public boolean getNoContent() {
return _noContent;
}
/**
* Set the query not to return the contents of documents, and rather just return the ids
*
* @return the query itself
*/
public Query setNoContent() {
this._noContent = true;
return this;
}
/**
* Set the query not to filter for stopwords. In general this should not be used
*
* @return the query object
*/
public Query setNoStopwords() {
this._noStopwords = true;
return this;
}
public boolean getWithScores() {
return _withScores;
}
/**
* Set the query to return a factored score for each results. This is useful to merge results from
* multiple queries.
*
* @return the query object itself
*/
public Query setWithScores() {
this._withScores = true;
return this;
}
/**
* Set the query language, for stemming purposes
*
* See http://redisearch.io for documentation on languages and stemming
*
* @param language a language.
*
* @return the query object itself
*/
public Query setLanguage(String language) {
this._language = language;
return this;
}
/**
* Set the query custom scorer
*
* See http://redisearch.io for documentation on extending RediSearch
*
* @param scorer a custom scorer.
*
* @return the query object itself
*/
public Query setScorer(String scorer) {
this._scorer = scorer;
return this;
}
/**
* Limit the query to results that are limited to a specific set of fields
*
* @param fields a list of TEXT fields in the schemas
* @return the query object itself
*/
public Query limitFields(String... fields) {
this._fields = fields;
return this;
}
/**
* Limit the query to results that are limited to a specific set of keys
*
* @param keys a list of TEXT fields in the schemas
* @return the query object itself
*/
public Query limitKeys(String... keys) {
this._keys = keys;
return this;
}
/**
* Result's projection - the fields to return by the query
*
* @param fields a list of TEXT fields in the schemas
* @return the query object itself
*/
public Query returnFields(String... fields) {
this._returnFields = fields;
this.returnFieldNames = null;
return this;
}
/**
* Result's projection - the fields to return by the query
*
* @param fields a list of TEXT fields in the schemas
* @return the query object itself
*/
public Query returnFields(FieldName... fields) {
this.returnFieldNames = fields;
this._returnFields = null;
return this;
}
public Query highlightFields(HighlightTags tags, String... fields) {
if (fields == null || fields.length > 0) {
highlightFields = fields;
}
if (tags != null) {
highlightTags = new String[]{tags.open, tags.close};
} else {
highlightTags = null;
}
wantsHighlight = true;
return this;
}
public Query highlightFields(String... fields) {
return highlightFields(null, fields);
}
public Query summarizeFields(int contextLen, int fragmentCount, String separator, String... fields) {
if (fields == null || fields.length > 0) {
summarizeFields = fields;
}
summarizeFragmentLen = contextLen;
summarizeNumFragments = fragmentCount;
summarizeSeparator = separator;
wantsSummarize = true;
return this;
}
public Query summarizeFields(String... fields) {
return summarizeFields(-1, -1, null, fields);
}
/**
* Set the query to be sorted by a Sortable field defined in the schema
*
* @param field the sorting field's name
* @param ascending if set to true, the sorting order is ascending, else descending
* @return the query object itself
*/
public Query setSortBy(String field, boolean ascending) {
_sortBy = field;
_sortAsc = ascending;
return this;
}
/**
* Parameters can be referenced in the query string by a $ , followed by the parameter name,
* e.g., $user , and each such reference in the search query to a parameter name is substituted
* by the corresponding parameter value.
*
* @param name
* @param value can be String, long or float
* @return the query object itself
*/
public Query addParam(String name, Object value) {
if (_params == null) {
_params = new HashMap<>();
}
_params.put(name, value);
return this;
}
/**
* Set the dialect version to execute the query accordingly
*
* @param dialect integer
* @return the query object itself
*/
public Query dialect(int dialect) {
_dialect = dialect;
return this;
}
/**
* This method will not replace the dialect if it has been already set.
* @param dialect dialect
* @return this
*/
public Query dialectOptional(int dialect) {
if (dialect != 0 && this._dialect == null) {
this._dialect = dialect;
}
return this;
}
/**
* Set the slop to execute the query accordingly
*
* @param slop integer
* @return the query object itself
*/
public Query slop(int slop) {
_slop = slop;
return this;
}
/**
* Set the timeout to execute the query accordingly
*
* @param timeout long
* @return the query object itself
*/
public Query timeout(long timeout) {
_timeout = timeout;
return this;
}
/**
* Set the query terms appear in the same order in the document as in the query, regardless of the offsets between them
*
* @return the query object
*/
public Query setInOrder() {
this._inOrder = true;
return this;
}
/**
* Set the query to use a custom query expander instead of the stemmer
*
* @param field the expander field's name
* @return the query object itself
*/
public Query setExpander(String field) {
_expander = field;
return this;
}
}