Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

375 lines (302 sloc) 13.07 kb
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
#require 'rubygems'
#require 'parslet'
#require 'spec'
#require 'spec/autorun'
#load '../../lib/parsing_nesting/grammar.rb'
#load '../../lib/parsing_nesting/tree.rb'
module SolrQuerySpecHelper
def parse(s, arg=nil)
if arg
ParsingNesting::Tree.parse(s, arg)
else
ParsingNesting::Tree.parse(s)
end
end
# yields localparam string, and the actual internal query
def local_param_match(query)
expect(query).to match(/^ *_query_:\"\{([^}]+)\}(.*)" *$/)
query =~ /^ *_query_:\"\{([^}]+)\}(.*)" *$/
expect(param_str = $1).not_to be_nil
expect(query = $2).not_to be_nil
yield [param_str, query] if block_given?
end
def bare_local_param_match(query)
expect(query).to match(/ *\{([^}]+)\}(.*)/)
query =~ /\{([^}]+)\}(.*)/
expect(param_str = $1).not_to be_nil
expect(query = $2).not_to be_nil
yield [param_str, query] if block_given?
end
# Convenience for matching a lucene query combining nested queries,
# and getting out the nested queries as matches.
# pass in a string representing a regexp that uses $QUERY as placeholder where
# a nested _query_: will be.
#
# * Any parens in your passed in regexp will
# be paren literals, don't escape em yourself -- you can't do your
# own captures, because if the regexp passes, it'll yield to a block
# with a list, in order, of nested queries.
#
#
# * Can include $ALL to represent literal "*:*"
#
# Yes, the regexp matching isn't as robust as it could be, hard
# to deal with like escaped end-quotes and stuff in a regexp, but
# should mostly work.
def query_template_matcher(top_query, regexp_str)
nested_re = '(_query_:".+")'
regexp_str = regexp_str.gsub("(", '\(').gsub(')', '\)').gsub("$QUERY", nested_re).gsub("$ALL", "\\*\\:\\*")
regexp = Regexp.new('^ *' + regexp_str + ' *$')
expect(top_query).to match( regexp )
yield *regexp.match(top_query).captures if block_given?
end
end
describe "NestingParser" do
describe "Translating to Solr" do
include SolrQuerySpecHelper
describe "with basic simple query" do
before do
@query = parse("one two three").to_query(:qf => "field field2^5", :pf=>"$pf_title")
end
it "should include LocalParams" do
local_param_match(@query) do |params, query|
expect(params).to include("pf=$pf_title")
expect(params).to include('qf=\'field field2^5\'')
end
end
it "should include the query" do
local_param_match(@query) do |params, query|
expect(query).to eq("one two three")
end
end
end
describe "with custom qf" do
it "should insist on dismax for nested query" do
query = parse("one two three").to_query(:defType => "field", :qf=>"$qf")
local_param_match(query) do |params, query|
expect(params).to match(/^\!dismax /)
expect(params).not_to match(/field/)
end
end
it "should insist on edismax for nested query" do
query = parse("one two three", 'edismax').to_query(:defType => "field", :qf=>"$qf")
local_param_match(query) do |params, query|
expect(params).to match(/^\!edismax qf=\$qf/)
expect(params).not_to match(/field/)
end
end
end
describe "with simple mandatory/excluded terms" do
before do
@inner_query = 'red +army -"soviet union" germany'
@full_query = parse(@inner_query).to_query(:qf => "foo", :pf=>"bar")
end
it "should include query" do
local_param_match(@full_query) do |params, query|
expect(query).to eq(@inner_query.gsub('"', '\\\\"'))
end
end
end
describe "with embeddable AND query" do
before do
@query = parse("one two AND three").to_query({:qf => "$qf"})
end
it "should flatten to one dismax query" do
local_param_match(@query) do |params, query|
expect(query).to eq("one +two +three")
end
end
describe ", with mandatory/excluded" do
before do
@query = parse("one -two AND +three").to_query({:qf=>"$qf"})
end
it "should preserve +/- operators" do
local_param_match(@query) do |params, query|
expect(query).to eq("one -two +three")
end
end
end
describe ", deeply nested" do
before do
@query = parse("blue (green AND -violet) AND (+big AND (-small AND medium))").to_query({:qf=>"$qf"})
end
it "should flatten into dismax" do
local_param_match(@query) do |params, query|
expect(query).to eq("blue +green -violet +big -small +medium")
end
end
end
it "for simple OR list, forcing mm=1" do
query = parse("one OR two OR three").to_query(:qf => "$qf", :mm=>"50%")
local_param_match(query) do |params, query|
expect(params).to include("mm=1")
expect(query).to eq("one two three")
end
end
end
describe "that needs to create multiple nested queries" do
it "for two lists, OR'd" do
query = parse("(one two three) OR (red -green +blue)").to_query(:qf => "$qf")
query_template_matcher(query, "( *$QUERY +OR +$QUERY *)" ) do |first_half, second_half|
local_param_match(first_half) do |params, query|
expect(params).to include("qf=$qf")
expect(query).to eq("one two three")
end
local_param_match(second_half) do |params, query|
expect(params).to include("qf=$qf")
expect(query).to eq("red -green +blue")
end
end
end
it "for AND list that can not be flattened" do
params = {:qf=>"$qf", :pf=>"$pf", :mm=>"50%"}
query = parse("a OR b AND x OR y").to_query( params )
query_template_matcher(query, "( *$QUERY +AND +$QUERY *)" ) do |first, second|
expect(first).to eq(parse("a OR b").to_query(params))
expect(second).to eq(parse("x OR y").to_query(params))
end
end
it "for AND of two lists" do
params = {:qf => "$qf", :pf=>"$pf", :mm=>"50%"}
query = parse("(one +two three) AND (four five -six)").to_query( params )
query_template_matcher(query, "( *$QUERY +AND +$QUERY *)" ) do |first, second|
expect(first).to eq(parse("one +two three").to_query(params))
expect(second).to eq(parse("four five -six").to_query(params))
end
end
it "for crazy complicated query" do
query = parse("red AND dawn OR (-night -afternoon) AND NOT (moscow OR beach) ").to_query(:qf => "$qf", :pf =>"$pf", :mm=>"50%")
query_template_matcher(query, "( *$QUERY +AND +( *$QUERY +OR +($ALL AND NOT $QUERY *) *) +AND NOT $QUERY *)") do |red_q, dawn_q, night_q, moscow_q|
local_param_match(red_q) { |params, query| expect(query).to eq("red") }
local_param_match(dawn_q) { |params, query| expect(query).to eq("dawn")}
local_param_match(night_q) do |params, query|
expect(params).to include("mm=1")
expect(query).to eq("night afternoon")
end
local_param_match(moscow_q) do |params, query|
expect(params).to include("mm=1")
expect(query).to eq("moscow beach")
end
end
end
end
describe "for NOT operator" do
it "simple" do
query = parse("NOT frog").to_query
query_template_matcher(query, "NOT $QUERY") do |q|
expect(q).to eq(parse("frog").to_query)
end
end
it "binds tightly" do
query = parse("one NOT two three").to_query
query_template_matcher(query, "$QUERY AND NOT $QUERY") do |q1, q2|
local_param_match(q1) do |params, query|
expect(query).to eq("one three")
end
local_param_match(q2) do |params, query|
expect(query).to eq("two")
end
end
end
it "complicated operand" do
query = parse("one OR two NOT (three OR four AND five)").to_query
#"_query_:'{!dismax mm=1}one two' AND NOT ( _query_:'{!dismax mm=1}three four' AND _query_:'{!dismax }five' )"
query_template_matcher(query, "$QUERY +AND NOT +( *$QUERY +AND +$QUERY *)") do |external_or, internal_or, internal_term|
expect(external_or).to eq(parse("one OR two").to_query)
expect(internal_or).to eq(parse("three OR four").to_query)
expect(internal_term).to eq(parse("five").to_query)
end
end
it "uses workaround on NOT as operand to OR" do
query = parse("two OR (NOT (three))").to_query
query_template_matcher(query, "( *$QUERY +OR +($ALL +AND +NOT +$QUERY) *)")
end
end
describe "for pure negative" do
it "should convert simple pure negative" do
query = parse('-one -two -"a phrase"').to_query(:qf => "$qf", :mm => "100%")
query_template_matcher(query, " *NOT $QUERY") do |query|
local_param_match(query) do |params, query|
expect(params).to include("mm=1")
expect(query).to eq('one two \\"a phrase\\"')
end
end
end
it "should convert pure negative AND" do
query = parse("-one AND -two AND -three").to_query(:qf => "$qf", :mm => "100%")
query_template_matcher(query, "NOT $QUERY") do |query|
local_param_match(query) do |params, query|
expect(params).to match(/mm=1 |$/)
expect(query).to eq('one two three')
end
end
end
it "should convert pure negative OR" do
query = parse("-one OR -two OR -three").to_query
query_template_matcher(query, "NOT $QUERY") do |query|
local_param_match(query) do |params, query|
expect(params).to include("mm=100%")
expect(query).to eq("one two three")
end
end
end
it "should convert crazy pure negative combo" do
query = parse("(-one -two) OR -three OR (-five AND -six)").to_query
query_template_matcher(query, "( *($ALL +AND +NOT +$QUERY) +OR +( *$ALL +AND +NOT +$QUERY *) +OR +( *$ALL +AND +NOT +$QUERY *) *)")
end
end
# When a single parse will be the whole query, we use
# different more compact production
describe "Single Query" do
before do
@solr_local_params = {"qf" => "$title_qf", "pf" => "$title_pf"}
end
describe "simple search" do
it "should work with local params" do
hash = parse("one +two -three").to_single_query_params(@solr_local_params)
expect(hash[:defType]).to eq("dismax")
bare_local_param_match(hash[:q]) do |params, query|
expect(query).to eq("one +two -three")
expect(params).to include("pf=$title_pf")
expect(params).to include("qf=$title_qf")
end
end
it "should work without local params" do
hash = parse("one +two -three").to_single_query_params({})
expect(hash[:defType]).to eq("dismax")
expect(hash[:q]).to eq("one +two -three")
end
end
describe "simple pure negative" do
it "should be nested NOT" do
hash = parse("-one -two").to_single_query_params({})
expect(hash[:defType]).to eq("dismax")
query_template_matcher(hash[:q], "NOT $QUERY") do |query|
local_param_match(query) do |params, query|
expect(query).to eq("one two")
expect(params).to include("mm=1")
end
end
end
end
describe "complex query" do
it "should parse" do
hash = parse("one AND (two OR three)").to_single_query_params({})
expect(hash[:defType]).to eq("lucene")
query_template_matcher(hash[:q], "( *$QUERY +AND +$QUERY *)") do |first, second|
local_param_match(first) do |params, query|
expect(params).to include("dismax")
expect(query).to eq("one")
end
local_param_match(second) do |params, query|
expect(params).to include("mm=1")
expect(params).to include("dismax")
expect(query).to eq("two three")
end
end
end
end
end
end
end
Jump to Line
Something went wrong with that request. Please try again.