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

com.elemica.cloudsearch.QueryBuilder.scala Maven / Gradle / Ivy

package com.elemica.cloudsearch

import scala.util.parsing.combinator._
import scala.util.parsing.input.CharSequenceReader

object CloudSearchQueryTerm {
  val numericFields = "objecttimestamp" :: Nil
}
sealed trait CloudSearchQueryTerm {
  def serializedTerm: String
}
case class AnyFieldContentQuery(query: String) extends CloudSearchQueryTerm {
  val serializedTerm = "'" + query + "'"
}
case class SingleFieldContentQuery(fieldName: String, query: String) extends CloudSearchQueryTerm {
  val serializedTerm = {
    if (CloudSearchQueryTerm.numericFields.contains(fieldName))
      fieldName + ":" + query
    else
      fieldName + ":'" + query + "'"
  }
}

sealed trait CloudSearchQueryClause extends CloudSearchQueryTerm {
  lazy val serializedTerm = serializedClause
  def serializedClause: String
}
case class PlainQueryClause(term: CloudSearchQueryTerm) extends CloudSearchQueryClause {
  val serializedClause = term.serializedTerm
}

sealed trait CondensingQueryClause extends CloudSearchQueryClause {
  def clauseOperator: String
  def clauses: List[CloudSearchQueryClause]

  lazy val serializedClause = clauses match {
    case Nil =>
      ""

    case singleTerm :: Nil =>
      singleTerm.serializedClause

    case _ =>
      "(" + clauseOperator + " " + clauses.map(_.serializedClause).mkString(" ") + ")"
  }
}
case class AndQueryClause(clauses: List[CloudSearchQueryClause]) extends CondensingQueryClause {
  val clauseOperator = "and"
}
case class OrQueryClause(clauses: List[CloudSearchQueryClause]) extends CondensingQueryClause {
  val clauseOperator = "or"
}

object QueryBuilder extends RegexParsers with ImplicitConversions {

  // all non-whitespace characters except '|'
  private lazy val nakedTerm: Parser[String] = "[\\S&&[^\\|]]+".r

  // 'in quotes' or "in quotes"
  private lazy val quoteTerm: Parser[String] = {
    "('|\")[^']*('|\")".r ^^ {
      case token => token.drop(1).dropRight(1)
    }
  }

  private lazy val fieldSpecifierTerm: Parser[String] = "\\w+".r <~ ":(?=\\S)".r

  private lazy val fieldValuesTerm: Parser[List[String]] = rep1sep(quoteTerm | nakedTerm, "|")

  lazy val fieldSpecifierQueryTerm: Parser[CloudSearchQueryTerm] = {
    phrase(rep(opt(fieldSpecifierTerm) ~ fieldValuesTerm)) ^^ {
      case termSequence =>
        val terms = termSequence.collect({

          case Some(fieldSpecifier) ~ (fieldValue :: Nil) =>
            PlainQueryClause(SingleFieldContentQuery(fieldSpecifier, fieldValue))

          case Some(fieldSpecifier) ~ fieldValues =>
            OrQueryClause(fieldValues.map(value => PlainQueryClause(SingleFieldContentQuery(fieldSpecifier, value))))

          case None ~ (term :: Nil) =>
            PlainQueryClause(AnyFieldContentQuery(term))
        })

        AndQueryClause(terms)
    }
  }

  def apply(input: String): String = {
    val reader: Input = new CharSequenceReader(input, 0)
    fieldSpecifierQueryTerm.apply(reader).map(_.serializedTerm).getOrElse("")
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy