Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 308 lines (191 sloc) 15.107 kb
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
1 = Searchlogic
2
26bc920 Ben Johnson Readme changes
binarylogic authored
3 Searchlogic makes using ActiveRecord named scopes easier and less repetitive. It helps keep your code DRY, clean, and simple.
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
4
8752f2b Ben Johnson Update readme and start using github to host gems
binarylogic authored
5 == Helpful links
6
7 * <b>Documentation:</b> http://rdoc.info/projects/binarylogic/searchlogic
8 * <b>Repository:</b> http://github.com/binarylogic/searchlogic/tree/master
ad3b20d Ben Johnson Fix url typo
binarylogic authored
9 * <b>Issues:</b> http://github.com/binarylogic/searchlogic/issues
8752f2b Ben Johnson Update readme and start using github to host gems
binarylogic authored
10 * <b>Google group:</b> http://groups.google.com/group/searchlogic
ce3fa7e Ben Johnson Add railscast link
binarylogic authored
11 * <b>Railscast:</b> http://railscasts.com/episodes/176-searchlogic
8752f2b Ben Johnson Update readme and start using github to host gems
binarylogic authored
12
13 <b>Before contacting me directly, please read:</b>
51d707c Ben Johnson Update README
binarylogic authored
14
c8547d5 Ben Johnson Update readme, switched from lighthouse to github
binarylogic authored
15 If you find a bug or a problem please post it in the issues section. If you need help with something, please use google groups. I check both regularly and get emails when anything happens, so that is the best place to get help. This also benefits other people in the future with the same questions / problems. Thank you.
51d707c Ben Johnson Update README
binarylogic authored
16
8752f2b Ben Johnson Update readme and start using github to host gems
binarylogic authored
17 == Install & use
51d707c Ben Johnson Update README
binarylogic authored
18
a77b72d Ben Johnson Added inner_join convenience method.
binarylogic authored
19 Install the gem from rubyforge:
20
21 sudo gem install searchlogic
22
b662e3d Ben Johnson Add hook method conditions_array to change the order in which conditions...
binarylogic authored
23 Now just set it as a dependency in your project and you are ready to go.
8752f2b Ben Johnson Update readme and start using github to host gems
binarylogic authored
24
a77b72d Ben Johnson Added inner_join convenience method.
binarylogic authored
25 You can also install this as a plugin:
51d707c Ben Johnson Update README
binarylogic authored
26
a77b72d Ben Johnson Added inner_join convenience method.
binarylogic authored
27 script/plugin install git://github.com/binarylogic/searchlogic.git
28
29 See below for usage examples.
51d707c Ben Johnson Update README
binarylogic authored
30
7db0934 Ben Johnson Some readme cleanup
binarylogic authored
31 == Search using conditions on columns
32
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
33 Instead of explaining what Searchlogic can do, let me show you. Let's start at the top:
34
35 # We have the following model
36 User(id: integer, created_at: datetime, username: string, age: integer)
37
38 # Searchlogic gives you a bunch of named scopes for free:
39 User.username_equals("bjohnson")
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
40 User.username_equals(["bjohnson", "thunt"])
41 User.username_equals("a".."b")
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
42 User.username_does_not_equal("bjohnson")
43 User.username_begins_with("bjohnson")
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
44 User.username_not_begin_with("bjohnson")
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
45 User.username_like("bjohnson")
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
46 User.username_not_like("bjohnson")
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
47 User.username_ends_with("bjohnson")
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
48 User.username_not_end_with("bjohnson")
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
49 User.age_greater_than(20)
50 User.age_greater_than_or_equal_to(20)
51 User.age_less_than(20)
52 User.age_less_than_or_equal_to(20)
53 User.username_null
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
54 User.username_not_null
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
55 User.username_blank
56
9744f24 Ben Johnson Added any or all named scopes. See README.
binarylogic authored
57 Any named scope Searchlogic creates is dynamic and created via method_missing. Meaning it will only create what you need. Also, keep in mind, these are just named scopes, you can chain them, call methods off of them, etc:
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
58
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
59 scope = User.username_like("bjohnson").age_greater_than(20).id_less_than(55)
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
60 scope.all
61 scope.first
62 scope.count
63 # etc...
64
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
65 For a complete list of conditions please see the constants in Searchlogic::NamedScopes::Conditions.
66
67 == Use condition aliases
68
69 Typing out 'greater_than_or_equal_to' is not fun. Instead Searchlogic provides various aliases for the conditions. For a complete list please see Searchlogic::NamedScopes::Conditions. But they are pretty straightforward:
70
26bc920 Ben Johnson Readme changes
binarylogic authored
71 User.username_is(10) # equals
72 User.username_eq(10) # equals
73 User.id_lt(10) # less than
74 User.id_lte(10) # less than or equal to
75 User.id_gt(10) # greater than
76 User.id_gte(10) # greater than or equal to
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
77 # etc...
78
79 == Search using scopes in associated classes
0e5912a Ben Johnson Ignore blank values when settings conditions via a Hash on the SearchPro...
binarylogic authored
80
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
81 This is my favorite part of Searchlogic. You can dynamically call scopes on associated classes and Searchlogic will take care of creating the necessary joins for you. This is REALY nice for keeping your code DRY. The best way to explain this is to show you:
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
82
1ed8434 Ben Johnson Readme updates
binarylogic authored
83 === Searchlogic provided scopes
84
26bc920 Ben Johnson Readme changes
binarylogic authored
85 Let's take some basic scopes that Searchlogic provides for every model:
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
86
87 # We have the following relationships
88 User.has_many :orders
89 Order.has_many :line_items
90 LineItem
91
92 # Set conditions on association columns
93 User.orders_total_greater_than(20)
94 User.orders_line_items_price_greater_than(20)
95
96 # Order by association columns
97 User.ascend_by_order_total
98 User.descend_by_orders_line_items_price
99
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
100 This is recursive, you can travel through your associations simply by typing it in the name of the method. Again these are just named scopes. You can chain them together, call methods off of them, etc.
101
1ed8434 Ben Johnson Readme updates
binarylogic authored
102 === Custom associated scopes
103
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
104 Also, these conditions aren't limited to the scopes Searchlogic provides. You can use your own scopes. Like this:
105
106 LineItem.named_scope :expensive, :conditions => "line_items.price > 500"
107
1ed8434 Ben Johnson Readme updates
binarylogic authored
108 User.orders_line_items_expensive
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
109
110 As I stated above, Searchlogic will take care of creating the necessary joins for you. This is REALLY nice when trying to keep your code DRY, because if you wanted to use a scope like this in your User model you would have to copy over the conditions. Now you have 2 named scopes that are essentially doing the same thing. Why do that when you can dynamically access that scope using this feature?
111
2ee4174 Ben Johnson New favorite feature: support for traversing through polymorphic associa...
binarylogic authored
112 === Polymorphic associations
113
114 Polymorphic associations are tough because ActiveRecord doesn't support them with the :joins or :include options. Searchlogic checks for a specific syntax and takes care of this for you. Ex:
115
116 Audit.belongs_to :auditable, :polymorphic => true
117 User.has_many :audits, :as => :auditable
118
119 Audit.auditable_user_type_username_equals("ben")
120
121 The above will take care of creating the inner join on the polymorphic association so that it only looks for type 'User'. On the surface it works the same as a non polymorphic association. The syntax difference being that you need to call the association and then specify the type:
122
123 [polymorphic association name]_[association type]_type
124
1ed8434 Ben Johnson Readme updates
binarylogic authored
125 === Uses :joins not :include
126
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
127 Another thing to note is that the joins created by Searchlogic do NOT use the :include option, making them <em>much</em> faster. Instead they leverage the :joins option, which is great for performance. To prove my point here is a quick benchmark from an application I am working on:
0e5912a Ben Johnson Ignore blank values when settings conditions via a Hash on the SearchPro...
binarylogic authored
128
129 Benchmark.bm do |x|
130 x.report { 10.times { Event.tickets_id_gt(10).all(:include => :tickets) } }
85fdcc6 Ben Johnson Clean up how the SearchProxy calls scopes with no arity
binarylogic authored
131 x.report { 10.times { Event.tickets_id_gt(10).all } }
0e5912a Ben Johnson Ignore blank values when settings conditions via a Hash on the SearchPro...
binarylogic authored
132 end
133 user system total real
134 10.120000 0.170000 10.290000 ( 12.625521)
135 2.630000 0.050000 2.680000 ( 3.313754)
136
137 If you want to use the :include option, just specify it:
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
138
139 User.orders_line_items_price_greater_than(20).all(:include => {:orders => :line_items})
140
74d5631 Ben Johnson * Refactored association code to be much simpler and rely on recursion. ...
binarylogic authored
141 Obviously, only do this if you want to actually use the included objects. Including objects into a query can be helpful with performance, especially when solving an N+1 query problem.
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
142
24bc371 Ben Johnson Added feature for combining conditions with OR and as well as some serio...
binarylogic authored
143 == Order your search
144
145 Just like the various conditions, Searchlogic gives you some very basic scopes for ordering your data:
146
147 User.ascend_by_id
148 User.descend_by_id
149 User.ascend_by_orders_line_items_price
150 # etc...
151
152 == Use any or all
153
154 Every condition you've seen in this readme also has 2 related conditions that you can use. Example:
155
156 User.username_like_any("bjohnson", "thunt") # will return any users that have either of the strings in their username
157 User.username_like_all("bjohnson", "thunt") # will return any users that have all of the strings in their username
158 User.username_like_any(["bjohnson", "thunt"]) # also accepts an array
159
160 This is great for checkbox filters, etc. Where you can pass an array right from your form to this condition.
161
162 == Combine scopes with 'OR'
163
164 In the same fashion that Searchlogic provides a tool for accessing scopes in associated classes, it also provides a tool for combining scopes with 'OR'. As we all know, when scopes are combined they are joined with 'AND', but sometimes you need to combine scopes with 'OR'. Searchlogic solves this problem:
165
166 User.username_or_first_name_like("ben")
167 => "username LIKE '%ben%' OR first_name like'%ben%'"
168
169 User.id_or_age_lt_or_username_or_first_name_begins_with(10)
170 => "id < 10 OR age < 10 OR username LIKE 'ben%' OR first_name like'ben%'"
171
172 Notice you don't have to specify the explicit condition (like, gt, lt, begins with, etc.). You just need to eventually specify it. If you specify a column it will just use the next condition specified. So instead of:
173
174 User.username_like_or_first_name_like("ben")
175
176 You can do:
177
178 User.username_or_first_name_like("ben")
179
180 Again, these just map to named scopes. Use Searchlogic's dynamic scopes, use scopes on associations, use your own custom scopes. As long as it maps to a named scope it will join the conditions with 'OR'. There are no limitations.
181
1280867 Ben Johnson * Add in scope_procedure as an alias for alias_scope.
binarylogic authored
182 == Create scope procedures
183
184 Sometimes you notice a pattern in your application where you are constantly combining certain named scopes. You want to keep the flexibility of being able to mix and match small named scopes, while at the same time being able to call a single scope for a common task. User searchlogic's scpe procedure:
185
186 User.scope_procedure :awesome, lambda { first_name_begins_with("ben").last_name_begins_with("johnson").website_equals("binarylogic.com") }
187
188 All that this is doing is creating a class level method, but what is nice about this method is that is more inline with your other named scopes. It also tells searchlogic that this method is 'safe' to use when using the search method. Ex:
189
190 User.search(:awesome => true)
191
192 Otherwise searchlogic will ignore the 'awesome' condition because there is no way to tell that its a valid scope. This is a security measure to keep users from passing in a scope with a named like 'destroy_all'.
193
7776b27 Ben Johnson Some readme cleanup
binarylogic authored
194 == Make searching and ordering data in your application trivial
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
195
8563a4d Ben Johnson * Split out left outer join creation into its own method, allowing you t...
binarylogic authored
196 The above is great, but what about tying all of this in with a search form in your application? What would be really nice is if we could use an object that represented a single search. Like this...
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
197
8563a4d Ben Johnson * Split out left outer join creation into its own method, allowing you t...
binarylogic authored
198 search = User.search(:username_like => "bjohnson", :age_less_than => 20)
199 search.all
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
200
201 The above is equivalent to:
202
8563a4d Ben Johnson * Split out left outer join creation into its own method, allowing you t...
binarylogic authored
203 User.username_like("bjohnson").age_less_than(20).all
204
205 You can set, read, and chain conditions off of your search too:
206
207 search.username_like => "bjohnson"
208 search.age_gt = 2 => 2
209 search.id_gt(10).email_begins_with("bjohnson") => <#Searchlogic::Search...>
210 search.all => An array of users
211 search.count => integer
212 # .. etc
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
213
a77b72d Ben Johnson Added inner_join convenience method.
binarylogic authored
214 So let's start with the controller...
215
216 === Your controller
217
218 The search class just chains named scopes together for you. What's so great about that? It keeps your controllers extremely simple:
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
219
220 class UsersController < ApplicationController
221 def index
222 @search = User.search(params[:search])
223 @users = @search.all
224 end
225 end
226
a77b72d Ben Johnson Added inner_join convenience method.
binarylogic authored
227 It doesn't get any simpler than that.
228
229 === Your form
230
231 Adding a search condition is as simple as adding a condition to your form. Remember all of those named scopes above? Just create fields with the same names:
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
232
233 - form_for @search do |f|
234 = f.text_field :username_like
235 = f.select :age_greater_than, (0..100)
236 = f.text_field :orders_total_greater_than
237 = f.submit
238
a77b72d Ben Johnson Added inner_join convenience method.
binarylogic authored
239 When a Searchlogic::Search object is passed to form_for it will add a hidden field for the "order" condition, to preserve the order of the data.
240
241 === Additional helpers
242
243 There really isn't a big need for helpers in searchlogic, other than helping you order data. If you want to order your search with a link, just specify the name of the column. Ex:
0f4b5d6 Ben Johnson Add in some basic helpers
binarylogic authored
244
245 = order @search, :by => :age
a77b72d Ben Johnson Added inner_join convenience method.
binarylogic authored
246 = order @search, :by => :created_at, :as => "Created date"
247
248 The first one will create a link that alternates between calling "ascend_by_age" and "descend_by_age". If you wanted to order your data by more than just a column, create your own named scopes: "ascend_by_*" and "descend_by_*". The "order" helper is a very straight forward helper, checkout the docs for some of the options.
0f4b5d6 Ben Johnson Add in some basic helpers
binarylogic authored
249
a77b72d Ben Johnson Added inner_join convenience method.
binarylogic authored
250 <b>This helper is just a convenience method. It's extremely simple and there is nothing wrong with creating your own. If it doesn't do what you want, copy the code, modify it, and create your own. You could even fork the project, modify it there, and use your own gem.</b>
0f4b5d6 Ben Johnson Add in some basic helpers
binarylogic authored
251
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
252 == Use your existing named scopes
253
254 This is one of the big differences between Searchlogic v1 and v2. What about your existing named scopes? Let's say you have this:
255
256 User.named_scope :four_year_olds, :conditions => {:age => 4}
257
258 Again, these are all just named scopes, use it in the same way:
259
260 User.search(:four_year_olds => true, :username_like => "bjohnson")
261
262 Notice we pass true as the value. If a named scope does not accept any parameters (arity == 0) you can simply pass it true or false. If you pass false, the named scope will be ignored. If your named scope accepts a parameter, the value will be passed right to the named scope regardless of the value.
263
264 Now just throw it in your form:
265
266 - form_for @search do |f|
267 = f.text_field :username_like
268 = f.check_box :four_year_olds
269 = f.submit
270
74d5631 Ben Johnson * Refactored association code to be much simpler and rely on recursion. ...
binarylogic authored
271 This really allows Searchlogic to extend beyond what it provides internally. If Searchlogic doesn't provide a named scope for that crazy edge case that you need, just create your own named scope and use it. The sky is the limit.
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
272
273 == Pagination (leverage will_paginate)
274
275 Instead of recreating the wheel with pagination, Searchlogic works great with will_paginate. All that Searchlogic is doing is creating named scopes, and will_paginate works great with named scopes:
276
277 User.username_like("bjohnson").age_less_than(20).paginate(:page => params[:page])
7db0934 Ben Johnson Some readme cleanup
binarylogic authored
278 User.search(:username_like => "bjohnson", :age_less_than => 20).paginate(:page => params[:page])
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
279
280 If you don't like will_paginate, use another solution, or roll your own. Pagination really has nothing to do with searching, and the main goal for Searchlogic v2 was to keep it lean and simple. No reason to recreate the wheel and bloat the library.
281
a60fc65 Ben Johnson * Added a no conflic resolution for other libraries already using the "s...
binarylogic authored
282 == Conflicts with other gems
283
74d5631 Ben Johnson * Refactored association code to be much simpler and rely on recursion. ...
binarylogic authored
284 You will notice searchlogic wants to create a method called "search". So do other libraries like thinking-sphinx, etc. So searchlogic has a no conflict resolution. If the "search" method is already taken the method will be called "searchlogic" instead. So instead of
a60fc65 Ben Johnson * Added a no conflic resolution for other libraries already using the "s...
binarylogic authored
285
286 User.search
287
288 You would do:
289
290 User.searchlogic
291
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
292 == Under the hood
293
294 Before I use a library in my application I like to glance at the source and try to at least understand the basics of how it works. If you are like me, a nice little explanation from the author is always helpful:
295
296 Searchlogic utilizes method_missing to create all of these named scopes. When it hits method_missing it creates a named scope to ensure it will never hit method missing for that named scope again. Sort of a caching mechanism. It works in the same fashion as ActiveRecord's "find_by_*" methods. This way only the named scopes you need are created and nothing more.
297
1280867 Ben Johnson * Add in scope_procedure as an alias for alias_scope.
binarylogic authored
298 The search object is just a proxy to your model that only delegates calls that map to named scopes and nothing more. This is obviously done for security reasons. It also helps make form integration easier, by type casting values, and playing nice with form_for. This class is pretty simple as well.
299
764f859 Ben Johnson Use inner joins for conditions instead of left outer
binarylogic authored
300 That's about it, the named scope options are pretty bare bones and created just like you would manually.
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
301
d8f61c4 Ben Johnson Update readme
binarylogic authored
302 == Credit
303
7db0934 Ben Johnson Some readme cleanup
binarylogic authored
304 Thanks a lot to {Tyler Hunt}[http://github.com/tylerhunt] for helping plan, design, and start the project. He was a big help.
d8f61c4 Ben Johnson Update readme
binarylogic authored
305
a9cbc43 Ben Johnson Add v2 files
binarylogic authored
306 == Copyright
307
308 Copyright (c) 2009 {Ben Johnson of Binary Logic}[http://www.binarylogic.com], released under the MIT license
Something went wrong with that request. Please try again.