diff --git a/grammar.y b/grammar.y index 5484994..2eed955 100644 --- a/grammar.y +++ b/grammar.y @@ -14,7 +14,9 @@ class Query | TERM_WITHOUT_QUOTES COLON TERM_WITH_QUOTES { result = {val[0] => val[2]} } | expression OPERATOR_OR expression { result = {:OPERATOR_OR => [val[0], val[2]]} } | expression OPERATOR_AND expression { result = {:OPERATOR_AND => [val[0], val[2]]} } + | OPERATOR_NOT expression { result = {:OPERATOR_NOT => val[1]} } | L_BRACKET expression R_BRACKET { result = val[1] } + | expression expression { result = {:OPERATOR_OR => [val[0], val[1]]} } end ---- header diff --git a/spec/query_parser_spec.rb b/spec/query_parser_spec.rb index f26c4a3..58e3da7 100644 --- a/spec/query_parser_spec.rb +++ b/spec/query_parser_spec.rb @@ -133,7 +133,7 @@ class QueryParserTester expect(@result[:OPERATOR_AND]).to eq @expected_array_total end - it 'tests operator precedence' do + it 'tests operator precedence, or - and' do @result1 = @evaluator.parse('tag:mta or name:JF and 12_4') @result2 = @evaluator.parse('tag:mta or (name:JF and 12_4)') @@ -155,10 +155,216 @@ class QueryParserTester end - # Tests to write : - # * query with multiple column names and search terms without logical operators - # * AND NOT, OR NOT tests + it 'tests operator precedence, and - or' do + @result1 = @evaluator.parse('tag:mta and name:JF or 12_4') + @result2 = @evaluator.parse('(tag:mta and name:JF) or 12_4') + expect(@result1).to eq @result2 + + expect(@result1.length).to eq 1 + + @expected_array_part_2 = [ + {'tag' => 'mta'}, + {'name' => 'JF'} + ] + + @expected_array_total = [ + {:OPERATOR_AND => @expected_array_part_2}, + {:DEFAULT_COLUMN => '12_4'} + ] + + expect(@result1[:OPERATOR_OR]).to eq @expected_array_total + + end + + it 'tests simple query with not operator' do + @result = @evaluator.parse('not -54') + + expect(@result.length).to be 1 + expect(@result[:OPERATOR_NOT][:DEFAULT_COLUMN]).to eq '-54' + end + + it 'tests query with mixed NOT and AND logic operator' do + @result = @evaluator.parse('name:"some wild name" and not -123-456') + + @expected_and_array = [ + {'name' => '"some wild name"'}, + {:OPERATOR_NOT => {:DEFAULT_COLUMN => '-123-456'}} + ] + + expect(@result.length).to eq 1 + expect(@result[:OPERATOR_AND]).to eq @expected_and_array + end + + it 'tests query with mixed NOT, AND and OR logic operators' do + @result = @evaluator.parse('name:"some wild name" or not (tag:mta and -123-456)') + + @expected_and_array_operands = [ + {'tag' => 'mta'}, + {:DEFAULT_COLUMN => '-123-456'} + ] + + @expected_or_array_operands = [ + {'name' => '"some wild name"'}, + {:OPERATOR_NOT => {:OPERATOR_AND => @expected_and_array_operands}} + ] + + expect(@result.length).to eq 1 + expect(@result[:OPERATOR_OR]).to eq @expected_or_array_operands + end + + it 'tests query with two default column search terms without quotes and without logical operators' do + @result = @evaluator.parse('id-5 -456') + + @expected_array = [ + {:DEFAULT_COLUMN => 'id-5'}, + {:DEFAULT_COLUMN => '-456'} + ] + + expect(@result.length).to eq 1 + expect(@result[:OPERATOR_OR]).to eq @expected_array + end + + it 'tests query with two default column search terms with quotes and without logical operators' do + @result = @evaluator.parse('"id-5 no Q" -456') + + @expected_array = [ + {:DEFAULT_COLUMN => '"id-5 no Q"'}, + {:DEFAULT_COLUMN => '-456'} + ] + + expect(@result.length).to eq 1 + expect(@result[:OPERATOR_OR]).to eq @expected_array + end + + it 'tests query with two default column search terms with quotes on both terms and without logical operators' do + @result = @evaluator.parse('"id-5 no Q" "wild id -456"') + + @expected_array = [ + {:DEFAULT_COLUMN => '"id-5 no Q"'}, + {:DEFAULT_COLUMN => '"wild id -456"'} + ] + + expect(@result.length).to eq 1 + expect(@result[:OPERATOR_OR]).to eq @expected_array + end + + it 'tests query with one default column and one non defualt column, search terms without quotes and without logical operators' do + @result = @evaluator.parse('tag:mta -456') + + @expected_array = [ + {'tag' => 'mta'}, + {:DEFAULT_COLUMN => '-456'} + ] + + expect(@result.length).to eq 1 + expect(@result[:OPERATOR_OR]).to eq @expected_array + end + + it 'tests query with one default column and one non defualt column, search terms with quotes and without logical operators' do + @result = @evaluator.parse('-1-23 name:"WILD name"') + + @expected_array = [ + {:DEFAULT_COLUMN => '-1-23'}, + {'name' => '"WILD name"'} + ] + + expect(@result.length).to eq 1 + expect(@result[:OPERATOR_OR]).to eq @expected_array + end + + it 'tests query with two columns, search terms with quotes and without logical operators' do + @result = @evaluator.parse('tag:-1-23 name:"WILD name"') + + @expected_array = [ + {'tag' => '-1-23'}, + {'name' => '"WILD name"'} + ] + + expect(@result.length).to eq 1 + expect(@result[:OPERATOR_OR]).to eq @expected_array + end + + it 'tests query with two columns, search terms with quotes on both and without logical operators' do + @result = @evaluator.parse('tag:"1 2 3" name:"WILD name"') + + @expected_array = [ + {'tag' => '"1 2 3"'}, + {'name' => '"WILD name"'} + ] + + expect(@result.length).to eq 1 + expect(@result[:OPERATOR_OR]).to eq @expected_array + end + + it 'tests query with three columns, search terms with quotes on both and without logical operators' do + @result = @evaluator.parse('tag:"1 2 3" name:"WILD name" name:"ANOTHER wild name"') + + @expected_array = [ + {'tag' => '"1 2 3"'}, + {:OPERATOR_OR => [ + {'name' => '"WILD name"'}, + {'name' => '"ANOTHER wild name"'} + ]} + ] + + expect(@result.length).to eq 1 + expect(@result[:OPERATOR_OR]).to eq @expected_array + end + + it 'tests complex query' do + @result = @evaluator.parse('(device-id:"with space" tag:mta no-quotes-id-123)'\ + 'or "id with quotes-5" and ( ("id with q 10" or "id with q 20")'\ + 'and ("id with Q 30" "id with Q 40") and not id-without-Q-50)') + + # (device-id:"with space" tag:mta no-quotes-id-123) or "id with quotes-5" and ( ("id with q 10" or "id with q 20") and ("id with Q 30" "id with Q 40") and not id-without-Q-50) + # _____________________A___________________________ or _______B___________ and __________________________________________________C______________________________________________ + # (____A1________________ ___A2__ ______A3________) or _______B___________ and ( ____________C1____________________ and ______________C2_______________ and ______C3___________) + + + @A = {:OPERATOR_OR => [ + {'device-id' => '"with space"'}, + {:OPERATOR_OR => [ + {'tag' => 'mta'}, + {:DEFAULT_COLUMN => 'no-quotes-id-123'}] + } + ]} + + @B = {:DEFAULT_COLUMN => '"id with quotes-5"'} + + + @C1 = {:OPERATOR_OR => [ + {:DEFAULT_COLUMN => '"id with q 10"'}, + {:DEFAULT_COLUMN => '"id with q 20"'} + ]} + + + @C2 = {:OPERATOR_OR => [ + {:DEFAULT_COLUMN => '"id with Q 30"'}, + {:DEFAULT_COLUMN => '"id with Q 40"'} + ]} + + @C3 = {:OPERATOR_NOT => {:DEFAULT_COLUMN => 'id-without-Q-50'}} + + @C = {:OPERATOR_AND => [ + {:OPERATOR_AND => [ + @C1, + @C2 + ]}, + @C3 + ]} + + @expected_final_array_result = [ + @A, + {:OPERATOR_AND => [ + @B, + @C + ]} + ] + + expect(@result.length).to eq 1 + expect(@result[:OPERATOR_OR]).to eq @expected_final_array_result + end end end