2020-02-11 10:31:39 +01:00
# frozen_string_literal: true
require_relative '../../lib/pg_searchable_regex'
describe PgSearchable do
describe 'pg_search' do
describe 'properties' do
describe 'scope' do
it 'defaults to "scope_search"' do
expect ( VectorModel ) . to respond_to ( :scope_search )
end
it 'can use a different scope name' do
expect ( VectorModelWithCustomSearchScope ) . to respond_to ( :fulltext )
end
it 'doesnt pollutes the default method name if customized' do
expect ( VectorModelWithCustomSearchScope ) . not_to respond_to ( :scope_search )
end
end
describe 'language' do
it 'defaults to english lexemes' do
record = VectorModel . create name : 'something' , value : 'amazing'
expect ( VectorModel . scope_search ( 'value:amaz' ) ) . to include ( record )
end
it 'can be changed to simple to avoid lexeme truncation' do
record = SimpleVectorModel . create name : 'something' , value : 'amazing'
expect ( SimpleVectorModel . scope_search ( 'value:amazings' ) ) . not_to include ( record )
end
end
end
describe 'single model' do
2020-02-11 13:36:15 +01:00
it 'fails if column name is unknown' do
record1 = VectorModel . create name : 'hamo' , value : '100'
expect { ( VectorModel . scope_search ( " device_id: #{ record1 . id } " ) ) } . to raise_error ( RuntimeError , " Unknown field 'device_id' " )
end
2020-02-11 10:31:39 +01:00
it 'searches only default column if no column name is used' do
record1 = VectorModel . create name : 'hamo' , value : '-45'
record2 = VectorModel . create name : 'meho' , value : '120'
expect ( VectorModel . scope_search ( '45' ) ) . to be_empty
expect ( VectorModel . scope_search ( " #{ record2 . id } " ) ) . to contain_exactly ( record2 )
end
2020-04-16 15:31:50 +02:00
it 'can search multiple default columns if no column name is used with single search term' do
records = VectorModelWithTwoDefaultColumns . create [ { name : 'hamo' , value : '5' } , { name : 'meho' , value : '20 hamo' } , { name : 'munja-5' , value : '300' } ]
expect ( VectorModelWithTwoDefaultColumns . scope_search ( 'name:hamo' ) ) . to contain_exactly ( records [ 0 ] )
expect ( VectorModelWithTwoDefaultColumns . scope_search ( 'hamo' ) ) . to contain_exactly ( records [ 0 ] , records [ 1 ] )
expect ( VectorModelWithTwoDefaultColumns . scope_search ( '5' ) ) . to contain_exactly ( records [ 0 ] , records [ 2 ] )
end
it 'can search multiple default columns if no column name is used in query containing multiple search terms' do
records = VectorModelWithTwoDefaultColumns . create [ { name : 'hamo' , value : '9' } , { name : 'meho' , value : '5' } , { name : 'oko-9' , value : '100' } ]
expect ( VectorModelWithTwoDefaultColumns . scope_search ( '(9 and not name:hamo) or meho' ) ) . to contain_exactly ( records [ 2 ] , records [ 1 ] )
end
2020-02-11 10:31:39 +01:00
it 'searches column declared in search term' do
record1 = VectorModel . create name : 'hamo' , value : '-45'
record2 = VectorModel . create name : 'meho' , value : '120'
expect ( VectorModel . scope_search ( 'name:45' ) ) . to be_empty
expect ( VectorModel . scope_search ( 'value:120' ) ) . to contain_exactly ( record2 )
end
it 'searches column declared in search term with only partial match' do
record1 = VectorModel . create name : 'hamo' , value : '-45'
record2 = VectorModel . create name : 'meho' , value : '120'
expect ( VectorModel . scope_search ( 'value:-4' ) ) . to contain_exactly ( record1 )
expect ( VectorModel . scope_search ( 'value:12' ) ) . to contain_exactly ( record2 )
end
it 'handles multiple search terms without logical operator between' do
record1 = VectorModel . create name : 'hamo' , value : '-45'
record2 = VectorModel . create name : 'meho' , value : '120'
record3 = VectorModel . create name : 'atif' , value : '80'
expect ( VectorModel . scope_search ( 'value:-4 name:meho' ) ) . to contain_exactly ( record1 , record2 )
end
it 'handles multiple search terms with AND operator between' do
record1 = VectorModel . create name : 'hamo' , value : '-45'
record2 = VectorModel . create name : 'meho' , value : '120'
record3 = VectorModel . create name : 'atif' , value : '80'
expect ( VectorModel . scope_search ( 'value:-4 AND name:meho' ) ) . to be_empty
end
it 'handles search term with NOT operator' do
record1 = VectorModel . create name : 'hamo' , value : '-45'
record2 = VectorModel . create name : 'meho' , value : '120'
record3 = VectorModel . create name : 'atif' , value : '80'
expect ( VectorModel . scope_search ( 'NOT value:0' ) ) . to contain_exactly ( record1 )
end
it 'handles multiple search terms with mixed logical operators between without brackets' do
record1 = VectorModel . create name : 'hamo' , value : '-45'
record2 = VectorModel . create name : 'meho' , value : '120'
record3 = VectorModel . create name : 'atif' , value : '80'
expect ( VectorModel . scope_search ( 'name:hamo AND value:120 OR value:80' ) ) . to contain_exactly ( record3 )
end
it 'handles multiple search terms with mixed logical operators between with brackets' do
record1 = VectorModel . create name : 'hamo' , value : '-45'
record2 = VectorModel . create name : 'meho' , value : '120'
record3 = VectorModel . create name : 'atif' , value : '80'
expect ( VectorModel . scope_search ( 'name:hamo AND (value:120 OR value:80)' ) ) . to be_empty
end
end
2020-02-11 13:36:15 +01:00
describe 'field mappings and joins' do
it 'does not fail if column is unknown but mapping with that name exists' do
record1 = VectorModelWithMappings . create name : 'hamo' , value : '-45'
expect ( VectorModelWithMappings . scope_search ( " device_id: #{ record1 . id } " ) ) . to contain_exactly ( record1 )
end
it 'can search in referenced column' do
record = DynamicModelWithTagValues . create name : 'something' , value : 'amazing'
Tag . create ( taggable : record , value : 'red' )
expect ( DynamicModelWithTagValues . scope_search ( 'tag:red' ) ) . to contain_exactly ( record )
end
it 'can search in referenced column with multiple search terms' do
record1 = DynamicModelWithTagValues . create name : 'something' , value : 'amazing'
record2 = DynamicModelWithTagValues . create name : 'new record' , value : 'not so amazing'
Tag . create ( taggable : record1 , value : 'red' )
Tag . create ( taggable : record2 , value : 'green' )
expect ( DynamicModelWithTagValues . scope_search ( 'tag:red tag:green' ) ) . to contain_exactly ( record1 , record2 )
end
it 'can search in referenced column and in model columns with multiple search terms' do
record1 = DynamicModelWithTagValues . create name : 'something' , value : 'amazing'
record2 = DynamicModelWithTagValues . create name : 'new record' , value : 'not so amazing'
Tag . create ( taggable : record1 , value : 'red' )
Tag . create ( taggable : record2 , value : 'green' )
expect ( DynamicModelWithTagValues . scope_search ( 'tag:red value:"not"' ) ) . to contain_exactly ( record1 , record2 )
end
2020-04-16 10:04:13 +02:00
it 'can find models without tags' do
record1 = DynamicModelWithTagValues . create name : 'something' , value : 'amazing'
record2 = DynamicModelWithTagValues . create name : 'new record' , value : 'not so amazing'
Tag . create ( taggable : record1 , value : 'green' )
expect ( DynamicModelWithTagValues . scope_search ( 'tag:green or value:"not"' ) ) . to contain_exactly ( record1 , record2 )
end
2020-04-20 22:21:26 +02:00
describe 'searching in model with has_many association' do
before do
records = DynamicModelWithTagValues . create [ { name : 'something' , value : 'amazing' } ,
{ name : 'new record' , value : 'not so amazing' } ,
{ name : 'last one' , value : 'no value' } ,
{ name : 'really last one' , value : 'no value' } ]
Tag . create [ { taggable : records [ 0 ] , value : 'red' , custom_attribute : 'rose' } ,
{ taggable : records [ 0 ] , value : 'green' , custom_attribute : 'garden' } ,
{ taggable : records [ 1 ] , value : 'black' , custom_attribute : 'sky' } ,
{ taggable : records [ 2 ] , value : '-1/12' , custom_attribute : 'gold nugget' } ,
{ taggable : records [ 3 ] , value : '-' , custom_attribute : 'unknown' } ,
{ taggable : records [ 3 ] , value : 'red-green' , custom_attribute : 'unicorn' } ]
end
it 'can search with multiple search terms connected with OR operator' do
records = DynamicModelWithTagValues . all
expect ( DynamicModelWithTagValues . scope_search ( 'tag:red or tag:black' ) ) . to contain_exactly ( records [ 0 ] , records [ 1 ] , records [ 3 ] )
expect ( DynamicModelWithTagValues . scope_search ( 'tag:red or tag:green' ) ) . to contain_exactly ( records [ 0 ] , records [ 3 ] )
end
it 'can search with multiple search terms connected with AND operator' do
records = DynamicModelWithTagValues . all
expect ( DynamicModelWithTagValues . scope_search ( 'tag:red and tag:black' ) ) . to be_empty
expect ( DynamicModelWithTagValues . scope_search ( 'tag:red and tag:green' ) ) . to contain_exactly ( records [ 0 ] , records [ 3 ] )
end
it 'can search with multiple search terms and containing NOT' do
records = DynamicModelWithTagValues . all
2020-02-11 13:36:15 +01:00
2020-04-20 22:21:26 +02:00
expect ( DynamicModelWithTagValues . scope_search ( 'not tag:"-1/12" and not value:amazing' ) ) . to contain_exactly ( records [ 3 ] )
end
it 'can search in referenced column and in model columns with multiple search terms connected with logical operators and with brackets' do
records = DynamicModelWithTagValues . all
expect ( DynamicModelWithTagValues . scope_search ( '(tag:- and not tag:12) or (value:"amazing" and not value:"not")' ) ) . to contain_exactly ( records [ 0 ] , records [ 3 ] )
end
2020-02-11 13:36:15 +01:00
end
2020-04-20 22:21:26 +02:00
describe 'searching through multiple referenced columns in model with has_many association' do
before do
records = DynamicModelWithTagValuesAndCustomAttribute . create [ { name : 'something' , value : 'amazing' } ,
{ name : 'new record' , value : 'not so amazing' } ,
{ name : 'last one' , value : 'no value' } ,
{ name : 'really last one' , value : 'no value' } ]
Tag . create [ { taggable : records [ 0 ] , value : 'red' , custom_attribute : 'rose' } ,
{ taggable : records [ 0 ] , value : 'green' , custom_attribute : 'garden' } ,
{ taggable : records [ 1 ] , value : 'black' , custom_attribute : 'sky' } ,
{ taggable : records [ 2 ] , value : '-1/12' , custom_attribute : 'gold nugget' } ,
{ taggable : records [ 3 ] , value : '-' , custom_attribute : 'unknown' } ,
{ taggable : records [ 3 ] , value : 'red-green' , custom_attribute : 'unicorn' } ]
end
it 'can search with multiple search terms connected with OR operator' do
records = DynamicModelWithTagValuesAndCustomAttribute . all
expect ( DynamicModelWithTagValuesAndCustomAttribute . scope_search ( 'tag:red or custom:sky' ) ) . to contain_exactly ( records [ 0 ] , records [ 1 ] , records [ 3 ] )
end
it 'can search with multiple search terms connected with AND operator' do
records = DynamicModelWithTagValuesAndCustomAttribute . all
expect ( DynamicModelWithTagValuesAndCustomAttribute . scope_search ( 'custom:unicorn and tag:black' ) ) . to be_empty
expect ( DynamicModelWithTagValuesAndCustomAttribute . scope_search ( 'tag:"-1/12" and custom:gold' ) ) . to contain_exactly ( records [ 2 ] )
end
it 'can search with multiple search terms and containing NOT' do
expect ( DynamicModelWithTagValuesAndCustomAttribute . scope_search ( 'not tag:"-1/12" and not value:amazing and not custom:unknown' ) ) . to be_empty
end
2020-02-11 13:36:15 +01:00
2020-04-20 22:21:26 +02:00
it 'can search with multiple search terms connected with logical operators and with brackets' do
records = DynamicModelWithTagValuesAndCustomAttribute . all
expect ( DynamicModelWithTagValuesAndCustomAttribute . scope_search ( '(tag:- and not tag:12) or (value:"amazing" and not value:"not") or (custom:unknown or custom:rose)' ) ) . to contain_exactly ( records [ 0 ] , records [ 3 ] )
end
end
describe 'searching in model with multiple has_many associations' do
before do
records = DynamicModelWithTagAndCategories . create [ { name : 'something' , value : 'amazing' } ,
{ name : 'new record' , value : 'not so amazing' } ,
{ name : 'last one' , value : 'no value' } ,
{ name : 'really last one' , value : 'no value' } ]
Tag . create [ { taggable : records [ 0 ] , value : 'red' , custom_attribute : 'rose' } ,
{ taggable : records [ 0 ] , value : 'green' , custom_attribute : 'garden' } ,
{ taggable : records [ 1 ] , value : 'black' , custom_attribute : 'sky' } ,
{ taggable : records [ 2 ] , value : '-1/12' , custom_attribute : 'gold nugget' } ,
{ taggable : records [ 3 ] , value : '-' , custom_attribute : 'unknown' } ,
{ taggable : records [ 3 ] , value : 'red-green' , custom_attribute : 'unicorn' } ]
Category . create [ { categoriable : records [ 0 ] , name : 'home' } ,
{ categoriable : records [ 0 ] , name : 'home' } ,
{ categoriable : records [ 1 ] , name : 'world' } ,
{ categoriable : records [ 2 ] , name : 'math' } ,
{ categoriable : records [ 3 ] , name : 'unknown' } ,
{ categoriable : records [ 3 ] , name : 'myth' } ]
end
it 'can search with multiple search terms connected with OR operator' do
records = DynamicModelWithTagAndCategories . all
expect ( DynamicModelWithTagAndCategories . scope_search ( 'tag:red or category:math' ) ) . to contain_exactly ( records [ 0 ] , records [ 2 ] , records [ 3 ] )
end
it 'can search with multiple search terms connected with AND operator' do
records = DynamicModelWithTagAndCategories . all
expect ( DynamicModelWithTagAndCategories . scope_search ( 'category:home and tag:-' ) ) . to be_empty
expect ( DynamicModelWithTagAndCategories . scope_search ( 'tag:"-1/12" and category:math' ) ) . to contain_exactly ( records [ 2 ] )
end
it 'can search with multiple search terms and containing NOT' do
expect ( DynamicModelWithTagAndCategories . scope_search ( 'not tag:"-1/12" and not value:amazing and not category:unknown' ) ) . to be_empty
end
it 'can search with multiple search terms connected with logical operators and with brackets' do
records = DynamicModelWithTagAndCategories . all
expect ( DynamicModelWithTagAndCategories . scope_search ( '(tag:- and not tag:12) or (value:"amazing" and not value:"not") or (category:unknown or category:math)' ) ) . to contain_exactly ( records [ 0 ] , records [ 2 ] , records [ 3 ] )
end
2020-02-11 13:36:15 +01:00
end
end
2020-02-11 10:31:39 +01:00
end
end