attr_searchable 0.0.2 → 0.0.3
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/README.md +51 -28
- data/lib/attr_searchable.rb +2 -2
- data/lib/attr_searchable/version.rb +1 -1
- data/test/attr_searchable_test.rb +21 -1
- data/test/test_helper.rb +21 -2
- metadata +2 -2
data/README.md
CHANGED
@@ -104,28 +104,9 @@ AttrSearchable will use `LIKE '%...%'` queries. Unfortunately, unless you
|
|
104
104
|
create a [trigram index](http://www.postgresql.org/docs/9.1/static/pgtrgm.html)
|
105
105
|
(postgres only), theses queries can not use SQL indices, such that every row
|
106
106
|
needs to be scanned by your RDBMS when you search for `Book.search("Harry
|
107
|
-
Potter")` or similar
|
108
|
-
|
109
|
-
|
110
|
-
indices (on price, stock, etc) will of course be used by your RDBMS when you
|
111
|
-
search for `Book.search("stock > 0")`, etc.
|
112
|
-
|
113
|
-
Regarding the `LIKE` penalty, the easiest way to make them use indices is
|
114
|
-
to remove the left wildcard. AttrSearchble supports this via:
|
115
|
-
|
116
|
-
```ruby
|
117
|
-
class Book < ActiveRecord::Base
|
118
|
-
# ...
|
119
|
-
|
120
|
-
attr_searchable_options, :title, :left_wildcard => false
|
121
|
-
|
122
|
-
# ...
|
123
|
-
end
|
124
|
-
```
|
125
|
-
|
126
|
-
However, this is often not desirable. Therefore, AttrSearchable can exploit the
|
127
|
-
fulltext index capabilities of MySQL and PostgreSQL. To use already existing
|
128
|
-
fulltext indices, simply tell AttrSearchable to use them via:
|
107
|
+
Potter")` or similar. Therefore, AttrSearchable can exploit the fulltext index
|
108
|
+
capabilities of MySQL and PostgreSQL. To use already existing fulltext indices,
|
109
|
+
simply tell AttrSearchable to use them via:
|
129
110
|
|
130
111
|
```ruby
|
131
112
|
class Book < ActiveRecord::Base
|
@@ -148,7 +129,7 @@ Book.search("Harry Potter")
|
|
148
129
|
```
|
149
130
|
|
150
131
|
Obviously, theses queries won't always return the same results as wildcard
|
151
|
-
`LIKE` queries, because we search for words instead of
|
132
|
+
`LIKE` queries, because we search for words instead of sub-strings. However,
|
152
133
|
fulltext indices will usually of course provide better performance.
|
153
134
|
|
154
135
|
Moreover, the query above is not yet perfect. To improve it even more,
|
@@ -229,6 +210,14 @@ attr_searchable_options :title, :dictionary => "english"
|
|
229
210
|
For more details about PostgreSQL fulltext indices visit
|
230
211
|
[http://www.postgresql.org/docs/9.3/static/textsearch.html](http://www.postgresql.org/docs/9.3/static/textsearch.html)
|
231
212
|
|
213
|
+
## Other indices
|
214
|
+
|
215
|
+
In case you expose non-fulltext attributes to search queries (price, stock,
|
216
|
+
etc.), the respective queries, like `Book.search("stock > 0")`, will profit
|
217
|
+
from from the usual non-fulltext indices. Thus, you should add a usual index on
|
218
|
+
every column you expose to search queries plus a fulltext index for every
|
219
|
+
fulltext attribute.
|
220
|
+
|
232
221
|
## Associations
|
233
222
|
|
234
223
|
If you specify searchable attributes from another model, like
|
@@ -237,14 +226,32 @@ If you specify searchable attributes from another model, like
|
|
237
226
|
class Book < ActiveRecord::Base
|
238
227
|
# ...
|
239
228
|
|
229
|
+
belongs_to :author
|
230
|
+
|
240
231
|
attr_searchable :author => "author.name"
|
241
232
|
|
242
233
|
# ...
|
243
234
|
end
|
244
235
|
```
|
245
236
|
|
246
|
-
AttrSearchable will by default `eager_load`
|
247
|
-
perform `Book.search(...)`.
|
237
|
+
AttrSearchable will by default `eager_load` the referenced associations, when
|
238
|
+
you perform `Book.search(...)`. Assocations of associations can thus as well be
|
239
|
+
referenced and used:
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
class Book < ActiveRecord::Base
|
243
|
+
# ...
|
244
|
+
|
245
|
+
has_many :comments
|
246
|
+
has_many :users, :through => :comments
|
247
|
+
|
248
|
+
attr_searchable :user => "users.username"
|
249
|
+
|
250
|
+
# ...
|
251
|
+
end
|
252
|
+
```
|
253
|
+
|
254
|
+
If you don't want the automatic `eager_load` or need to perform special
|
248
255
|
operations, define a `search_scope` within your model:
|
249
256
|
|
250
257
|
```ruby
|
@@ -333,9 +340,11 @@ Thus, if you use fulltext indices, you better avoid chaining.
|
|
333
340
|
|
334
341
|
## Debugging
|
335
342
|
|
336
|
-
AttrSearchable conveniently
|
337
|
-
|
338
|
-
|
343
|
+
When using `Model#search`, AttrSearchable conveniently prevents certain
|
344
|
+
exceptions from being raised in case the query string passed to it is invalid
|
345
|
+
(parse errors, incompatible datatype errors, etc). Instead, `Model#search`
|
346
|
+
returns an empty relation. However, if you need to debug certain cases, use
|
347
|
+
`Model#unsafe_search`, which will raise them.
|
339
348
|
|
340
349
|
```ruby
|
341
350
|
Book.unsafe_search("stock: None") # => raise AttrSearchable::IncompatibleDatatype
|
@@ -348,3 +357,17 @@ Book.unsafe_search("stock: None") # => raise AttrSearchable::IncompatibleDatatyp
|
|
348
357
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
349
358
|
4. Push to the branch (`git push origin my-new-feature`)
|
350
359
|
5. Create new Pull Request
|
360
|
+
|
361
|
+
## Changelog
|
362
|
+
|
363
|
+
Version 0.0.3:
|
364
|
+
|
365
|
+
* belongs_to association fixes
|
366
|
+
|
367
|
+
Version 0.0.2:
|
368
|
+
|
369
|
+
* Arel abstraction layer added
|
370
|
+
* count() queries resulting in "Cannot visit AttrSearchableGrammar::Nodes..." fixed
|
371
|
+
* Better error messages
|
372
|
+
* Model#unsafe_search added
|
373
|
+
|
data/lib/attr_searchable.rb
CHANGED
@@ -53,9 +53,9 @@ module AttrSearchable
|
|
53
53
|
def attr_searchable_hash(hash)
|
54
54
|
hash.each do |key, value|
|
55
55
|
self.searchable_attributes[key.to_s] = Array(value).collect do |column|
|
56
|
-
table, attribute = column.to_s =~ /\./ ? column.to_s.split(".") : [name, column]
|
56
|
+
table, attribute = column.to_s =~ /\./ ? column.to_s.split(".") : [name.tableize, column]
|
57
57
|
|
58
|
-
"#{table
|
58
|
+
"#{table}.#{attribute}"
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
require File.expand_path("../test_helper", __FILE__)
|
3
3
|
|
4
4
|
class AttrSearchableTest < AttrSearchable::TestCase
|
5
|
-
def
|
5
|
+
def test_multi_associations
|
6
6
|
product = create(:product, :comments => [
|
7
7
|
create(:comment, :title => "Title1", :message => "Message1"),
|
8
8
|
create(:comment, :title => "Title2", :message => "Message2")
|
@@ -12,6 +12,26 @@ class AttrSearchableTest < AttrSearchable::TestCase
|
|
12
12
|
assert_includes Product.search("comment: Title2 comment: Message2"), product
|
13
13
|
end
|
14
14
|
|
15
|
+
def test_single_association
|
16
|
+
expected = create(:comment, :user => create(:user, :username => "Expected"))
|
17
|
+
rejected = create(:comment, :user => create(:user, :username => "Rejected"))
|
18
|
+
|
19
|
+
results = Comment.search("user: Expected")
|
20
|
+
|
21
|
+
assert_includes results, expected
|
22
|
+
refute_includes results, rejected
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_deep_associations
|
26
|
+
expected = create(:product, :comments => [create(:comment, :user => create(:user, :username => "Expected"))])
|
27
|
+
rejected = create(:product, :comments => [create(:comment, :user => create(:user, :username => "Rejected"))])
|
28
|
+
|
29
|
+
results = Product.search("user: Expected")
|
30
|
+
|
31
|
+
assert_includes results, expected
|
32
|
+
refute_includes results, rejected
|
33
|
+
end
|
34
|
+
|
15
35
|
def test_multiple
|
16
36
|
product = create(:product, :comments => [create(:comment, :title => "Title", :message => "Message")])
|
17
37
|
|
data/test/test_helper.rb
CHANGED
@@ -20,13 +20,22 @@ DATABASE = ENV["DATABASE"] || "sqlite"
|
|
20
20
|
|
21
21
|
ActiveRecord::Base.establish_connection YAML.load_file(File.expand_path("../database.yml", __FILE__))[DATABASE]
|
22
22
|
|
23
|
-
class
|
23
|
+
class User < ActiveRecord::Base; end
|
24
|
+
|
25
|
+
class Comment < ActiveRecord::Base
|
26
|
+
include AttrSearchable
|
27
|
+
|
28
|
+
belongs_to :user
|
29
|
+
|
30
|
+
attr_searchable :user => "user.username"
|
31
|
+
attr_searchable :title, :message
|
32
|
+
end
|
24
33
|
|
25
34
|
class Product < ActiveRecord::Base
|
26
35
|
include AttrSearchable
|
27
36
|
|
28
37
|
attr_searchable :title, :description, :brand, :stock, :price, :created_at, :available
|
29
|
-
attr_searchable :comment => ["comments.title", "comments.message"]
|
38
|
+
attr_searchable :comment => ["comments.title", "comments.message"], :user => "users.username"
|
30
39
|
|
31
40
|
attr_searchable :primary => [:title, :description]
|
32
41
|
|
@@ -41,6 +50,7 @@ class Product < ActiveRecord::Base
|
|
41
50
|
end
|
42
51
|
|
43
52
|
has_many :comments
|
53
|
+
has_many :users, :through => :comments
|
44
54
|
end
|
45
55
|
|
46
56
|
FactoryGirl.define do
|
@@ -49,10 +59,14 @@ FactoryGirl.define do
|
|
49
59
|
|
50
60
|
factory :comment do
|
51
61
|
end
|
62
|
+
|
63
|
+
factory :user do
|
64
|
+
end
|
52
65
|
end
|
53
66
|
|
54
67
|
ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS products"
|
55
68
|
ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS comments"
|
69
|
+
ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS users"
|
56
70
|
|
57
71
|
ActiveRecord::Base.connection.create_table :products do |t|
|
58
72
|
t.string :title
|
@@ -66,10 +80,15 @@ end
|
|
66
80
|
|
67
81
|
ActiveRecord::Base.connection.create_table :comments do |t|
|
68
82
|
t.references :product
|
83
|
+
t.references :user
|
69
84
|
t.string :title
|
70
85
|
t.text :message
|
71
86
|
end
|
72
87
|
|
88
|
+
ActiveRecord::Base.connection.create_table :users do |t|
|
89
|
+
t.string :username
|
90
|
+
end
|
91
|
+
|
73
92
|
if DATABASE == "mysql"
|
74
93
|
ActiveRecord::Base.connection.execute "ALTER TABLE products ENGINE=MyISAM"
|
75
94
|
ActiveRecord::Base.connection.execute "ALTER TABLE products ADD FULLTEXT INDEX(title), ADD FULLTEXT INDEX(description), ADD FULLTEXT INDEX(title, description)"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attr_searchable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-06-
|
12
|
+
date: 2014-06-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: treetop
|