Package org.rssowl.core.internal.persist.search

Source Code of org.rssowl.core.internal.persist.search.ModelSearchQueries

/*   **********************************************************************  **
**   Copyright notice                                                       **
**                                                                          **
**   (c) 2005-2009 RSSOwl Development Team                                  **
**   http://www.rssowl.org/                                                 **
**                                                                          **
**   All rights reserved                                                    **
**                                                                          **
**   This program and the accompanying materials are made available under   **
**   the terms of the Eclipse Public License v1.0 which accompanies this    **
**   distribution, and is available at:                                     **
**   http://www.rssowl.org/legal/epl-v10.html                               **
**                                                                          **
**   A copy is found in the file epl-v10.html and important notices to the  **
**   license from the team is found in the textfile LICENSE.txt distributed **
**   in this package.                                                       **
**                                                                          **
**   This copyright notice MUST APPEAR in all copies of the file!           **
**                                                                          **
**   Contributors:                                                          **
**     RSSOwl Development Team - initial API and implementation             **
**                                                                          **
**  **********************************************************************  */

package org.rssowl.core.internal.persist.search;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Token;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.document.DateTools;
import org.apache.lucene.document.NumberTools;
import org.apache.lucene.document.DateTools.Resolution;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.queryParser.QueryParser.Operator;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreRangeQuery;
import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery.TooManyClauses;
import org.rssowl.core.Owl;
import org.rssowl.core.internal.Activator;
import org.rssowl.core.persist.IBookMark;
import org.rssowl.core.persist.IEntity;
import org.rssowl.core.persist.IFolder;
import org.rssowl.core.persist.IMark;
import org.rssowl.core.persist.IModelFactory;
import org.rssowl.core.persist.INews;
import org.rssowl.core.persist.INewsBin;
import org.rssowl.core.persist.ISearch;
import org.rssowl.core.persist.ISearchCondition;
import org.rssowl.core.persist.ISearchField;
import org.rssowl.core.persist.ISearchValueType;
import org.rssowl.core.persist.SearchSpecifier;
import org.rssowl.core.persist.INews.State;
import org.rssowl.core.persist.reference.BookMarkReference;
import org.rssowl.core.persist.reference.FolderReference;
import org.rssowl.core.persist.reference.NewsBinReference;
import org.rssowl.core.util.StringUtils;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.EnumSet;
import java.util.List;

/**
* Helper to build the queries to search for {@link INews}.
*
* @author bpasero
*/
public class ModelSearchQueries {

  /* Maximum Date as String */
  private static final String MAX_DATE = DateTools.dateToString(new Date(Long.MAX_VALUE), Resolution.DAY);

  /* Minimum Date as String */
  private static final String MIN_DATE = DateTools.dateToString(new Date(0), Resolution.DAY);

  /* Maximum Number as String */
  private static final String MAX_NUMBER = NumberTools.longToString(Long.MAX_VALUE);

  /* Minimum Number as String */
  private static final String MIN_NUMBER = NumberTools.longToString(Long.MIN_VALUE);

  /* One Day in Millis */
  private static final Long DAY = 1000 * 3600 * 24L;

  /* One Minute in Millis */
  private static final Long MINUTE = 1000 * 60L;

  /* Wildcard matching any String */
  private static final char STRING_WILDCARD = '*';

  /* Wildcard matching any Char */
  private static final char CHAR_WILDCARD = '?';

  /**
   * Creates a Lucene {@link Query} from the given parameters.
   *
   * @param search the search for the query.
   * @return a {@link Query} from the given parameters.
   * @throws IOException in case of an error.
   */
  public static Query createQuery(ISearch search) throws IOException {
    return createQuery(search.getSearchConditions(), search.matchAllConditions());
  }

  /**
   * Creates a Lucene {@link Query} from the given parameters.
   *
   * @param conditions the search conditions for the query.
   * @param matchAllConditions <code>true</code> to match all conditions and
   * <code>false</code> otherwise.
   * @return a {@link Query} from the given parameters.
   * @throws IOException in case of an error.
   */
  public static Query createQuery(Collection<ISearchCondition> conditions, boolean matchAllConditions) throws IOException {
    try {
      return internalCreateQuery(conditions, matchAllConditions);
    }

    /* Too Many Clauses - Increase Clauses Limit */
    catch (TooManyClauses e) {

      /* Disable Clauses Limit */
      if (BooleanQuery.getMaxClauseCount() != ModelSearchImpl.MAX_CLAUSE_COUNT) {
        BooleanQuery.setMaxClauseCount(ModelSearchImpl.MAX_CLAUSE_COUNT);
        return internalCreateQuery(conditions, matchAllConditions);
      }

      /* Maximum reached */
      throw e;
    }
  }

  private static Query internalCreateQuery(Collection<ISearchCondition> conditions, boolean matchAllConditions) throws IOException {
    BooleanQuery bQuery = new BooleanQuery();

    /* Handle Scope Query separately */
    boolean isScoped = false;
    for (ISearchCondition condition : conditions) {
      if (isScopeCondition(condition)) {
        BooleanQuery scopeClause = createLocationClause(condition);
        if (!scopeClause.clauses().isEmpty()) {
          bQuery.add(scopeClause, Occur.MUST);
          isScoped = true;
        }
      }
    }

    /* If scoped, the fieldQuery is a MUST-Clause to the outer Query */
    BooleanQuery fieldQuery = bQuery;
    if (isScoped)
      fieldQuery = new BooleanQuery();

    /* Add Conditions into the Boolean Query */
    addFieldClauses(conditions, matchAllConditions, fieldQuery);

    /* Only add if not empty if scoped */
    if (isScoped && !fieldQuery.clauses().isEmpty())
      bQuery.add(fieldQuery, Occur.MUST);

    return bQuery;
  }

  private static void addFieldClauses(Collection<ISearchCondition> conditions, boolean matchAllConditions, BooleanQuery bQuery) throws IOException {

    /* Handle State-Field separately (group) */
    BooleanQuery statesQuery = null;
    for (ISearchCondition condition : conditions) {
      if (requiresStateGrouping(condition)) {

        /* Create and add new BooleanQuery for State */
        if (statesQuery == null) {
          statesQuery = new BooleanQuery();
          bQuery.add(statesQuery, matchAllConditions ? Occur.MUST : Occur.SHOULD);
        }

        /* Add Boolean Clause per State */
        addStateClause(statesQuery, condition);
      }
    }

    /* Create a Query for each condition */
    BooleanQuery fieldQuery = null;
    Analyzer analyzer = Indexer.createAnalyzer();
    for (ISearchCondition condition : conditions) {

      /* State and Scope Queries already handled */
      if (requiresStateGrouping(condition) || isScopeCondition(condition))
        continue;

      /* Create and add new BooleanQuery for other Fields */
      if (fieldQuery == null) {
        fieldQuery = new BooleanQuery();
        bQuery.add(fieldQuery, matchAllConditions ? Occur.MUST : Occur.SHOULD);
      }

      /* Create the Clause */
      BooleanClause clause = null;
      if (condition.getField().getId() == IEntity.ALL_FIELDS)
        clause = createAllNewsFieldsClause(analyzer, condition, matchAllConditions);
      else
        clause = createBooleanClause(analyzer, condition, matchAllConditions);

      /* Check if the Clause has any valid Query */
      Query query = clause.getQuery();
      if (query instanceof BooleanQuery && ((BooleanQuery) query).clauses().isEmpty())
        continue;

      /*
       * Specially treat this case where the specifier is a negation but any of
       * the supplied conditions should match in the result set.
       */
      if (condition.getSpecifier().isNegation() && !matchAllConditions) {
        BooleanQuery nestedquery = new BooleanQuery();
        nestedquery.add(clause);
        nestedquery.add(new BooleanClause(new MatchAllDocsQuery(), Occur.MUST));
        fieldQuery.add(new BooleanClause(nestedquery, Occur.SHOULD));
      }

      /* Normal Case */
      else {
        fieldQuery.add(clause);
      }
    }

    /* Add the MatchAllDocsQuery (MUST_NOT is used, All Conditions match) */
    if (fieldQuery != null && matchAllConditions) {
      boolean requireAllDocsQuery = true;
      BooleanClause[] clauses = fieldQuery.getClauses();
      for (BooleanClause clause : clauses) {
        if (clause.getOccur() != Occur.MUST_NOT) {
          requireAllDocsQuery = false;
          break;
        }
      }

      /* Add if required */
      if (requireAllDocsQuery)
        fieldQuery.add(new BooleanClause(new MatchAllDocsQuery(), Occur.MUST));
    }
  }

  @SuppressWarnings("unchecked")
  private static void addStateClause(BooleanQuery statesQuery, ISearchCondition condition) {
    String fieldName = String.valueOf(INews.STATE);
    Occur occur = condition.getSpecifier().isNegation() ? Occur.MUST_NOT : Occur.SHOULD;
    EnumSet<INews.State> newsStates = (EnumSet<State>) condition.getValue();
    for (INews.State state : newsStates) {
      String value = String.valueOf(state.ordinal());
      TermQuery stateQuery = new TermQuery(new Term(fieldName, value));
      statesQuery.add(new BooleanClause(stateQuery, occur));
    }

    /* Check if the match-all-docs query is required */
    if (condition.getSpecifier().isNegation())
      statesQuery.add(new BooleanClause(new MatchAllDocsQuery(), Occur.MUST));
  }

  private static boolean requiresStateGrouping(ISearchCondition condition) {
    return condition.getField().getId() == INews.STATE;
  }

  private static boolean isScopeCondition(ISearchCondition condition) {
    return condition.getSpecifier() == SearchSpecifier.SCOPE;
  }

  private static BooleanClause createAllNewsFieldsClause(Analyzer analyzer, ISearchCondition condition, boolean matchAllConditions) throws IOException {
    IModelFactory factory = Owl.getModelFactory();
    BooleanQuery allFieldsQuery = new BooleanQuery();

    /* Require all words to be contained or not contained */
    if (condition.getSpecifier() == SearchSpecifier.CONTAINS_ALL) {
      List<ISearchCondition> tokenConditions = new ArrayList<ISearchCondition>();

      List<String> tokens = StringUtils.tokenizePhraseAware((String) condition.getValue());
      for (String token : tokens) {
        ISearchCondition tokenCondition = factory.createSearchCondition(condition.getField(), condition.getSpecifier(), token);

        /* Rewrite Specifier */
        if (condition.getSpecifier() == SearchSpecifier.CONTAINS_ALL)
          tokenCondition.setSpecifier(SearchSpecifier.CONTAINS);
        else
          tokenCondition.setSpecifier(SearchSpecifier.CONTAINS_NOT);

        tokenConditions.add(tokenCondition);
      }

      /* Build custom Query out of Conditions */
      for (ISearchCondition tokenCondition : tokenConditions) {
        BooleanClause tokenClause = createAllNewsFieldsClause(analyzer, tokenCondition, matchAllConditions);

        /* Ignore empty clauses (e.g. due to Stop Words) */
        if (tokenClause.getQuery() instanceof BooleanQuery && ((BooleanQuery) tokenClause.getQuery()).getClauses().length == 0)
          continue;

        tokenClause.setOccur(Occur.MUST);
        allFieldsQuery.add(tokenClause);
      }
    }

    /* Require any word to be contained or not contained */
    else {
      List<ISearchCondition> allFieldsConditions = new ArrayList<ISearchCondition>(5);

      /* Title */
      ISearchField field = factory.createSearchField(INews.TITLE, condition.getField().getEntityName());
      allFieldsConditions.add(factory.createSearchCondition(field, condition.getSpecifier(), condition.getValue()));

      /* Description */
      field = factory.createSearchField(INews.DESCRIPTION, condition.getField().getEntityName());
      allFieldsConditions.add(factory.createSearchCondition(field, condition.getSpecifier(), condition.getValue()));

      /* Author */
      field = factory.createSearchField(INews.AUTHOR, condition.getField().getEntityName());
      allFieldsConditions.add(factory.createSearchCondition(field, condition.getSpecifier(), condition.getValue()));

      /* Category */
      field = factory.createSearchField(INews.CATEGORIES, condition.getField().getEntityName());
      List<String> tokens = StringUtils.tokenizePhraseAware(condition.getValue().toString());
      if (!tokens.contains(condition.getValue().toString()))
        tokens.add(condition.getValue().toString());
      for (String token : tokens) {
        allFieldsConditions.add(factory.createSearchCondition(field, condition.getSpecifier().isNegation() ? SearchSpecifier.IS_NOT : SearchSpecifier.IS, token));
      }

      /* Attachment Content */
      field = factory.createSearchField(INews.ATTACHMENTS_CONTENT, condition.getField().getEntityName());
      allFieldsConditions.add(factory.createSearchCondition(field, condition.getSpecifier(), condition.getValue()));

      /* Create Clauses out of Conditions */
      boolean anyClauseIsEmpty = false;
      List<BooleanClause> clauses = new ArrayList<BooleanClause>();
      for (ISearchCondition allFieldCondition : allFieldsConditions) {
        BooleanClause clause = createBooleanClause(analyzer, allFieldCondition, matchAllConditions);
        clause.setOccur(Occur.SHOULD);
        clauses.add(clause);

        /* Ignore empty clauses (e.g. due to Stop Words) */
        if (clause.getQuery() instanceof BooleanQuery && ((BooleanQuery) clause.getQuery()).getClauses().length == 0)
          anyClauseIsEmpty = true;
      }

      /* Only add if none of the clauses is empty */
      if (!anyClauseIsEmpty) {
        for (BooleanClause clause : clauses) {
          allFieldsQuery.add(clause);
        }
      }
    }

    /* Determine Occur (MUST, SHOULD, MUST NOT) */
    Occur occur = getOccur(condition.getSpecifier(), matchAllConditions);
    return new BooleanClause(allFieldsQuery, occur);
  }

  /*
   * Will fallback to a TermQuery if the search-term is not valid for a
   * WildcardQuery
   */
  private static Query createWildcardQuery(String field, String term) {
    if (String.valueOf(INews.LABEL).equals(field) || isValidWildcardTerm(term))
      return new WildcardQuery(new Term(field, term));

    return new TermQuery(new Term(field, term));
  }

  private static boolean isValidWildcardTerm(String term) {
    for (int i = 0; i < term.length(); i++) {
      char charAtIndex = term.charAt(i);
      if (charAtIndex != STRING_WILDCARD && charAtIndex != CHAR_WILDCARD)
        return true;
    }

    return false;
  }

  private static BooleanClause createBooleanClause(Analyzer analyzer, ISearchCondition condition, boolean matchAllConditions) throws IOException {
    Query query = null;

    /* Separately handle this dynamic Query */
    if (condition.getField().getId() == INews.AGE_IN_DAYS || condition.getField().getId() == INews.AGE_IN_MINUTES)
      query = createAgeClause(condition);

    /* Separately handle this dynamic Query */
    else if (condition.getField().getId() == INews.LOCATION)
      query = createLocationClause(condition);

    /* Other Fields */
    else {
      try {
        switch (condition.getField().getSearchValueType().getId()) {

          /* Boolean: Simple Term-Query */
          case ISearchValueType.BOOLEAN:
            query = createTermQuery(condition);
            break;

          /* String / Link / Enum: String Query */
          case ISearchValueType.ENUM:
          case ISearchValueType.STRING:
          case ISearchValueType.LINK:
            query = createStringQuery(analyzer, condition);
            break;

          /* Date / Time / DateTime: Date Query (Ranged) */
          case ISearchValueType.DATE:
          case ISearchValueType.TIME:
          case ISearchValueType.DATETIME:
            query = createDateQuery(condition);
            break;

          /* Number / Integer: Number Query (Ranged) */
          case ISearchValueType.NUMBER:
          case ISearchValueType.INTEGER:
            query = createNumberQuery(condition);
        }
      } catch (ParseException e) {
        Activator.getDefault().getLog().log(Activator.getDefault().createErrorStatus(e.getMessage(), e));
      }
    }

    /* In case of the Query not being created, fallback to Term-Query */
    if (query == null) {
      query = createTermQuery(condition);
    }

    /* Determine Occur (MUST, SHOULD, MUST NOT) */
    Occur occur = getOccur(condition.getSpecifier(), matchAllConditions);
    return new BooleanClause(query, occur);
  }

  /* This Clause needs to be generated dynamically */
  private static Query createAgeClause(ISearchCondition condition) {
    Integer age = (Integer) condition.getValue();
    String value;

    /* Minute Format */
    if (age < 0) {
      Calendar cal = Calendar.getInstance();
      cal.setTimeInMillis(System.currentTimeMillis() + age * MINUTE); //age is negative
      value = DateTools.dateToString(cal.getTime(), Resolution.MINUTE);

      String fieldname = String.valueOf(INews.AGE_IN_MINUTES);
      return createAgeQuery(condition, fieldname, value);
    }

    /* Day Format */
    Calendar cal = Calendar.getInstance();
    cal.setTimeInMillis(System.currentTimeMillis() - age * DAY);
    value = DateTools.dateToString(cal.getTime(), Resolution.DAY);

    String fieldname = String.valueOf(INews.AGE_IN_DAYS);
    return createAgeQuery(condition, fieldname, value);
  }

  private static Query createAgeQuery(ISearchCondition condition, String fieldname, String value) {
    switch (condition.getSpecifier()) {
      case IS: {
        return new TermQuery(new Term(fieldname, value));
      }

      case IS_GREATER_THAN: {
        Term lowerBound = new Term(fieldname, MIN_DATE);
        Term upperBound = new Term(fieldname, value);

        return new ConstantScoreRangeQuery(fieldname, lowerBound.text(), upperBound.text(), false, false);
      }

      case IS_LESS_THAN: {
        Term lowerBound = new Term(fieldname, value);
        Term upperBound = new Term(fieldname, MAX_DATE);

        return new ConstantScoreRangeQuery(fieldname, lowerBound.text(), upperBound.text(), false, false);
      }
    }

    throw new UnsupportedOperationException("Unsupported Specifier for Age Query"); //$NON-NLS-1$
  }

  /* This Clause needs to be generated dynamically */
  private static BooleanQuery createLocationClause(ISearchCondition condition) {
    BooleanQuery bQuery = new BooleanQuery();
    Long[][] value = (Long[][]) condition.getValue();
    if (value != null) {

      /* Receive Folders */
      for (int i = 0; value[0] != null && i < value[0].length; i++) {
        if (value[0][i] != null) {
          IFolder folder = new FolderReference(value[0][i]).resolve();
          if (folder != null)
            addFolderLocationClause(bQuery, folder);
        }
      }

      /* Receive BookMarks */
      for (int i = 0; value[1] != null && i < value[1].length; i++) {
        if (value[1][i] != null) {
          IBookMark bookmark = new BookMarkReference(value[1][i]).resolve();
          if (bookmark != null)
            addBookMarkLocationClause(bQuery, bookmark);
        }
      }

      /* Receive NewsBins */
      if (value.length == 3) {
        for (int i = 0; value[2] != null && i < value[2].length; i++) {
          if (value[2][i] != null) {
            INewsBin newsbin = new NewsBinReference(value[2][i]).resolve();
            if (newsbin != null)
              addNewsBinLocationClause(bQuery, newsbin);
          }
        }
      }
    }

    /* The folder could be empty, make sure to add at least 1 Clause */
    if (bQuery.clauses().isEmpty())
      bQuery.add(new TermQuery(new Term(String.valueOf(INews.FEED), "")), Occur.SHOULD); //$NON-NLS-1$

    return bQuery;
  }

  private static void addFolderLocationClause(BooleanQuery bQuery, IFolder folder) {
    if (folder != null) {
      List<IFolder> folders = folder.getFolders();
      List<IMark> marks = folder.getMarks();

      /* Child Folders */
      for (IFolder childFolder : folders)
        addFolderLocationClause(bQuery, childFolder);

      /* BookMarks and Newsbins */
      for (IMark mark : marks)
        if (mark instanceof IBookMark)
          addBookMarkLocationClause(bQuery, (IBookMark) mark);
        else if (mark instanceof INewsBin)
          addNewsBinLocationClause(bQuery, (INewsBin) mark);
    }
  }

  private static void addBookMarkLocationClause(BooleanQuery bQuery, IBookMark bookmark) {
    if (bookmark != null) {
      BooleanQuery bookMarkLocationQuery = new BooleanQuery();

      /* Match on Feed */
      String feed = bookmark.getFeedLinkReference().getLinkAsText().toLowerCase();
      bookMarkLocationQuery.add(new TermQuery(new Term(String.valueOf(INews.FEED), feed)), Occur.MUST);

      /* But ignore News from Bins */
      bookMarkLocationQuery.add(new TermQuery(new Term(String.valueOf(INews.PARENT_ID), NumberTools.longToString(0))), Occur.MUST);

      bQuery.add(bookMarkLocationQuery, Occur.SHOULD);
    }
  }

  private static void addNewsBinLocationClause(BooleanQuery bQuery, INewsBin newsbin) {
    if (newsbin != null)
      bQuery.add(new TermQuery(new Term(String.valueOf(INews.PARENT_ID), NumberTools.longToString(newsbin.getId()))), Occur.SHOULD);
  }

  @SuppressWarnings("unchecked")
  private static Query createStringQuery(Analyzer analyzer, ISearchCondition condition) throws ParseException, IOException {
    SearchSpecifier specifier = condition.getSpecifier();
    String fieldname = String.valueOf(condition.getField().getId());

    /* Retrieve Value */
    String value;
    if (condition.getValue() instanceof Enum)
      value = String.valueOf(((Enum<?>) condition.getValue()).ordinal());
    else
      value = String.valueOf(condition.getValue());

    switch (specifier) {

      /* Create Wildcard-Query */
      case IS:
      case IS_NOT: {
        return createWildcardQuery(fieldname, value.toLowerCase());
      }

        /* Let Query-Parser handle this */
      case CONTAINS:
      case CONTAINS_ALL:
      case CONTAINS_NOT: {
        QueryParser parser = new QueryParser(fieldname, analyzer);
        Operator operator = (specifier == SearchSpecifier.CONTAINS || specifier == SearchSpecifier.CONTAINS_NOT) ? QueryParser.OR_OPERATOR : QueryParser.AND_OPERATOR;
        parser.setDefaultOperator(operator);
        parser.setAllowLeadingWildcard(true);

        /* Prepare the value for parsing */
        value = prepareForParsing(value);

        /* Parse */
        return parser.parse(value);
      }

        /* Wildcard-Query with trailing '*' */
      case BEGINS_WITH: {
        value = value.toLowerCase() + "*"; //$NON-NLS-1$
        return createWildcardQuery(fieldname, value);
      }

        /* Wildcard-Query with leading '*' */
      case ENDS_WITH: {
        value = "*" + value.toLowerCase(); //$NON-NLS-1$
        return createWildcardQuery(fieldname, value);
      }

        /* Fuzzy Query */
      case SIMILIAR_TO: {
        BooleanQuery similarityQuery = new BooleanQuery();

        LowercaseWhitespaceAnalyzer similarAnalyzer = new LowercaseWhitespaceAnalyzer();
        TokenStream tokenStream = similarAnalyzer.tokenStream(String.valueOf(IEntity.ALL_FIELDS), new StringReader(value));
        Token token = null;
        while ((token = tokenStream.next()) != null) {
          String termText = new String(token.termBuffer(), 0, token.termLength());
          Term term = new Term(fieldname, termText);
          similarityQuery.add(new BooleanClause(new FuzzyQuery(term), Occur.MUST));
        }

        return similarityQuery;
      }
    }

    throw new UnsupportedOperationException("Unsupported Specifier for Parsed Queries"); //$NON-NLS-1$
  }

  @SuppressWarnings("unchecked")
  private static Query createTermQuery(ISearchCondition condition) {
    String value;
    if (condition.getValue() instanceof Enum)
      value = String.valueOf(((Enum<?>) condition.getValue()).ordinal());
    else
      value = String.valueOf(condition.getValue());

    String fieldname = String.valueOf(condition.getField().getId());

    Term term = new Term(fieldname, value);
    return new TermQuery(term);
  }

  private static Query createDateQuery(ISearchCondition condition) {
    SearchSpecifier specifier = condition.getSpecifier();
    String value = DateTools.dateToString((Date) condition.getValue(), Resolution.DAY);
    String fieldname = String.valueOf(condition.getField().getId());

    switch (specifier) {
      case IS:
      case IS_NOT:
        return new TermQuery(new Term(fieldname, value));

      case IS_AFTER: {
        Term lowerBound = new Term(fieldname, value);
        Term upperBound = new Term(fieldname, MAX_DATE);

        return new ConstantScoreRangeQuery(fieldname, lowerBound.text(), upperBound.text(), false, false);
      }

      case IS_BEFORE: {
        Term lowerBound = new Term(fieldname, MIN_DATE);
        Term upperBound = new Term(fieldname, value);

        return new ConstantScoreRangeQuery(fieldname, lowerBound.text(), upperBound.text(), false, false);
      }
    }

    throw new UnsupportedOperationException("Unsupported Specifier for Date/Time Queries"); //$NON-NLS-1$
  }

  private static Query createNumberQuery(ISearchCondition condition) {
    SearchSpecifier specifier = condition.getSpecifier();
    String value = NumberTools.longToString((Integer) condition.getValue());
    String fieldname = String.valueOf(condition.getField().getId());

    switch (specifier) {
      case IS:
      case IS_NOT:
        return new TermQuery(new Term(fieldname, value));

      case IS_GREATER_THAN: {
        Term lowerBound = new Term(fieldname, value);
        Term upperBound = new Term(fieldname, MAX_NUMBER);

        return new ConstantScoreRangeQuery(fieldname, lowerBound.text(), upperBound.text(), false, false);
      }

      case IS_LESS_THAN: {
        Term lowerBound = new Term(fieldname, MIN_NUMBER);
        Term upperBound = new Term(fieldname, value);

        return new ConstantScoreRangeQuery(fieldname, lowerBound.text(), upperBound.text(), false, false);
      }
    }

    throw new UnsupportedOperationException("Unsupported Specifier for Number Queries"); //$NON-NLS-1$
  }

  private static Occur getOccur(SearchSpecifier specifier, boolean matchAllConditions) {
    switch (specifier) {
      case IS_NOT:
      case CONTAINS_NOT:
        return Occur.MUST_NOT;

      default:
        return matchAllConditions ? Occur.MUST : Occur.SHOULD;
    }
  }

  private static String prepareForParsing(String s) {
    int doubleQuoteCount = 0;
    boolean startsWithDoubleQuote = false;
    boolean endsWithDoubleQuote = false;

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);

      /* Escape Special Characters being used in Lucene */
      if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':' || c == '^' || c == '[' || c == ']' || c == '{' || c == '}' || c == '~')
        sb.append('\\');

      /* Keep Track of Doublequotes being used - they need special treatment */
      else if (c == '\"') {
        doubleQuoteCount++;
        if (i == 0)
          startsWithDoubleQuote = true;
        else if (i == s.length() - 1)
          endsWithDoubleQuote = true;
      }

      sb.append(c);
    }

    String escapedString = sb.toString();

    /*
     * This results in a parser exception from QueryParser and thus needs
     * special treatment
     */
    if (doubleQuoteCount % 2 != 0) {
      escapedString = escapedString.replace("\"", "\\\""); //$NON-NLS-1$ //$NON-NLS-2$
      if (startsWithDoubleQuote && endsWithDoubleQuote) //Restore Quotes for Phrase Search if necessary
        escapedString = escapedString.substring(1, escapedString.length() - 2) + "\""; //$NON-NLS-1$
    }

    return escapedString;
  }
}
TOP

Related Classes of org.rssowl.core.internal.persist.search.ModelSearchQueries

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.