dusen 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|