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 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, which is btw. usually ok for small data sets and a small
108
- amount of regular queries. Contrary, when you search for
109
- `Book.search("title=Potter")` indices can and will be used. Moreover, other
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 substrings. However,
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` these associations, when you
247
- perform `Book.search(...)`. If you don't want that or need to perform special
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 hides certain errors, like parse errors, and
337
- instead returns an empty relation. However, if you need to debug certain
338
- cases, use `Model#unsafe_search`, which will raise them.
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
+
@@ -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.tableize}.#{attribute}"
58
+ "#{table}.#{attribute}"
59
59
  end
60
60
  end
61
61
  end
@@ -1,3 +1,3 @@
1
1
  module AttrSearchable
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -2,7 +2,7 @@
2
2
  require File.expand_path("../test_helper", __FILE__)
3
3
 
4
4
  class AttrSearchableTest < AttrSearchable::TestCase
5
- def test_associations
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 Comment < ActiveRecord::Base; end
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.2
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-21 00:00:00.000000000 Z
12
+ date: 2014-06-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: treetop