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

com.groupbyinc.api.Query Maven / Gradle / Ivy

There is a newer version: 3.2.0
Show newest version
package com.groupbyinc.api;

import com.groupbyinc.api.model.Bias;
import com.groupbyinc.api.model.Biasing;
import com.groupbyinc.api.model.CustomUrlParam;
import com.groupbyinc.api.model.MatchStrategy;
import com.groupbyinc.api.model.Navigation;
import com.groupbyinc.api.model.NumericBoost;
import com.groupbyinc.api.model.PartialMatchRule;
import com.groupbyinc.api.model.Refinement;
import com.groupbyinc.api.model.Sort;
import com.groupbyinc.api.model.refinement.RefinementRange;
import com.groupbyinc.api.model.refinement.RefinementValue;
import com.groupbyinc.api.model.sort.FieldSort;
import com.groupbyinc.api.model.sort.SortByIds;
import com.groupbyinc.api.request.RefinementsRequest;
import com.groupbyinc.api.request.Request;
import com.groupbyinc.api.request.RestrictNavigation;
import com.groupbyinc.api.request.SelectedRefinement;
import com.groupbyinc.api.request.refinement.SelectedRefinementRange;
import com.groupbyinc.api.request.refinement.SelectedRefinementValue;
import com.groupbyinc.common.apache.commons.collections4.CollectionUtils;
import com.groupbyinc.common.apache.commons.lang3.StringUtils;
import com.groupbyinc.common.jackson.Mappers;
import com.groupbyinc.common.jregex.Pattern;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

public class Query {

  private static final Pattern REFINEMENTS_SPLITTER_PATTERN = new Pattern("~((?=[\\w.]*[=:]))");
  private static final String[] EMPTY_REFINEMENTS = new String[]{};
  private static final Logger LOG = Logger.getLogger(Query.class.getName());
  private static final String DOTS = "\\.\\.";

  private List sort = new ArrayList();
  private MatchStrategy matchStrategy;
  private boolean wildcardSearchEnabled;
  private List includedNavigations = new ArrayList();
  private List excludedNavigations = new ArrayList();
  private String sessionId;
  private String visitorId;
  private String query;
  private int skip = 0;
  private int pageSize = 10;
  private String collection;
  private String area;
  private String biasingProfile;
  private String matchStrategyName;
  private String language;
  private Map queryUrlParams = new HashMap();
  private List customUrlParams = new ArrayList();
  private LinkedHashMap navigations = new LinkedHashMap();
  private List fields = new ArrayList();
  private List orFields = new ArrayList();
  private List subsetIds = new ArrayList();
  private boolean pruneRefinements = true;
  private boolean returnBinary = true;
  private boolean disableAutocorrection = false;
  private RestrictNavigation restrictNavigation;
  private Biasing biasing = new Biasing();

  /**
   * 
   * Used internally by the bridge object to generate the JSON that is sent to the search service.
   * 
   *
   * @param clientKey
   *         The client key used to authenticate this request.
   *
   * @return A JSON representation of this query object.
   */
  public String getBridgeJson(String clientKey) {
    return requestToJson(populateRequest(clientKey));
  }

  private static String requestToJson(Request request) {
    try {
      return Mappers.writeValueAsString(request);
    } catch (IllegalArgumentException e) {
      return "{}";
    }
  }

  private Request populateRequest(String clientKey) {
    Request request = new Request();

    request.setSessionId(sessionId);
    request.setVisitorId(visitorId);
    request.setIncludedNavigations(includedNavigations);
    request.setExcludedNavigations(excludedNavigations);
    request.setClientKey(clientKey);
    request.setArea(area);
    request.setCollection(collection);
    request.setQuery(query);
    request.setFields(fields);
    request.setOrFields(orFields);
    request.setLanguage(language);
    request.setBiasingProfile(biasingProfile);
    request.setMatchStrategyName(matchStrategyName);
    request.setPageSize(pageSize);
    request.setSkip(skip);
    request.setBiasing(convertBiasing(biasing));
    request.setCustomUrlParams(getCustomUrlParams());
    request.setRefinements(generateSelectedRefinements(navigations));
    request.setRestrictNavigation(convertRestrictNavigation());
    request.setWildcardSearchEnabled(isWildcardSearchEnabled());
    if (CollectionUtils.isNotEmpty(sort)) {
      for (Sort s : sort) {
        request.setSort(convertSort(s));
      }
    }
    request.setMatchStrategy(convertPartialMatchStrategy(matchStrategy));

    if (!pruneRefinements) {
      request.setPruneRefinements(false);
    }
    if (returnBinary) {
      request.setReturnBinary(true);
    }
    if (disableAutocorrection) {
      request.setDisableAutocorrection(true);
    }
    return request;
  }

  protected static com.groupbyinc.api.request.Biasing convertBiasing(Biasing biasing) {
    com.groupbyinc.api.request.Biasing convertedBiasing = new com.groupbyinc.api.request.Biasing();
    boolean hasData = false;
    if (biasing != null) {
      convertedBiasing.setAugmentBiases(biasing.isAugmentBiases());
      if (CollectionUtils.isNotEmpty(biasing.getRestrictToIds())) {
        convertedBiasing.setRestrictToIds(new ArrayList(biasing.getRestrictToIds()));
        hasData = true;
      }
      if (CollectionUtils.isNotEmpty(biasing.getBringToTop())) {
        convertedBiasing.setBringToTop(new ArrayList(biasing.getBringToTop()));
        hasData = true;
      }
      if (CollectionUtils.isNotEmpty(biasing.getBiases())) {
        convertedBiasing.setBiases(new ArrayList(convertBiases(biasing.getBiases())));
        hasData = true;
      }
      if (biasing.getInfluence() != null) {
        convertedBiasing.setInfluence(biasing.getInfluence());
        hasData = true;
      }
      if (CollectionUtils.isNotEmpty(biasing.getNumericBoosts())) {
        convertedBiasing.setNumericBoosts(convertNumericBoosts(biasing.getNumericBoosts()));
        hasData = true;
      }
    }
    return hasData ? convertedBiasing : null;
  }

  /**
   * @return A list of custom url params
   */
  public List getCustomUrlParams() {
    return customUrlParams;
  }

  private List generateSelectedRefinements(LinkedHashMap navigations) {
    List refinements = new ArrayList();
    for (Navigation n : navigations.values()) {
      for (Refinement r : n.getRefinements()) {
        switch (r.getType()) {
          case Range: {
            RefinementRange rr = (RefinementRange) r;
            refinements.add(new SelectedRefinementRange().setNavigationName(n.getName()).setLow(rr.getLow()).setHigh(rr.getHigh()).setExclude(rr.getExclude()));
            break;
          }
          case Value: {
            RefinementValue rv = (RefinementValue) r;
            refinements.add(new SelectedRefinementValue().setNavigationName(n.getName()).setValue(rv.getValue()).setExclude(rv.getExclude()));
            break;
          }
          default:
            break;
        }
      }
    }
    return refinements;
  }

  private RestrictNavigation convertRestrictNavigation() {
    return restrictNavigation == null ? null : new RestrictNavigation().setName(restrictNavigation.getName()).setCount(restrictNavigation.getCount());
  }

  public boolean isWildcardSearchEnabled() {
    return wildcardSearchEnabled;
  }

  protected static com.groupbyinc.api.request.Sort convertSort(Sort sort) {
    com.groupbyinc.api.request.Sort convertedSort = null;
    if (sort != null) {
      if (sort instanceof FieldSort) {
        FieldSort fieldSort = (FieldSort) sort;
        com.groupbyinc.api.request.sort.FieldSort converted = new com.groupbyinc.api.request.sort.FieldSort().setField(fieldSort.getField());
        switch (fieldSort.getOrder()) {
          case Ascending:
            converted.setOrder(com.groupbyinc.api.request.Sort.Order.Ascending);
            break;
          case Descending:
            converted.setOrder(com.groupbyinc.api.request.Sort.Order.Descending);
            break;
          default:
            break;
        }
        convertedSort = converted;
      } else if (sort instanceof SortByIds) {
        SortByIds sortByIds = (SortByIds) sort;
        convertedSort = new com.groupbyinc.api.request.sort.SortByIds().setIds(sortByIds.getIds());
      }
    }
    return convertedSort;
  }

  protected static com.groupbyinc.api.request.MatchStrategy convertPartialMatchStrategy(MatchStrategy strategy) {
    com.groupbyinc.api.request.MatchStrategy convertedStrategy = null;
    if (strategy != null && CollectionUtils.isNotEmpty(strategy.getRules())) {
      convertedStrategy = new com.groupbyinc.api.request.MatchStrategy();
      for (PartialMatchRule r : strategy.getRules()) {
        convertedStrategy.addRule(r);
      }
    }
    return convertedStrategy;
  }

  private static List convertBiases(List biases) {
    List convertedBiases = new ArrayList();
    for (Bias bias : biases) {
      convertedBiases.add(convertBias(bias));
    }
    return convertedBiases;
  }

  private static com.groupbyinc.api.request.Bias convertBias(Bias bias) {
    return new com.groupbyinc.api.request.Bias().setName(bias.getName()).setContent(bias.getContent()).setStrength(convertStrength(bias.getStrength()));
  }

  private static List convertNumericBoosts(List numericBoosts) {
    List convertedBiases = new ArrayList();
    for (NumericBoost numericBoost : numericBoosts) {
      convertedBiases.add(convertNumericBoost(numericBoost));
    }
    return convertedBiases;
  }

  private static com.groupbyinc.api.request.NumericBoost convertNumericBoost(NumericBoost numericBoost) {
    return new com.groupbyinc.api.request.NumericBoost().setName(numericBoost.getName()).setStrength(numericBoost.getStrength()).setInverted(numericBoost.isInverted());
  }

  private static com.groupbyinc.api.request.Bias.Strength convertStrength(Bias.Strength strength) {
    com.groupbyinc.api.request.Bias.Strength convertedStrength;

    try {
      convertedStrength = com.groupbyinc.api.request.Bias.Strength.valueOf(strength.name());
    } catch (IllegalArgumentException e) {
      LOG.warning("Could not convert bias strength: " + strength.name());
      convertedStrength = com.groupbyinc.api.request.Bias.Strength.Leave_Unchanged;
    }

    return convertedStrength;
  }

  /**
   * 
   * Indicate if the *(star) character in the search string should be treated as a wildcard prefix search.
   * For example, `sta*` will match `star` and `start`.
   *
   * JSON Reference:
   *
   *     { "wildcardSearchEnabled" : true }
   *
   * 
   *
   * @param wildcardSearchEnabled true to enable wildcard search, false otherwise.
   * @return the Query object itself
   */
  public Query setWildcardSearchEnabled(boolean wildcardSearchEnabled) {
    this.wildcardSearchEnabled = wildcardSearchEnabled;
    return this;
  }

  /**
   * 
   * Used internally by the bridge object to generate the JSON that is sent to the search service.
   * 
   *
   * @param clientKey
   *         The client key used to authenticate this request.
   *
   * @return A JSON representation of this query object.
   */
  public String getBridgeRefinementsJson(String clientKey, String navigationName) {
    RefinementsRequest request = new RefinementsRequest();
    request.setOriginalQuery(populateRequest(clientKey));
    request.setNavigationName(navigationName);
    return requestToJson(request);
  }

  private static String requestToJson(RefinementsRequest request) {
    try {
      return Mappers.writeValueAsString(request);
    } catch (IllegalArgumentException e) {
      return "{}";
    }
  }

  /**
   * @return The current search string.
   */
  public String getQuery() {
    return query;
  }

  /**
   * 
   * Set a search string. If query is blank all records are considered. There are some limits enforced on the search string, it:
   *
   *          - must not exceed 60 characters
   *          - must not exceed 10 terms.
   *
   * If the limits are exceeded, the search string is truncated until all limits are satisfied. For example, the following search string
   *
   *      The quick brown fox jumps over the colorful wide bridge into the cold river.
   *
   * will get truncated to:
   *
   *      The quick brown fox jumps over the colorful wide bridge
   *
   * The terms `the`, `cold`, and `river` were truncated because the term limit was exceed, and `into` was also removed because the
   * resulting string exceeded the character limit. Stop words are included in the string when determining if limits are exceeded. If
   * there is only one term and it exceeds the character limit, the query will fail.
   *
   * JSON Reference:
   *
   *     { "query": "gloves" }
   *
   * 
   *
   * @param query
   *         The search string to fire against the engine.
   *
   * @return
   */
  public Query setQuery(String query) {
    this.query = query;
    return this;
  }

  /**
   * @return The data collection
   *
   * @deprecated since 2.0, use getCollection instead.
   */
  public String getSubCollection() {
    return collection;
  }

  /**
   * @param subCollection
   *         The string representation of a collection query.
   *
   * @return
   *
   * @deprecated since 2.0, use setCollection instead.
   */
  public Query setSubCollection(String subCollection) {
    collection = subCollection;
    return this;
  }

  /**
   * @return The data collection
   */
  public String getCollection() {
    return collection;
  }

  /**
   * 
   * The collection to use.  If you have uploaded additional data into collections apart from the default
   * collection using the stream tool, you can access them by specifying them here.
   * You can also search across multiple collections. It is important to note that relevancy is affected across
   * collections and it is recommended that collections be modeled so that cross-collection searching is not required.
   * As an example, to search across FAQs and Manuals you would use "FAQs|Manuals".
   *
   * JSON Reference:
   *
   *     { "collection": "FAQs" }
   *     { "collection": "FAQs|Manuals" }
   * 
   *
   * @param collection
   *         The string representation of a collection query.
   *
   * @return
   */
  public Query setCollection(String collection) {
    this.collection = collection;
    return this;
  }

  /**
   * @return The area name
   */
  public String getArea() {
    return area;
  }

  /**
   * 
   * The area you wish to fire against, production, staging, etc...
   * If blank, the default production area will be used.
   *
   * JSON Reference:
   *
   *     { "area": "Development" }
   *
   * 
   *
   * @param area
   *         The area name.
   *
   * @return
   */
  public Query setArea(String area) {
    this.area = area;
    return this;
  }

  /**
   * @return A string representation of all of the currently set refinements
   */
  public String getRefinementString() {
    if (CollectionUtils.isNotEmpty(navigations.values())) {
      StringBuilder result = new StringBuilder();
      for (Navigation n : navigations.values()) {
        for (Refinement r : n.getRefinements()) {
          result.append("~").append(n.getName()).append(r.toTildeString());
        }
      }
      if (result.length() > 0) {
        return result.toString();
      }
    }
    return null;
  }

  /**
   * @return A string representation of all of the currently set custom url
   * parameters
   */
  public String getCustomUrlParamsString() {
    if (CollectionUtils.isEmpty(customUrlParams)) {
      return null;
    }
    StringBuilder result = new StringBuilder();
    for (CustomUrlParam customUrlParam : customUrlParams) {
      result.append("~").append(customUrlParam.getKey()).append("=").append(customUrlParam.getValue());
    }
    return result.toString();
  }

  /**
   * @param clientKey
   *         Your client key
   *
   * @return
   *
   * @internal
   */
  protected String getBridgeJsonRefinementSearch(String clientKey) {
    Request request = new Request();
    request.setIncludedNavigations(includedNavigations);
    request.setExcludedNavigations(excludedNavigations);
    request.setClientKey(clientKey);
    request.setCollection(collection);
    request.setArea(area);
    request.setRefinementQuery(query);
    request.setWildcardSearchEnabled(isWildcardSearchEnabled());
    if (CollectionUtils.isNotEmpty(sort)) {
      for (Sort s : sort) {
        request.setSort(convertSort(s));
      }
    }
    request.setMatchStrategy(convertPartialMatchStrategy(matchStrategy));
    return requestToJson(request);
  }

  /**
   * 
   * A helper method to parse and set refinements.
   * If you pass in refinements of the format
   *
   *     Brand=Bose~price:20..80
   *
   * The query object will correctly parse out the refinements.
   * 
   *
   * @param refinementString
   *         A tilde separated list of refinements
   *
   * @return
   */
  public Query addRefinementsByString(String refinementString) {
    if (refinementString == null) {
      return this;
    }
    String[] filterStrings = splitRefinements(refinementString);
    for (String filterString : filterStrings) {
      if (StringUtils.isBlank(filterString) || "=".equals(filterString)) {
        continue;
      }
      int colon = filterString.indexOf(":");
      int equals = filterString.indexOf("=");
      boolean isRange = colon != -1 && equals == -1;
      String[] nameValue = filterString.split("[:=]", 2);
      Refinement refinement;
      if (isRange) {
        RefinementRange rr = new RefinementRange();
        if (nameValue[1].endsWith("..")) {
          rr.setLow(nameValue[1].split(DOTS)[0]);
          rr.setHigh("");
        } else if (nameValue[1].startsWith("..")) {
          rr.setLow("");
          rr.setHigh(nameValue[1].split(DOTS)[1]);
        } else {
          String[] lowHigh = nameValue[1].split(DOTS);
          rr.setLow(lowHigh[0]);
          rr.setHigh(lowHigh[1]);
        }
        refinement = rr;
      } else {
        refinement = new RefinementValue();
        ((RefinementValue) refinement).setValue(nameValue[1]);
      }
      if (StringUtils.isNotBlank(nameValue[0])) {
        addRefinement(nameValue[0], refinement);
      }
    }
    return this;
  }

  protected String[] splitRefinements(String refinementString) {
    return StringUtils.isBlank(refinementString) ? EMPTY_REFINEMENTS : REFINEMENTS_SPLITTER_PATTERN.tokenizer(refinementString).split();
  }

  /**
   * 
   * Add a refinement.  Please note that refinements are case-sensitive
   *
   * JSON Reference:
   *
   * Value and range refinements are both appended to an array on the refinements field.
   * Note the 'type' field, which marks the refinement as either a value or range refinement.
   *
   *     { "refinements": [ {"type": "Range", "navigationName": "price", "low": "1.0", "high": "2.0"},
   *     {"type": "Value", "navigationName": "brand", "value": "Nike" } ] }
   *
   * Refinements can be negated by setting the exclude property. An excluded refinement will return
   * results that do not match the value or fall into the range specified in the refinement.
   *
   *     { "refinements": [ {"type": "Range", "navigationName": "price", "low": "1.0", "high": "2.0", "exclude": true},
   *     {"type": "Value", "navigationName": "brand", "value": "Nike", "exclude": true } ] }
   *
   * 
   *
   * @param navigationName
   *         The name of the refinement
   * @param refinement
   *         The refinement to add
   *
   * @return
   */
  private Query addRefinement(String navigationName, Refinement refinement) {
    Navigation navigation = navigations.get(navigationName);
    if (navigation == null) {
      navigation = new Navigation().setName(navigationName);
      navigation.setRange(refinement instanceof RefinementRange);
      navigations.put(navigationName, navigation);
    }
    navigation.getRefinements().add(refinement);
    return this;
  }

  /**
   * 
   * Sets any additional parameters that can be used to trigger rules.
   * Takes a CustomUrlParam object.
   * 
   *
   * @param customUrlParam
   *         The parameter to add
   *
   * @return
   */
  public Query addCustomUrlParam(CustomUrlParam customUrlParam) {
    customUrlParams.add(customUrlParam);
    return this;
  }

  /**
   * 
   * Sets any additional parameters that can be used to trigger rules.
   * Takes a name and a value.
   *
   * JSON Reference:
   *
   * Custom URL parameters separated by ~ in the form:
   *
   *     { "customUrlParams": [ { "key": "region", "value": "east" } ] }
   *
   * 
   *
   * @param key
   *         The parameter key
   * @param value
   *         The parameter value
   *
   * @return
   */
  public Query addCustomUrlParam(String key, String value) {
    customUrlParams.add(new CustomUrlParam().setKey(key).setValue(value));
    return this;
  }

  /**
   * 
   * Helper method that takes a ~ separated string of additional parameters that can be
   * used to trigger rules. Takes ~ separated name/value list
   * 
   *
   * @param values
   *         The list of name/values
   *
   * @return
   */
  public Query addCustomUrlParamsByString(String values) {
    if (values == null) {
      return this;
    }
    String[] params = values.split("&");
    for (String value : params) {
      if (StringUtils.isNotBlank(value)) {
        String[] keyValue = value.split("=");
        if (keyValue.length == 2 && StringUtils.isNotBlank(keyValue[0]) && StringUtils.isNotBlank(keyValue[1])) {
          customUrlParams.add(new CustomUrlParam().setKey(keyValue[0]).setValue(keyValue[1]));
        }
      }
    }
    return this;
  }

  /**
   * @return A list of fields that will be returned by the engine.
   */
  public List getFields() {
    return fields;
  }

  /**
   * 
   * Specify which fields should be returned on each record that comes back from the engine. You may specify more
   * than one field, if you specify \\* all fields will be returned.
   * If this parameter is blank, the search service will return an error.
   * If this parameter is omitted, the search service will return only the `title` field. 
   * The `title` field is always returned.
   * You can exclude fields from being returned using `-`. Exclusion will take precedence over inclusion.
   *
   *
   *
   * JSON Reference:
   *
   *     { "fields": [ "width", "brand", "height" ] }
   *     { "fields" : [ "*", "-height", "-price" ] }
   *
   * 
   *
   * @param name
   *         The case-sensitive name of the attribute to return
   *
   * @return
   */
  public Query addFields(String... name) {
    return addField(fields, name);
  }

  protected Query addField(List fields, String... name) {
    if (name == null) {
      return this;
    }
    Collections.addAll(fields, name);
    return this;
  }

  /**
   * @return A list of the fields that the search service will treat as OR-able.
   */
  public List getOrFields() {
    return orFields;
  }

  /**
   * 
   * Specify which fields should be queried with 'OR' instead of the default 'AND'.
   * This behavior is typically defined in command center on a per navigation basis.  However,
   * you can set which fields should be treated as an OR field at the query level if desired.
   * As with normal refinement selections, once you have refined, the list of refinements for
   * that selected navigation will no longer be returned.
   *
   * JSON Reference:
   *
   *     { "orFields": [ "field1", "field2" ] }
   *
   * 
   *
   * @param name
   *         The field that should be treated as OR by the search service before
   *         being executed.
   *
   * @return
   */
  public Query addOrField(String... name) {
    return addField(orFields, name);
  }

  /**
   * 
   * Add a range refinement.  Takes a refinement name, a lower and upper bounds.
   * 
   *
   * @param navigationName
   *         The name of the refinement
   * @param low
   *         The low value
   * @param high
   *         The high value
   *
   * @return
   */
  public Query addRangeRefinement(String navigationName, String low, String high) {
    return addRangeRefinement(navigationName, low, high, false);
  }

  /**
   * 
   * Add a range refinement.  Takes a refinement name, a lower and upper bounds, and whether or not to exclude
   * this refinement.
   * 
   *
   * @param navigationName
   *         The name of the refinement
   * @param low
   *         The low value
   * @param high
   *         The high value
   * @param exclude
   *         True if the results should exclude this range refinement, false otherwise
   *
   * @return
   */
  public Query addRangeRefinement(String navigationName, String low, String high, boolean exclude) {
    return addRefinement(navigationName, new RefinementRange().setLow(low).setHigh(high).setExclude(exclude));
  }

  /**
   * 
   * Add a value refinement.  Takes a refinement name and a value.
   * 
   *
   * @param navigationName
   *         The name of the navigation
   * @param value
   *         The refinement value
   *
   * @return
   */
  public Query addValueRefinement(String navigationName, String value) {
    return addValueRefinement(navigationName, value, false);
  }

  /**
   * 
   * Add a value refinement.  Takes a refinement name, a value, and whether or not to exclude this refinement.
   * 
   *
   * @param navigationName
   *         The name of the navigation
   * @param value
   *         The refinement value
   * @param exclude
   *         True if the results should exclude this value refinement, false otherwise
   *
   * @return
   */
  public Query addValueRefinement(String navigationName, String value, boolean exclude) {
    return addRefinement(navigationName, new RefinementValue().setValue(value).setExclude(exclude));
  }

  /**
   * @return The number of documents to skip
   */
  public int getSkip() {
    return skip;
  }

  /**
   * 
   * Tell the search service to offset by N records. For example, if N is 10, the records returned will start at 11.
   *
   * JSON Reference:
   *
   *     { "skip": 400 }
   *
   * 
   *
   * @param skip
   *         The number of documents to skip
   *
   * @return
   */
  public Query setSkip(int skip) {
    this.skip = skip;
    return this;
  }

  /**
   * @return The current page size
   */
  public int getPageSize() {
    return pageSize;
  }

  /**
   * 
   * Page size.  Default is 10.
   *
   * JSON Reference:
   *
   *     { "pageSize": 8 }
   *
   * 
   *
   * @param pageSize
   *         The number of records to return with the query.
   *
   * @return
   */
  public Query setPageSize(int pageSize) {
    this.pageSize = pageSize;
    return this;
  }

  /**
   * @return A map of the currently set refinements
   */
  public Map getNavigations() {
    return navigations;
  }

  /**
   * @return Is return JSON set to true.
   */
  public boolean isReturnBinary() {
    return returnBinary;
  }

  /**
   * 
   * Tells the search service to return binary data. This is enabled by default in the APIs for more efficient transport.
   * To disable this in an API, set this to `false`.
   *
   * JSON Reference:
   *
   * If passed true, informs the search service to return binary data rather than JSON.
   *
   *     { "returnBinary": true }
   *
   * 
   *
   * @param returnBinary
   *         Whether to tell the search service to return binary data rather than JSON.
   *
   * @return
   */
  public Query setReturnBinary(boolean returnBinary) {
    this.returnBinary = returnBinary;
    return this;
  }

  /**
   * @return The current biasing profile name.
   */
  public String getBiasingProfile() {
    return biasingProfile;
  }

  /**
   * 
   * Override the biasing profile used for this query - takes precedence over any
   * biasing profile set in the command center.
   *
   * JSON Reference:
   *
   *     { "biasingProfile": "PopularityBias" }
   *
   * 
   *
   * @param biasingProfile The name of the biasing profile
   *
   * @return
   */
  public Query setBiasingProfile(String biasingProfile) {
    this.biasingProfile = biasingProfile;
    return this;
  }

  /**
   * @return The current match strategy name.
   */
  public String getMatchStrategyName() {
    return matchStrategyName;
  }

  /**
   * 
   * Override the match strategy used for this query - takes precedence over any
   * match strategy set in the command center.
   *
   * JSON Reference:
   *
   *     { "matchStrategyName": "RelaxedMatch" }
   *
   * 
   *
   * @param matchStrategyName The name of the match strategy
   *
   * @return
   */
  public Query setMatchStrategyName(String matchStrategyName) {
    this.matchStrategyName = matchStrategyName;
    return this;
  }

  /**
   * @return The current language filter on the query.
   */
  public String getLanguage() {
    return language;
  }

  /**
   * 
   * Sets the language filter on the query and restricts the results to a certain language. If you do not specify a
   * language, english ("lang_en") will be considered the default. An unrecognized language will result in an error.
   *
   * Currently supported languages are:
   *
   *     lang_en
   *
   * JSON Reference:
   *
   *     { "language": "lang_en" }
   *
   * 
   *
   * @param language
   *         The value for language restrict
   *
   * @return
   */
  public Query setLanguage(String language) {
    this.language = language;
    return this;
  }

  /**
   * @return Are refinements with zero counts being removed.
   *
   * @internal
   */
  public boolean isPruneRefinements() {
    return pruneRefinements;
  }

  /**
   * 
   * | Parameter | Default State |
   * |----|----|
   * |`pruneRefinements` |  `true` |
   *
   * By default, the engine will only return refinements that make a difference in the returned results. This is called pruning.
   *
   * For example, let's say you search for "Nike Red Shoes", and 15 results come back. If we have refinements on Brand and Color, and they show:
   *
   * - `brand: Nike (15)`
   * - `color: red (15)`
   *
   * ... the engine will not show those refinements by default, as they make no difference.
   *
   * However, if you set `pruneRefinements` to `false`, the engine will return navigations even if they make no difference in the returned set of results.
   *
   *
   * JSON Reference:
   *
   *     { pruneRefinements: false }
   *
   * 
   *
   * @param pruneRefinements true to prune refinements, false other
   *
   * @return
   */
  public Query setPruneRefinements(boolean pruneRefinements) {
    this.pruneRefinements = pruneRefinements;
    return this;
  }

  /**
   * @return Is the auto-correction behavior disabled
   *
   * @internal
   */
  public boolean isAutocorrectionDisabled() {
    return disableAutocorrection;
  }

  /**
   * 
   * Specifies whether the auto-correction behavior should be disabled. By default, when no results are returned
   * for the given query (and there is a did-you-mean available), the first did-you-mean is automatically queried
   * instead.
   * Defaults to false
   *
   * JSON Reference:
   *
   *     { "disableAutocorrection": false }
   *
   * 
   *
   * @param disableAutocorrection true to disable autocorrection, false otherwise
   *
   * @return
   */
  public Query setDisableAutocorrection(boolean disableAutocorrection) {
    this.disableAutocorrection = disableAutocorrection;
    return this;
  }

  /**
   * 
   * Warning  This will count as two queries against your search index.
   * Typically, this feature is used when you have a large number of navigation items that will overwhelm the end
   * user. It works by using one of the existing navigation items to decide what the query is about and fires a second
   * query to restrict the navigation to the most relevant set of navigation items for this search term.
   * For example, if you pass in a search of `paper` and a restrict navigation of `category:2`
   * The bridge will find the category navigation refinements in the first query and fire a second query for the top 2
   * most populous categories.  Therefore, a search for something generic like "paper" will bring back top category
   * matches like copy paper (1,030), paper pads (567).  The bridge will fire off the second query with the search
   * term, plus an OR refinement with the most likely categories.  The navigation items in the first query are
   * entirely replaced with the navigation items in the second query, except for the navigation that was used for the
   * restriction so that users still have the ability to navigate by all category types.
   *
   * JSON Reference:
   *
   *     { "restrictNavigation": { "name": "category", "count": 2 } }
   *
   * 
   *
   * @param restrictNavigation
   *         Restriction criteria
   *
   * @return this query
   */
  public Query setRestrictNavigation(RestrictNavigation restrictNavigation) {
    this.restrictNavigation = restrictNavigation;
    return this;
  }

  /**
   * 
   * Warning @see Query#setRestrictNavigation(RestrictNavigation). This is a convenience method.
   * 
   *
   * @param name
   *         the name of the field should be used in the navigation restriction in the second query.
   * @param count
   *         the number of fields matches
   *
   * @return this query
   */
  public Query setRestrictNavigation(String name, int count) {
    this.restrictNavigation = new RestrictNavigation().setName(name).setCount(count);
    return this;
  }

  /**
   * @return The current list of sort parameters
   */
  public List getSort() {
    return sort;
  }

  /**
   * 
   * Specifies the sort order applied to the fields in the order specified. If no sort criteria are specified, the
   * default is to sort by relevance. There is a special sort field `_relevance`, which also specifies sorting by
   * relevance. It is possible to specify multiple sort criteria. The criteria order matters, as the records will be
   * sorted by the first criteria and then any matches will be tie-broken using the next criteria. Given an example
   * where the sort is specified as `category` then `_relevance`, results will be sorted first by `category` and
   * relevance will only affect the order between records that have the same category. Records can also be sorted by
   * a specific ID as well when you want to return items in a specific order. If a record ID is included as a sort,
   * but that record not a part of the result set, that item will not be included (unlike push to top). There is a limit
   * of 1000 id's that you can sort by. Any ID's beyond this limit will be ignored. ID sort can also be used with
   * other types of sorts.
   *
   * Please note, sorting is based on the actual value in the record. For example, if sorting on `price`, and
   * `price` is a `Range` navigation, the records will be sorted according to the actual price value in the record
   * and not the bucket value.
   *
   * The order field can be set to either `Ascending` or `Descending`. When sorting by relevance, the order is always
   * `Descending`. For any other field, the default order is `Ascending`.
   *
   * JSON Reference:
   *
   *     { "sort": { "field": "price", "order": "Descending" } }
   *     { "sort": [{ "field": "_relevance" }, { "field": "price", "order": "Descending" }] }
   *     { "sort": [{ "field": "brand", "order":"Ascending" }, { "field": "_relevance" }, { "field": "price" }] }
   *     { "sort": [{ "type": "ByIds", "ids": ["1234"," 5678"]}] }
   *     { "sort": [{ "type": "ByIds", "ids": ["1234"," 5678"]}, { "field": "price", "order": "Descending" }] }
   *
   * 
   *
   * @param sort Any number of sort criteria.
   * @return
   */
  public Query setSort(Sort... sort) {
    CollectionUtils.addAll(this.sort, sort);
    return this;
  }

  /**
   * @return The current match strategy.
   */
  public MatchStrategy getMatchStrategy() {
    return this.matchStrategy;
  }

  /**
   * 
   * A match strategy allows you to explicitly manage recall on a per query basis. There must always be one term
   * matching in a query, thus `termsGreaterThan` can only be defined from 1 upwards and `terms` can only be defined
   * from 2 upwards. It is not possible to match more terms than passed into the query. Relative `mustMatch` values
   * can be used in conjunction with `termsGreaterThan`. A `"percentage": true` flag denotes a relative `mustMatch`
   * to the portion of the terms and will always round down (i.e. 50% must match of 3 terms, means that 1 term must
   * match).
   *
   * The following is the default match strategy:
   *
   * ```
   * { "matchStrategy": { "rules":[{ "terms": 2, "mustMatch": 2 },
   *                               { "terms": 3, "mustMatch": 2 },
   *                               { "terms": 4, "mustMatch": 3 },
   *                               { "terms": 5, "mustMatch": 3 },
   *                               { "terms": 6, "mustMatch": 4 },
   *                               { "terms": 7, "mustMatch": 4 },
   *                               { "terms": 8, "mustMatch": 5 },
   *                               { "termsGreaterThan": 8, "mustMatch": 60, "percentage": true }] } }
   * ```
   *
   * An exact matching strategy would be:
   *
   * ```
   * { "matchStrategy": { "rules": { "termsGreaterThan": 1, "mustMatch": 100, "percentage": true } } }
   * ```
   *
   * Please note, it is highly recommended that the highest rule is defined with `termsGreaterThan`
   * and a relative `mustMatch` as that guarantees that the number of matches required grows with the number of terms
   * passed into the query.
   *
   * JSON Reference:
   *
   *     { "matchStrategy": { "rules":[{ "terms": 2, "mustMatch": 2 },
   *                                   { "terms": 3, "mustMatch": 2 },
   *                                   { "terms": 4, "mustMatch": 3 },
   *                                   { "terms": 5, "mustMatch": 3 },
   *                                   { "terms": 6, "mustMatch": 4 },
   *                                   { "terms": 7, "mustMatch": 4 },
   *                                   { "terms": 8, "mustMatch": 5 },
   *                                   { "termsGreaterThan": 8, "mustMatch": 60, "percentage": true }] } }
   *     { "matchStrategy": { "rules": { "termsGreaterThan": 1, "mustMatch": 100, "percentage": true } } }
   *     { "matchStrategy": { "rules":[{ "terms": 2, "mustMatch": 1 },
   *                                   { "termsGreaterThan": 2, "mustMatch": 75, "percentage": true }] } }
   *
   * 
   *
   * @param matchStrategy A match strategy composed of partial matching rules.
   * @return
   */
  public Query setMatchStrategy(MatchStrategy matchStrategy) {
    this.matchStrategy = matchStrategy;
    return this;
  }

  /**
   * @return A list of navigations that will be included with the response.
   */
  public List getIncludeNavigations() {
    return includedNavigations;
  }

  /**
   * 
   * An array that specifies which navigations should be returned. 
   *
   * If set, this overrides the navigations defined in Command Center and only returns the navigations specified.
   * If this parameter is blank the Dynamic Navigations from Command Center are returned.
   *
   * The values here must be defined via Command Center or Bulk Upload. If a navigation is specified that has not been defined,
   * it will be ignored. 
   *
   * This means, if this parameter uses a `dummy` navigation that is not real, this will both override any Command Center definitions, and will return nothing, as the navigation does not exist.
   *
   *
   * The field name supports two types of wildcard characters: '?' and '\*'.
   * The '?' wildcard will match one character. For example "????_price" will match "sale_price",
   * but not "sales_price". The '\*' wildcard will match any number of characters. For example, a
   * name of "\*_price" will match both "sale_price and "sales_price", but not "sale_prices".
   *
   *
   *
   * JSON Reference:
   *
   *     { "includedNavigations": [ "width", "brand", "categories.categories.value" ] }
   * 
   *
   * @param navigationName
   *         The case-sensitive name of the navigation to return
   *
   * @return
   */
  public Query addIncludedNavigations(String... navigationName) {
    return addField(includedNavigations, navigationName);
  }

  /**
   * @return A list of navigations that will be excluded from the response.
   */
  public List getExcludeNavigations() {
    return excludedNavigations;
  }

  /**
   * 
   * Specify which navigations should not be returned. If set, this forces the response to
   * exclude certain navigations defined in Command Center. If this parameter is blank all
   * navigations in Command Center are returned. If a navigation name is specified that does
   * not exist, it will be ignored. If "includedNavigations" are specified, then all
   * "excludedNavigations" are ignored. Please see the documentation on "includedNavigations"
   * for details on wildcard characters in the field name.
   *
   * JSON Reference:
   *
   *     { "excludedNavigations": [ "width", "brand", "categories.categories.value" ] }
   *
   * 
   *
   * @param navigationName
   *         The case-sensitive name of the navigation to exclude
   *
   * @return
   */
  public Query addExcludedNavigations(String... navigationName) {
    return addField(excludedNavigations, navigationName);
  }

  /**
   * @return The query level url-parameters.
   */
  public Map getQueryUrlParams() {
    return queryUrlParams;
  }

  /**
   * 
   *
   * Sets the query level url parameters. These will be used in the future to enable and disable
   * features, such as disabling Navigations in the response.
   *
   * 
   *
   * @param queryUrlParams
   *         The map of query level url parameters
   *
   * @return
   */
  public Query setQueryUrlParams(Map queryUrlParams) {
    this.queryUrlParams = queryUrlParams;
    return this;
  }

  /**
   * 
   *
   * @see Query#setQueryUrlParams(Map). This is a convenience method for when you have no
   * value for the url parameter.
   *
   * 
   *
   * @param key
   *         The key of the url parameter
   *
   * @return
   */
  public Query addQueryUrlParams(String key) {
    return addQueryUrlParams(key, null);
  }

  /**
   * 
   *
   * @see Query#setQueryUrlParams(Map).
   *
   * 
   *
   * @param key
   *         The key of the url parameter
   * @param value
   *         The value of the url parameter
   *
   * @return
   */
  public Query addQueryUrlParams(String key, String value) {
    this.queryUrlParams.put(key, value);
    return this;
  }

  /**
   * 
   *
   * @see Query#setBiasing(Biasing). This is a convenience method to set which products should be
   * brought to the top of the result set.
   *
   * 
   *
   * @param bringToTop
   *         Any number of product IDs to bring to the top of the result set.
   *
   * @return
   */
  public Query setBringToTop(String... bringToTop) {
    CollectionUtils.addAll(this.biasing.getBringToTop(), bringToTop);
    return this;
  }

  /**
   * 
   *
   * @see Query#setBiasing(Biasing). This is a convenience method to set the list of products IDs that will be
   * used for `restrictToIds`.
   *
   * 
   *
   * @param restrictToIds
   *         Any number of product IDs to bring to the top of the result set.
   *
   * @return
   */
  public Query setRestrictToIds(String... restrictToIds) {
    CollectionUtils.addAll(this.biasing.getRestrictToIds(), restrictToIds);
    return this;
  }

  /**
   * 
   *
   * @see Query#setBiasing(Biasing). This is a convenience method to set the biasing augment status.
   *
   * 
   *
   * @param augment
   *         True to replace the biases defined in Command Center, false to augment.
   *
   * @return
   */
  public Query setBiasingAugment(boolean augment) {
    biasing.setAugmentBiases(augment);
    return this;
  }

