All Downloads are FREE. Search and download functionalities are using the official Maven repository.

redis.clients.jedis.search.Query Maven / Gradle / Ivy

There is a newer version: 5.2.0
Show newest version
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.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 boolean _withPayloads = 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 byte[] _payload = null;
  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 int _dialect = 0;
  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 (_withPayloads) {
      args.add(SearchKeyword.WITHPAYLOADS.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 (_payload != null) {
      args.add(SearchKeyword.PAYLOAD.getRaw());
      args.add(_payload);
    }

    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 != 0) {
      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 payload to be evaluated by the scoring function
   *
   * @return the query object itself
   * @deprecated Since RediSearch 2.0.0, PAYLOAD option is deprecated.
   */
  @Deprecated
  public Query setPayload(byte[] payload) {
    _payload = payload;
    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;
  }

  @Deprecated
  public boolean getWithPayloads() {
    return _withPayloads;
  }

  /**
   * Set the query to return object payloads, if any were given
   *
   * @return the query object itself
   * @deprecated Since RediSearch 2.0.0, WITHPAYLOADS option is deprecated.
   */
  @Deprecated
  public Query setWithPayload() {
    this._withPayloads = 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; } /** * 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; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy