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

org.powerscala.search.SearchQueryBuilder.scala Maven / Gradle / Ivy

package org.powerscala.search

import com.spatial4j.core.distance.DistanceUtils
import org.apache.lucene.facet.LabelAndValue
import org.apache.lucene.index.Term
import org.apache.lucene.queryparser.classic.QueryParser
import org.apache.lucene.search._
import org.apache.lucene.spatial.query.{SpatialOperation, SpatialArgs}
import org.apache.lucene.util.NumericUtils

/**
 * @author Matt Hicks 
 */
case class SearchQueryBuilder(instance: Search,
                              defaultField: String,
                              queryString: Option[String] = None,
                              queries: List[(Query, BooleanClause.Occur)] = Nil,
                              offset: Int = 0,
                              limit: Int = 100,
                              allowLeadingWildcard: Boolean = true,
                              facetRequests: List[FacetRequest] = Nil,
                              drillDown: List[DrillDown] = Nil,
                              sort: Sort = Sort.RELEVANCE,
                              filter: Filter = null) {
  def queryString(query: String): SearchQueryBuilder = copy(queryString = Some(query))
  def add(query: Query, clause: BooleanClause.Occur = BooleanClause.Occur.MUST) = copy(queries = query -> clause :: queries)
  def term(text: String, field: String = defaultField, clause: BooleanClause.Occur = BooleanClause.Occur.MUST) = {
    add(new TermQuery(new Term(field, text)), clause)
  }
  def prefix(text: String, field: String = defaultField, clause: BooleanClause.Occur = BooleanClause.Occur.MUST) = {
    add(new PrefixQuery(new Term(field, text)), clause)
  }
  def phrase(text: String, field: String = defaultField, clause: BooleanClause.Occur = BooleanClause.Occur.MUST) = {
    val query = new PhraseQuery
    text.split(" ").foreach(word => query.add(new Term(field, word)))
    add(query, clause)
  }
  def regexp(regex: String, field: String = defaultField, clause: BooleanClause.Occur = BooleanClause.Occur.MUST) = {
    add(new RegexpQuery(new Term(field, regex)), clause)
  }
  def wildcard(text: String, field: String = defaultField, clause: BooleanClause.Occur = BooleanClause.Occur.MUST) = {
    add(new WildcardQuery(new Term(field, text)), clause)
  }
  def fuzzy(text: String,
            field: String = defaultField,
            maxEdits: Int = FuzzyQuery.defaultMaxEdits,
            prefixLength: Int = FuzzyQuery.defaultPrefixLength,
            maxExpansions: Int = FuzzyQuery.defaultMaxExpansions,
            transpositions: Boolean = FuzzyQuery.defaultTranspositions,
            clause: BooleanClause.Occur = BooleanClause.Occur.MUST) = {
    add(new FuzzyQuery(new Term(field, text), maxEdits, prefixLength, maxExpansions, transpositions), clause)
  }
  def doubleRange(min: Double = Double.MinValue,
                  max: Double = Double.MaxValue,
                  field: String = defaultField,
                  minInclusive: Boolean = true,
                  maxInclusive: Boolean = true,
                  precisionStep: Int = NumericUtils.PRECISION_STEP_DEFAULT,
                  clause: BooleanClause.Occur = BooleanClause.Occur.MUST) = {
    add(NumericRangeQuery.newDoubleRange(field, precisionStep, min, max, minInclusive, maxInclusive), clause)
  }
  def floatRange(min: Float = Float.MinValue,
                 max: Float = Float.MaxValue,
                 field: String = defaultField,
                 minInclusive: Boolean = true,
                 maxInclusive: Boolean = true,
                 precisionStep: Int = NumericUtils.PRECISION_STEP_DEFAULT_32,
                 clause: BooleanClause.Occur = BooleanClause.Occur.MUST) = {
    add(NumericRangeQuery.newFloatRange(field, precisionStep, min, max, minInclusive, maxInclusive), clause)
  }
  def longRange(min: Long = Long.MinValue,
                max: Long = Long.MaxValue,
                field: String = defaultField,
                minInclusive: Boolean = true,
                maxInclusive: Boolean = true,
                precisionStep: Int = NumericUtils.PRECISION_STEP_DEFAULT,
                clause: BooleanClause.Occur = BooleanClause.Occur.MUST) = {
    add(NumericRangeQuery.newLongRange(field, precisionStep, min, max, minInclusive, maxInclusive), clause)
  }
  def intRange(min: Int = Int.MinValue,
               max: Int = Int.MaxValue,
               field: String = defaultField,
               minInclusive: Boolean = true,
               maxInclusive: Boolean = true,
               precisionStep: Int = NumericUtils.PRECISION_STEP_DEFAULT_32,
               clause: BooleanClause.Occur = BooleanClause.Occur.MUST) = {
    add(NumericRangeQuery.newIntRange(field, precisionStep, min, max, minInclusive, maxInclusive), clause)
  }
  def facets(facetRequests: FacetRequest*) = copy(facetRequests = facetRequests.toList)
  def facet(name: String, max: Int = 10, filter: Option[LabelAndValue => Boolean] = None) = {
    copy(facetRequests = FacetRequest(DrillDown(name), max, filter) :: facetRequests)
  }
  def drillDown(facet: String, values: String*): SearchQueryBuilder = copy(drillDown = DrillDown(facet, values: _*) :: drillDown)
  def hasDrillDown(facet: String, values: Seq[String]) = drillDown.find(d => d.dim == facet && d.path == values).nonEmpty
  def sort(s: Sort): SearchQueryBuilder = copy(sort = s)
  def sortFromPoint(latitude: Double, longitude: Double, reverse: Boolean = false) = {
    val point = instance.point(latitude, longitude)
    val valueSource = instance.spatialStrategy.makeDistanceValueSource(point, DistanceUtils.DEG_TO_KM)
    val distanceSortField = valueSource.getSortField(reverse)
    addSort(distanceSortField)
  }
  def addSort(field: SortField) = {
    val sort = if (this.sort == null) {
      new Sort()
    } else {
      this.sort
    }
    val fields = sort.getSort :+ field
    copy(sort = new Sort(fields: _*).rewrite(instance.searcher))
  }
  def filter(f: Filter): SearchQueryBuilder = copy(filter = f)
  def filterByCircle(latitude: Double, longitude: Double, distanceInDegrees: Double) = {
    val args = new SpatialArgs(SpatialOperation.Intersects, instance.spatialContext.makeCircle(longitude, latitude, distanceInDegrees))
    val filter = instance.spatialStrategy.makeFilter(args)
    copy(filter = filter)
  }
  def run() = instance.search(this)

  def offset(o: Int): SearchQueryBuilder = copy(offset = o)
  def limit(l: Int): SearchQueryBuilder = copy(limit = l)

  private[search] def createQuery() = if (queries.nonEmpty || queryString.nonEmpty) {
    val query = new BooleanQuery
    queries.foreach {
      case (q, clause) => query.add(q, clause)
    }
    queryString match {
      case Some(qs) => {
        val parser = new QueryParser(defaultField, instance.analyzer)
        parser.setAllowLeadingWildcard(allowLeadingWildcard)
        query.add(parser.parse(qs), BooleanClause.Occur.MUST)
      }
      case None => // Ignore
    }
    query
  } else {
    new MatchAllDocsQuery
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy