LogicalQueryParser

A parser to generate a tree structure from a logical search query string using treetop.

Dependencies

  • ruby 2.3+
  • treetop 1.6+
  • activerecord 4.2+ (optional)

Installation

Add this line to your application's Gemfile:

gem 'logical_query_parser'

And then execute:

$ bundle

Or install it yourself as:

$ gem install logical_query_parser

Usage

You can parse a logical query string as follows:

parser = LogicalQueryParser.new
parser.parse('a AND b')

# return value is a syntax tree of treetop
=> SyntaxNode+Exp0+ExpNode offset=0, "a AND b" (any):
  SyntaxNode+Cond0+CondNode offset=0, "a AND b" (lexp,logic,rexp):
    SyntaxNode+Literal0+LiteralNode offset=0, "a" (word,negative):
      SyntaxNode offset=0, ""
      SyntaxNode+WordNode offset=0, "a":
        SyntaxNode+Atom0 offset=0, "a":
          SyntaxNode offset=0, ""
          SyntaxNode offset=0, "a"
    SyntaxNode offset=1, " ":
      SyntaxNode offset=1, " "
    SyntaxNode+AndNode offset=2, "AND"
    SyntaxNode offset=5, " ":
      SyntaxNode offset=5, " "
    SyntaxNode+Exp0+ExpNode offset=6, "b" (any):
      SyntaxNode+Literal0+LiteralNode offset=6, "b" (word,negative):
        SyntaxNode offset=6, ""
        SyntaxNode+WordNode offset=6, "b":
          SyntaxNode+Atom0 offset=6, "b":
            SyntaxNode offset=6, ""
            SyntaxNode offset=6, "b"

You can parse quoted strings:

parser = LogicalQueryParser.new
parser.parse('("a a" AND "b b") OR (c AND d)')

You can also parse negative conditions:

parser = LogicalQueryParser.new
parser.parse('("a a" AND NOT "b b") OR (c AND -d)')

Supported operators

  • AND / and / &: represents an AND logic.
  • OR / or / |: represents an OR logic.
  • NOT / -: represents a NOT logic. This should precede to a word or a parenthesis.
  • (: represents beginning of a nested expression.
  • ): represents end of a nested expression.
  • ": represents beginning or end of a quoted word.
  • Space: represents a boundary between two words.

For more information, see grammar definition.

Use with ActiveRecord

You can use a syntax tree to compile a SQL statement for activerecord. For example:

class Doc < ActiveRecord::Base
end

LogicalQueryParser.search("a AND b", Doc.all, :c1, :c2).to_sql
# SELECT "docs".* FROM "docs"
#  WHERE (("docs"."c1" LIKE '%a%' OR "docs"."c2" LIKE '%a%') AND ("docs"."c1" LIKE '%b%' OR "docs"."c2" LIKE '%b%'))

Use with associations:

class Doc < ActiveRecord::Base
  has_many :tags
end

class Tag < ActiveRecord::Base
end

LogicalQueryParser.search("a AND b", Doc.all, :c1, :c2, tags: [:c3]).to_sql
# SELECT "docs".* FROM "docs"
#  INNER JOIN "tags" ON "tags"."doc_id" = "docs"."id"
#  WHERE ((("docs"."c1" LIKE '%a%' OR "docs"."c2" LIKE '%a%') OR "tags"."c3" LIKE '%a%') AND
#        (("docs"."c1" LIKE '%b%' OR "docs"."c2" LIKE '%b%') OR "tags"."c3" LIKE '%b%'))

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/kanety/logical_query_parser.

License

The gem is available as open source under the terms of the MIT License.