  /**
   * 
   *
   * @see Query#setBiasing(Biasing). This is a convenience method to set the biasing influence.
   *
   * 
   *
   * @param influence
   *         The influence
   * @return
   */
  public Query setInfluence(Float influence) {
    biasing.setInfluence(influence);
    return this;
  }

  /**
   * 
   *
   * @see Query#setBiasing(Biasing). This is a convenience method to add an individual bias.
   *
   * 
   *
   * @param name
   *         The name of the field to bias on
   * @param content
   *         The value to bias
   * @param strength
   *         The strength of the bias. Legal values are: "Absolute_Increase", "Strong_Increase", "Medium_Increase",
   *         "Weak_Increase", "Leave_Unchanged", "Weak_Decrease", "Medium_Decrease", "Strong_Decrease", "Absolute_Decrease".
   *
   * @return
   */
  public Query addBias(String name, String content, Bias.Strength strength) {
    biasing.getBiases().add(new Bias().setName(name).setContent(content).setStrength(strength));
    return this;
  }

  /**
   * @return The session ID
   */
  public String getSessionId() {
    return sessionId;
  }

  /**
   * 
   *  A unique string identifier of the session that your customer is currently in. The sessionID should be a unique value for a
   *  given user that persists for them as long as that user is active on the site for that session. We define a session as the
   *  time that you would consider a duration of an A/B test. In future, A/B testing tools within our solution will leverage
   *  the session ID to group customers into different experiences. Ensuring that session ID is persistent throughout a measure
   *  of time will help ensure that the customer experience is consistent as they shop and browse your site. Therefore, the
   *  sessionID should update only if the user is inactive for some period - we recommend keeping this in alignment for what
   *  you consider a shopping session for your customers. For example, you can align this to the timeout of items stored in the
   *  shopping cart. Session ID should not change when the user logs in and can be used to track a user changing from anonymous
   *  to logged in. Session ID must also be consistent between the Search and Recommendations APIs to ensure correct monitoring of
   *  conversion metrics.
   *
   * |@warn
   * | Sending raw session IDs is a security risk. Encrypt or hash session IDs prior to transmission.
   *
   * 
   * @param sessionId
   *         The session ID
   * @return
   */
  public Query setSessionId(String sessionId) {
    this.sessionId = sessionId;
    return this;
  }

  /**
   * @return The user ID
   */
  public String getVisitorId() {
    return visitorId;
  }

  /**
   * 
   *
   *  A unique string identifier of an end customer. Anonymous users (not logged in) should have a visitorID that is
   *  a randomly generated v4 UUID. This visitorID should stay with the anonymous user for as long as possible or
   *  until they log in. When a user logs in, their visitorID change to a known globally unique identifier for that customer.
   *  Visitor ID should remain the same for a particular customer over different sessions.  Also, it must be consistent
   *  between the Search and Recommendations APIs to ensure correct monitoring of conversion metrics.
   *
   * |@warn
   * | Sending raw session IDs is a security risk. Encrypt or hash session IDs prior to transmission.
   *
   * 
   * @param visitorId
   *         The visitor ID
   * @return
   */
  public Query setVisitorId(String visitorId) {
    this.visitorId = visitorId;
    return this;
  }

  /**
   * @return The biasing
   */
  public Biasing getBiasing() {
    return biasing;
  }

  /**
   * 
   * Add a biasing profile, which is defined at query time. Possible settings
   * include:
   *
   *  - `bringToTop`: A list of product IDs to bring to the top of the result set. This list
   *  will ensure that the products are included in the result set and appear in the order
   *  defined.
   *  - `restrictToIds`: A list of product IDs that will be used as the subset on which any subsequent refinements, search, sort,
   *  and/or bias will be conducted on. For example, you can pass in a list of IDs within which to search. The list is not order sensitive.
   *  Records will only return if their ID is defined in this list *and* exist in the queried result set. This operation has a 1000 ID limit.
   *  - `influence`: The influence to apply to query-time biases and biases set in Command Center.
   *  If this field is not defined, then the influence of the biasing profile defined in Command Center will take effect.
   *  If an influence is not defined in Command Center, then the influence will default to 5.
   *  - `augmentBiases`: If true, the biases defined here will augment biasing profiles defined in Command Center.
   *  Otherwise, the biases will override the ones defined in command Center. By default, this is set to false.
   *  - `biases`: A list of biases, which either override or augment biasing profiles defined
   *  in Command Center. See the documentation for `addBias` for more information.
   * - `numericBoosts`: A list of numeric boosts, which will override or augment biasing profiles defined in Command Center. 
   *
   * |@tip
   * | #### When to use it
   * |
   * | `restrictToIds` is especially useful when you need to request a subset of your records due to information from a 3rd party source.
   * | All the typical search operations: search, navigation, biasing, sort, and so on - can be conducted on this set.
   *
   * |@warn
   * | When both `bringToTop` and `restrctToIds` are used, the ids specified in `bringToTop` will be first,
   * | followed by the ids in the second set, with any subsequent biasing, sort, refinements, and query operations applied to them.
   *
   * JSON Reference:
   *
   *     { "biasing": {
   *          "bringToTop": ["productId1","productId3","productId2"]
   *          "restrictToIds": ["productId1","productId3","productId2"]
   *          "influence": 5.0,
   *          "augmentBiases": false,
   *          "biases": [
   *               {"name":"brand", "content":"Brand A", "strength":"Medium_Increase"},
   *               {"name":"brand", "content":"Brand B", "strength":"Strong_Increase"},
   *               {"name":"material", "content":"Material A", "strength":"Strong_Decrease"}
   *          ],
   *         "numericBoosts": [
   *          {"name":"size", "strength":10, "inverted":false},
   *          {"name":"price", "strength":0.001, "inverted":true}
   *     ]
   *     }}
   *
   *
   * 
   *
   * @param biasing
   *         The biasing parameters
   *
   * @return
   * @internal
   */
  public Query setBiasing(Biasing biasing) {
    this.biasing = biasing;
    return this;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy