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

com.github.takezoe.solr.scala.query.ExpressionParser.scala Maven / Gradle / Ivy

package com.github.takezoe.solr.scala.query

import scala.util.parsing.combinator.RegexParsers

trait ExpressionParser {
  def parse(expression: String): AST
}

/**
 * The default implementation of ExpressionParser which supports the simple expression
 * contains operators such as &, | and !.
 */
class DefaultExpressionParser extends RegexParsers with ExpressionParser {

  def operator: Parser[AST] = chainl1(expression,
      "&" ^^ { op => (left: AST, right: AST) => ASTAnd(left, right)} |
      "|" ^^ { op => (left: AST, right: AST) => ASTOr(left, right)} |
      ""  ^^ { op => (left: AST, right: AST) => ASTAnd(left, right)}
  )

  def expression: Parser[AST] = not | phrase | word | "(" ~> operator <~ ")"

  def not: Parser[AST] = "!" ~> expression ^^ ASTNot.apply

  def word: Parser[AST] = """[^(|!& \t)]+""".r ^^ ASTWord.apply

  def phrase: Parser[AST] = "\"" ~> """[^"]+""".r <~ "\"" ^^ ASTPhrase.apply

  def parse(str:String) = parseAll(expression, str).get

}

/**
 * The optional implementation of ExpressionParser which supports Google-like query.
 */
class GoogleExpressionParser extends RegexParsers with ExpressionParser {

  def operator: Parser[AST] = chainl1(expression,
      "OR" ^^ { op => (left: AST, right: AST) => ASTOr(left, right)} |
      ""   ^^ { op => (left: AST, right: AST) => ASTAnd(left, right)}
  )

  def expression: Parser[AST] = not | phrase | word | "(" ~> operator <~ ")"

  def not: Parser[AST] = "-" ~> expression ^^ ASTNot.apply

  def word: Parser[AST] = """[^(\- \t)]+""".r ^^ ASTWord.apply

  def phrase: Parser[AST] = "\"" ~> """[^"]+""".r <~ "\"" ^^ ASTPhrase.apply

  def parse(str:String) = parseAll(expression, str).get

}

class ExpressionParseException(cause: Throwable) extends RuntimeException(cause)

object ExpressionParser {

  /**
   * Parses the given expression and returns the Solr query.
   *
   * @param str    the expression
   * @param parser the expression parser (default is [[DefaultExpressionParser]])
   * @return the Solr query
   * @throws ExpressionParseException Failed to parse the given expression.
   */
  def parse(str: String)(implicit parser: ExpressionParser): String = {
    try {
      "(" + visit(parser.parse("(" + normalize(str) + ")")) + ")"
    } catch {
      case e : Throwable => throw new ExpressionParseException(e)
    }
  }

  /**
   * Converts the full-width space to the half-wide space.
   */
  private def normalize(str: String): String = str.replaceAll(" ", " ")

  /**
   * Visits nodes of the given AST recursive and assembles Solr query.
   */
  private def visit(ast: AST): String = {
    ast match {
      case and    : ASTAnd    => "(" + visit(and.left) + " AND " + visit(and.right) + ")"
      case or     : ASTOr     => "(" + visit(or.left) + " OR " + visit(or.right) + ")"
      case not    : ASTNot    => "NOT (" + visit(not.expr) + ")"
      case phrase : ASTPhrase => "\"" + QueryUtils.escape(phrase.value) + "\""
      case word   : ASTWord   => QueryUtils.escape(word.value)
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy