dusen 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/.travis.yml +3 -0
- data/README.md +176 -47
- data/documents/fulltext_vs_like_benchmark/all_records_and_scope/fulltext.csv +22 -0
- data/documents/fulltext_vs_like_benchmark/all_records_and_scope/fulltext_vs_like.xls +0 -0
- data/documents/fulltext_vs_like_benchmark/all_records_and_scope/like.csv +22 -0
- data/documents/fulltext_vs_like_benchmark/benchmark.rb +70 -0
- data/documents/fulltext_vs_like_benchmark/exact_number_of_records/fulltext.csv +22 -0
- data/documents/fulltext_vs_like_benchmark/exact_number_of_records/fulltext_vs_like.png +0 -0
- data/documents/fulltext_vs_like_benchmark/exact_number_of_records/fulltext_vs_like.xls +0 -0
- data/documents/fulltext_vs_like_benchmark/exact_number_of_records/like.csv +21 -0
- data/dusen.gemspec +1 -1
- data/lib/dusen/active_record/base_ext.rb +104 -0
- data/lib/dusen/active_record/search_text.rb +50 -0
- data/lib/dusen/description.rb +5 -4
- data/lib/dusen/query.rb +24 -4
- data/lib/dusen/railtie.rb +9 -0
- data/lib/dusen/syntax.rb +5 -0
- data/lib/dusen/tasks.rb +31 -0
- data/lib/dusen/token.rb +4 -0
- data/lib/dusen/util.rb +86 -1
- data/lib/dusen/version.rb +1 -1
- data/lib/dusen.rb +7 -1
- data/spec/rails-2.3/Gemfile +3 -1
- data/spec/rails-2.3/Rakefile +1 -1
- data/spec/rails-2.3/app_root/config/database.yml +4 -19
- data/spec/rails-2.3/app_root/config/environments/{in_memory.rb → test.rb} +0 -0
- data/spec/rails-2.3/spec/spec_helper.rb +7 -9
- data/spec/rails-3.0/Gemfile +3 -1
- data/spec/rails-3.0/Rakefile +1 -1
- data/spec/rails-3.0/app_root/config/database.yml +5 -3
- data/spec/rails-3.0/spec/spec_helper.rb +6 -7
- data/spec/rails-3.2/Gemfile +2 -1
- data/spec/rails-3.2/Rakefile +1 -1
- data/spec/rails-3.2/app_root/config/database.yml +5 -3
- data/spec/rails-3.2/spec/spec_helper.rb +3 -7
- data/spec/shared/app_root/app/models/recipe/category.rb +13 -0
- data/spec/shared/app_root/app/models/recipe/ingredient.rb +13 -0
- data/spec/shared/app_root/app/models/recipe.rb +14 -0
- data/spec/shared/app_root/app/models/user/with_fulltext.rb +35 -0
- data/spec/shared/app_root/app/models/user/without_fulltext.rb +34 -0
- data/spec/shared/app_root/config/database.sample.yml +6 -0
- data/spec/shared/app_root/db/migrate/001_create_search_text.rb +19 -0
- data/spec/shared/app_root/db/migrate/002_create_user_variants.rb +25 -0
- data/spec/shared/app_root/db/migrate/003_create_recipe_models.rb +23 -0
- data/spec/shared/spec/dusen/active_record/base_ext_spec.rb +138 -0
- data/spec/shared/spec/dusen/active_record/search_text_spec.rb +23 -0
- data/spec/shared/spec/dusen/parser_spec.rb +14 -0
- data/spec/shared/spec/dusen/query_spec.rb +20 -0
- data/spec/shared/spec/dusen/util_spec.rb +21 -0
- metadata +80 -46
- data/lib/dusen/active_record_ext.rb +0 -35
- data/spec/rails-2.3/app_root/config/environments/mysql.rb +0 -0
- data/spec/rails-2.3/app_root/config/environments/postgresql.rb +0 -0
- data/spec/rails-2.3/app_root/config/environments/sqlite.rb +0 -0
- data/spec/rails-2.3/app_root/config/environments/sqlite3.rb +0 -0
- data/spec/shared/app_root/app/models/user.rb +0 -22
- data/spec/shared/app_root/db/migrate/001_create_users.rb +0 -17
- data/spec/shared/dusen/active_record_spec.rb +0 -55
@@ -0,0 +1,138 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
shared_examples_for 'model with search syntax' do
|
6
|
+
|
7
|
+
describe '.search' do
|
8
|
+
|
9
|
+
it 'should find records by given words' do
|
10
|
+
match = subject.create!(:name => 'Abraham')
|
11
|
+
no_match = subject.create!(:name => 'Elizabath')
|
12
|
+
subject.search('Abraham').to_a.should == [match]
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should AND multiple words' do
|
16
|
+
match = subject.create!(:name => 'Abraham Lincoln')
|
17
|
+
no_match = subject.create!(:name => 'Abraham')
|
18
|
+
subject.search('Abraham Lincoln').to_a.should == [match]
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should find records by phrases' do
|
22
|
+
match = subject.create!(:name => 'Abraham Lincoln')
|
23
|
+
no_match = subject.create!(:name => 'Abraham John Lincoln')
|
24
|
+
subject.search('"Abraham Lincoln"').to_a.should == [match]
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should find records by qualified fields' do
|
28
|
+
match = subject.create!(:name => 'foo@bar.com', :email => 'foo@bar.com')
|
29
|
+
no_match = subject.create!(:name => 'foo@bar.com', :email => 'bam@baz.com')
|
30
|
+
subject.search('email:foo@bar.com').to_a.should == [match]
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should allow phrases as values for qualified field queries' do
|
34
|
+
match = subject.create!(:name => 'Foo Bar', :city => 'Foo Bar')
|
35
|
+
no_match = subject.create!(:name => 'Foo Bar', :city => 'Bar Foo')
|
36
|
+
subject.search('city:"Foo Bar"').to_a.should == [match]
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should allow to mix multiple types of tokens in a single query' do
|
40
|
+
match = subject.create!(:name => 'Abraham', :city => 'Foohausen')
|
41
|
+
no_match = subject.create!(:name => 'Abraham', :city => 'Barhausen')
|
42
|
+
subject.search('Foo city:Foohausen').to_a.should == [match]
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should not find records from another model' do
|
46
|
+
match = subject.create!(:name => 'Abraham')
|
47
|
+
Recipe.create!(:name => 'Abraham')
|
48
|
+
subject.search('Abraham').to_a.should == [match]
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '.search_syntax' do
|
54
|
+
|
55
|
+
it "should return the model's syntax definition when called without a block" do
|
56
|
+
subject.search_syntax.should be_a(Dusen::Syntax)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should be callable multiple times, appending additional syntax' do
|
60
|
+
subject.search_syntax.fields.keys.should =~ ['text', 'email', 'city', 'role']
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
describe ActiveRecord::Base do
|
69
|
+
|
70
|
+
describe 'for a model without an associated FULLTEXT table' do
|
71
|
+
|
72
|
+
subject { User::WithoutFulltext }
|
73
|
+
|
74
|
+
it_should_behave_like 'model with search syntax'
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'for a model with an associated FULLTEXT table' do
|
79
|
+
|
80
|
+
subject { User::WithFulltext }
|
81
|
+
|
82
|
+
it_should_behave_like 'model with search syntax'
|
83
|
+
|
84
|
+
it 'should be shadowed by a Dusen::ActiveRecord::SearchText, which is created, updated and destroyed with the record' do
|
85
|
+
user = User::WithFulltext.create!(:name => 'name', :email => 'email', :city => 'city')
|
86
|
+
User::WithFulltext.index_search_texts
|
87
|
+
Dusen::ActiveRecord::SearchText.all.collect(&:words).should == ['name email city']
|
88
|
+
user.reload
|
89
|
+
user.update_attributes!(:email => 'changed_email')
|
90
|
+
User::WithFulltext.index_search_texts
|
91
|
+
Dusen::ActiveRecord::SearchText.all.collect(&:words).should == ['name changed_email city']
|
92
|
+
user.destroy
|
93
|
+
User::WithFulltext.index_search_texts
|
94
|
+
Dusen::ActiveRecord::SearchText.count.should be_zero
|
95
|
+
end
|
96
|
+
|
97
|
+
describe 'indexing fields from associated records'
|
98
|
+
|
99
|
+
it 'should allow to index fields from an associated record' do
|
100
|
+
category = Recipe::Category.create!(:name => 'Rice')
|
101
|
+
recipe = Recipe.create!(:name => 'Martini Chicken', :category => category)
|
102
|
+
recipe.ingredients.create!(:name => 'Paprica')
|
103
|
+
recipe.ingredients.create!(:name => 'Tomatoes')
|
104
|
+
Recipe.search('Rice').to_a.should == [recipe]
|
105
|
+
Recipe.search('Martini').to_a.should == [recipe]
|
106
|
+
Recipe.search('Paprica').to_a.should == [recipe]
|
107
|
+
Recipe.search('Tomatoes').to_a.should == [recipe]
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'if the associated model has a .part_of_search_text_for directive' do
|
111
|
+
|
112
|
+
it 'should automatically reindex itself when an associated record changes' do
|
113
|
+
category = Recipe::Category.create!(:name => 'Rice')
|
114
|
+
recipe = category.recipes.create!(:name => 'Martini Chicken')
|
115
|
+
ingredient = recipe.ingredients.create!(:name => 'Paprica')
|
116
|
+
category.update_attributes!(:name => 'Noodles')
|
117
|
+
ingredient.update_attributes!(:name => 'Onions')
|
118
|
+
Recipe.search('Noodles').to_a.should == [recipe]
|
119
|
+
Recipe.search('Onion').to_a.should == [recipe]
|
120
|
+
category.reload
|
121
|
+
category.destroy
|
122
|
+
Recipe.search('Noodles').to_a.should be_empty
|
123
|
+
end
|
124
|
+
|
125
|
+
#it 'should automatically reindex both containers if the container changes' do
|
126
|
+
# recipe1 = Recipe.create!(:name => 'Martini Chicken')
|
127
|
+
# recipe2 = Recipe.create!(:name => 'Whiskey Chicken')
|
128
|
+
# ingredient = recipe1.ingredients.create!(:name => 'Paprica')
|
129
|
+
# Recipe.search('Paprica').to_a.should == [recipe1]
|
130
|
+
# ingredient.update_attributes!(:recipe => recipe2)
|
131
|
+
# Recipe.search('Paprica').to_a.should == [recipe2]
|
132
|
+
#end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dusen::ActiveRecord::SearchText do
|
4
|
+
|
5
|
+
describe '.match' do
|
6
|
+
|
7
|
+
it 'should find records for the given list of words' do
|
8
|
+
match = User::WithFulltext.create!(:name => 'Abraham', :city => 'Fooville')
|
9
|
+
no_match = User::WithFulltext.create!(:name => 'Elizabeth', :city => 'Fooville')
|
10
|
+
matches = Dusen::ActiveRecord::SearchText.match(User::WithFulltext, ['Abraham', 'Fooville'])
|
11
|
+
matches.all.should == [match]
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should find records by only giving the prefix of a word' do
|
15
|
+
match = User::WithFulltext.create!(:name => 'Abraham')
|
16
|
+
no_match = User::WithFulltext.create!(:name => 'Elizabeth')
|
17
|
+
matches = Dusen::ActiveRecord::SearchText.match(User::WithFulltext, ['Abra'])
|
18
|
+
matches.all.should == [match]
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dusen::Parser do
|
4
|
+
|
5
|
+
describe '.parse' do
|
6
|
+
|
7
|
+
it 'should parse field tokens first, because they usually give maximum filtering at little cost' do
|
8
|
+
query = Dusen::Parser.parse('word1 field1:field1-value word2 field2:field2-value')
|
9
|
+
query.collect(&:value).should == ['field1-value', 'field2-value', 'word1', 'word2']
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dusen::Query do
|
4
|
+
|
5
|
+
describe '#condensed' do
|
6
|
+
|
7
|
+
it 'should return a version of the query where all text tokens have been collapsed into a single token with an Array value' do
|
8
|
+
query = Dusen::Parser.parse('field:value foo bar baz')
|
9
|
+
query.tokens.size.should == 4
|
10
|
+
condensed_query = query.condensed
|
11
|
+
condensed_query.tokens.size.should == 2
|
12
|
+
condensed_query[0].field.should == 'field'
|
13
|
+
condensed_query[0].value.should == 'value'
|
14
|
+
condensed_query[1].field.should == 'text'
|
15
|
+
condensed_query[1].value.should == ['foo', 'bar', 'baz']
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dusen::Util do
|
4
|
+
|
5
|
+
describe '.boolean_fulltext_query' do
|
6
|
+
|
7
|
+
it 'should generate a query for boolean MySQL fulltext search, which includes all words and allows additional characters on the right side' do
|
8
|
+
Dusen::Util.boolean_fulltext_query(['aaa', 'bbb']).should == '+aaa* +bbb*'
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should keep phrases intact' do
|
12
|
+
Dusen::Util.boolean_fulltext_query(['aaa', 'bbb ccc', 'ddd']).should == '+aaa* +"bbb ccc" +ddd*'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should escape characters that have special meaning in boolean MySQL fulltext searches' do
|
16
|
+
Dusen::Util.boolean_fulltext_query(['+-~\\']).should == '+\\+\\-\\~\\\\*'
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
metadata
CHANGED
@@ -1,50 +1,68 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: dusen
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Henning Koch
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
|
18
|
+
date: 2012-12-10 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
15
22
|
name: rails
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: Comprehensive full text search for ActiveRecord and MySQL
|
32
36
|
email: henning.koch@makandra.de
|
33
37
|
executables: []
|
38
|
+
|
34
39
|
extensions: []
|
40
|
+
|
35
41
|
extra_rdoc_files: []
|
36
|
-
|
42
|
+
|
43
|
+
files:
|
37
44
|
- .gitignore
|
38
45
|
- .travis.yml
|
39
46
|
- README.md
|
40
47
|
- Rakefile
|
48
|
+
- documents/fulltext_vs_like_benchmark/all_records_and_scope/fulltext.csv
|
49
|
+
- documents/fulltext_vs_like_benchmark/all_records_and_scope/fulltext_vs_like.xls
|
50
|
+
- documents/fulltext_vs_like_benchmark/all_records_and_scope/like.csv
|
51
|
+
- documents/fulltext_vs_like_benchmark/benchmark.rb
|
52
|
+
- documents/fulltext_vs_like_benchmark/exact_number_of_records/fulltext.csv
|
53
|
+
- documents/fulltext_vs_like_benchmark/exact_number_of_records/fulltext_vs_like.png
|
54
|
+
- documents/fulltext_vs_like_benchmark/exact_number_of_records/fulltext_vs_like.xls
|
55
|
+
- documents/fulltext_vs_like_benchmark/exact_number_of_records/like.csv
|
41
56
|
- dusen.gemspec
|
42
57
|
- lib/dusen.rb
|
43
|
-
- lib/dusen/
|
58
|
+
- lib/dusen/active_record/base_ext.rb
|
59
|
+
- lib/dusen/active_record/search_text.rb
|
44
60
|
- lib/dusen/description.rb
|
45
61
|
- lib/dusen/parser.rb
|
46
62
|
- lib/dusen/query.rb
|
63
|
+
- lib/dusen/railtie.rb
|
47
64
|
- lib/dusen/syntax.rb
|
65
|
+
- lib/dusen/tasks.rb
|
48
66
|
- lib/dusen/token.rb
|
49
67
|
- lib/dusen/util.rb
|
50
68
|
- lib/dusen/version.rb
|
@@ -53,11 +71,7 @@ files:
|
|
53
71
|
- spec/rails-2.3/app_root/config/boot.rb
|
54
72
|
- spec/rails-2.3/app_root/config/database.yml
|
55
73
|
- spec/rails-2.3/app_root/config/environment.rb
|
56
|
-
- spec/rails-2.3/app_root/config/environments/
|
57
|
-
- spec/rails-2.3/app_root/config/environments/mysql.rb
|
58
|
-
- spec/rails-2.3/app_root/config/environments/postgresql.rb
|
59
|
-
- spec/rails-2.3/app_root/config/environments/sqlite.rb
|
60
|
-
- spec/rails-2.3/app_root/config/environments/sqlite3.rb
|
74
|
+
- spec/rails-2.3/app_root/config/environments/test.rb
|
61
75
|
- spec/rails-2.3/app_root/config/initializers/fix_missing_source_file.rb
|
62
76
|
- spec/rails-2.3/app_root/config/preinitializer.rb
|
63
77
|
- spec/rails-2.3/app_root/config/routes.rb
|
@@ -104,33 +118,53 @@ files:
|
|
104
118
|
- spec/rails-3.2/rcov.opts
|
105
119
|
- spec/rails-3.2/spec/spec_helper.rb
|
106
120
|
- spec/shared/app_root/app/controllers/application_controller.rb
|
107
|
-
- spec/shared/app_root/app/models/
|
108
|
-
- spec/shared/app_root/
|
109
|
-
- spec/shared/
|
121
|
+
- spec/shared/app_root/app/models/recipe.rb
|
122
|
+
- spec/shared/app_root/app/models/recipe/category.rb
|
123
|
+
- spec/shared/app_root/app/models/recipe/ingredient.rb
|
124
|
+
- spec/shared/app_root/app/models/user/with_fulltext.rb
|
125
|
+
- spec/shared/app_root/app/models/user/without_fulltext.rb
|
126
|
+
- spec/shared/app_root/config/database.sample.yml
|
127
|
+
- spec/shared/app_root/db/migrate/001_create_search_text.rb
|
128
|
+
- spec/shared/app_root/db/migrate/002_create_user_variants.rb
|
129
|
+
- spec/shared/app_root/db/migrate/003_create_recipe_models.rb
|
130
|
+
- spec/shared/spec/dusen/active_record/base_ext_spec.rb
|
131
|
+
- spec/shared/spec/dusen/active_record/search_text_spec.rb
|
132
|
+
- spec/shared/spec/dusen/parser_spec.rb
|
133
|
+
- spec/shared/spec/dusen/query_spec.rb
|
134
|
+
- spec/shared/spec/dusen/util_spec.rb
|
135
|
+
has_rdoc: true
|
110
136
|
homepage: https://github.com/makandra/dusen
|
111
137
|
licenses: []
|
138
|
+
|
112
139
|
post_install_message:
|
113
140
|
rdoc_options: []
|
114
|
-
|
141
|
+
|
142
|
+
require_paths:
|
115
143
|
- lib
|
116
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
145
|
none: false
|
118
|
-
requirements:
|
119
|
-
- -
|
120
|
-
- !ruby/object:Gem::Version
|
121
|
-
|
122
|
-
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
hash: 3
|
150
|
+
segments:
|
151
|
+
- 0
|
152
|
+
version: "0"
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
154
|
none: false
|
124
|
-
requirements:
|
125
|
-
- -
|
126
|
-
- !ruby/object:Gem::Version
|
127
|
-
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
hash: 3
|
159
|
+
segments:
|
160
|
+
- 0
|
161
|
+
version: "0"
|
128
162
|
requirements: []
|
163
|
+
|
129
164
|
rubyforge_project:
|
130
|
-
rubygems_version: 1.
|
165
|
+
rubygems_version: 1.3.9.4
|
131
166
|
signing_key:
|
132
167
|
specification_version: 3
|
133
|
-
summary:
|
134
|
-
scope chains
|
168
|
+
summary: Comprehensive full text search for ActiveRecord and MySQL
|
135
169
|
test_files: []
|
136
|
-
|
170
|
+
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Dusen
|
4
|
-
module ActiveRecord
|
5
|
-
|
6
|
-
def search_syntax(&dsl)
|
7
|
-
if dsl
|
8
|
-
@search_syntax = Dusen::Description.read_syntax(&dsl)
|
9
|
-
singleton_class.send(:define_method, :search) do |query_string|
|
10
|
-
@search_syntax.search(self, query_string)
|
11
|
-
end
|
12
|
-
else
|
13
|
-
@search_syntax
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def where_like(conditions)
|
18
|
-
scope = self
|
19
|
-
conditions.each do |field_or_fields, query|
|
20
|
-
fields = Array(field_or_fields).collect do |field|
|
21
|
-
Util.qualify_column_name(scope, field)
|
22
|
-
end
|
23
|
-
query_with_placeholders = fields.collect { |field| "#{field} LIKE ?" }.join(' OR ')
|
24
|
-
like_expression = Dusen::Util.like_expression(query)
|
25
|
-
bindings = [like_expression] * fields.size
|
26
|
-
conditions = [ query_with_placeholders, *bindings ]
|
27
|
-
scope = Util.append_scope_conditions(scope, conditions)
|
28
|
-
end
|
29
|
-
scope
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
ActiveRecord::Base.send(:extend, Dusen::ActiveRecord)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
class User < ActiveRecord::Base
|
4
|
-
|
5
|
-
search_syntax do
|
6
|
-
|
7
|
-
search_by :text do |scope, text|
|
8
|
-
scope.where_like([:name, :email, :city] => text)
|
9
|
-
end
|
10
|
-
|
11
|
-
search_by :city do |scope, city|
|
12
|
-
scope.scoped(:conditions => { :city => city })
|
13
|
-
end
|
14
|
-
|
15
|
-
search_by :email do |scope, email|
|
16
|
-
scope.scoped(:conditions => { :email => email })
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Dusen::ActiveRecord do
|
6
|
-
|
7
|
-
describe '.search' do
|
8
|
-
|
9
|
-
it 'should find records by given words' do
|
10
|
-
match = User.create!(:name => 'foo')
|
11
|
-
no_match = User.create!(:name => 'bar')
|
12
|
-
User.search('foo').to_a.should == [match]
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'should AND multiple words' do
|
16
|
-
match = User.create!(:name => 'foo bar')
|
17
|
-
no_match = User.create!(:name => 'foo')
|
18
|
-
User.search('foo bar').to_a.should == [match]
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'should find records by phrases' do
|
22
|
-
match = User.create!(:name => 'foo bar baz')
|
23
|
-
no_match = User.create!(:name => 'foo baz bar')
|
24
|
-
User.search('"foo bar"').to_a.should == [match]
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'should find records by qualified fields' do
|
28
|
-
match = User.create!(:name => 'foo@bar.com', :email => 'foo@bar.com')
|
29
|
-
no_match = User.create!(:name => 'foo@bar.com', :email => 'bam@baz.com')
|
30
|
-
User.search('email:foo@bar.com').to_a.should == [match]
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'should allow phrases as values for qualified field queries' do
|
34
|
-
match = User.create!(:name => 'Foo Bar', :city => 'Foo Bar')
|
35
|
-
no_match = User.create!(:name => 'Foo Bar', :city => 'Bar Foo')
|
36
|
-
User.search('city:"Foo Bar"').to_a.should == [match]
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'should allow to mix multiple types of tokens in a single query' do
|
40
|
-
match = User.create!(:name => 'Foo', :city => 'Foohausen')
|
41
|
-
no_match = User.create!(:name => 'Foo', :city => 'Barhausen')
|
42
|
-
User.search('Foo city:Foohausen').to_a.should == [match]
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
describe '.search_syntax' do
|
48
|
-
|
49
|
-
it "should return the model's syntax definition when called without a block" do
|
50
|
-
User.search_syntax.should be_a(Dusen::Syntax)
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|