Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 357 lines (250 sloc) 14.18 kb
6449698 @binarylogic Released verversion 0.9.7
authored
1 = Searchgasm
aa5b90a @binarylogic First commit
authored
2
9968056 @binarylogic Updated readme
authored
3 Searchgasm is orgasmic. Maybe not orgasmic, but you will get aroused. So go grab a towel and let's dive in.
1f7251c @binarylogic Added association_collection
authored
4
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
5 <b>Searchgasm's inspiration comes right from ActiveRecord. ActiveRecord lets you create objects that represent a record in the database, so why can't you create objects that represent searching the database? Now you can! It's searching, ordering, and pagination all in one.</b>
c703fbe @binarylogic Updated readme
authored
6
3966652 @binarylogic Hardened some test, added more documentation
authored
7 == Helpful links
c4a3956 @binarylogic Updated helpers, cleaned up code, started documentation
authored
8
85bfebf @binarylogic Updated readme, Github has problems parsing rdoc
authored
9 * <b>Documentation:</b> http://searchgasm.rubyforge.org
10 * <b>Easy pagination, ordering, and searching tutorial:</b> http://www.binarylogic.com/2008/9/7/tutorial-pagination-ordering-and-searching-with-searchgasm
4c13dd0 @binarylogic Release v1.0.1 (see changelog)
authored
11 * <b>Live example of the tutorial above (with source):</b> http://searchgasm_example.binarylogic.com
6449698 @binarylogic Released verversion 0.9.7
authored
12
13 == Install and use
14
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
15 sudo gem install searchgasm
6449698 @binarylogic Released verversion 0.9.7
authored
16
3b7f5ae @binarylogic Updated installation guide in readme
authored
17 For rails
6449698 @binarylogic Released verversion 0.9.7
authored
18
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
19 $ cd vendor/plugins
20 $ sudo gem unpack searchgasm
6449698 @binarylogic Released verversion 0.9.7
authored
21
22 Or as a plugin
23
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
24 script/plugin install git://github.com/binarylogic/searchgasm.git
6449698 @binarylogic Released verversion 0.9.7
authored
25
26 Now try out some of the examples below:
27
28 <b>For all examples, let's assume the following relationships: User => Orders => Line Items</b>
29
3966652 @binarylogic Hardened some test, added more documentation
authored
30 == Simple Searching Example
31
32 User.all(
33 :conditions => {
34 :first_name_contains => "Ben", # first_name like '%Ben%'
35 :email_ends_with => "binarylogic.com" # email like '%binarylogic.com'
36 },
37 :per_page => 20, # limit 20
38 :page => 3, # offset 40, which starts us on page 3
39 :order_as => "ASC",
40 :order_by => {:user_group => :name} # order user_groups.name ASC
41 )
42
43 same as above, but object based
44
45 search = User.new_search
46 search.conditions.first_name_contains = "Ben"
47 search.conditions.email_ends_with = "binarylogic.com"
48 search.per_page = 20
49 search.page = 3
50 search.order_as = "ASC"
51 search.order_by = {:user_group => :name}
52 search.all
53
54 In both examples, instead of using the "all" method you could use any search method: first, find(:all), find(:first), count, sum, average, etc, just like ActiveRecord.
55
56 == The beauty of searchgasm, integration into rails
6449698 @binarylogic Released verversion 0.9.7
authored
57
07f8a72 Released v1.0.2
Ben Johnson authored
58 Using Searchgasm in rails is the best part, because rails has all kinds of nifty methods to make dealing with ActiveRecord objects quick and easy, especially with forms. So let's take advantage of them! That's the idea behind this plugin. Searchgasm is searching, ordering, and pagination all rolled into one simple plugin. Take all of that pagination and searching cruft out of your models and controllers, and let Searchgasm handle it. Check it out:
24a7d88 @binarylogic Updated readme
authored
59
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
60 # app/controllers/users_controller.rb
61 def index
62 @search = User.new_search(params[:search])
63 @users, @users_count = @search.all, @search.count
64 end
24a7d88 @binarylogic Updated readme
authored
65
07f8a72 Released v1.0.2
Ben Johnson authored
66 Now your view:
dc39ad7 @binarylogic Updated readme
authored
67
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
68 # app/views/users/index.html.haml
69 - form_for @search do |f|
70 - f.fields_for @search.conditions do |users|
71 = users.text_field :first_name_contains
72 = users.calendar_date_select :created_after # nice rails plugin for replacing date_select
73 - users.fields_for users.object.orders do |orders|
74 = orders.select :total_gt, (1..100)
75 = f.submit "Search"
76
4c13dd0 @binarylogic Release v1.0.1 (see changelog)
authored
77 - if @users_count > 0
78 %table
79 %tr
80 %th= order_by_link :account => :name
81 %th= order_by_link :first_name
82 %th= order_by_link :last_name
83 %th= order_by_link :email
84 - @users.each do |user|
6ad1526 @binarylogic Released v1.0
authored
85 %tr
86 %td= user.account? ? user.account.name : "-"
87 %td= user.first_name
88 %td= user.last_name
89 %td= user.email
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
90
4c13dd0 @binarylogic Release v1.0.1 (see changelog)
authored
91 Per page:
92 = per_page_select
93 Page:
94 = page_select
95 - else
96 No users were found.
24a7d88 @binarylogic Updated readme
authored
97
07f8a72 Released v1.0.2
Ben Johnson authored
98 Things to note in this view:
99
100 1. Passing a search object right into form\_for and fields\_for
101 2. The built in conditions for each column and how you can traverse the relationships and set conditions on them
102 3. The order_by_link helper
103 4. The page_select and per_page_select helpers
104 5. All of your search logic is in 1 spot: your view. Nice and DRY.
105
4c13dd0 @binarylogic Release v1.0.1 (see changelog)
authored
106 <b>See my tutorial on this example: http://www.binarylogic.com/2008/9/7/tutorial-pagination-ordering-and-searching-with-searchgasm</b>
4065f0d @binarylogic Updated readme
authored
107
6449698 @binarylogic Released verversion 0.9.7
authored
108 == Exhaustive Example w/ Object Based Searching (great for form_for or fields_for)
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
109
3966652 @binarylogic Hardened some test, added more documentation
authored
110 # Start a new search
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
111 @search = User.new_search(
112 :conditions => {
113 :first_name_contains => "Ben",
114 :age_gt => 18,
115 :orders => {:total_lt => 100}
116 },
117 :per_page => 20,
118 :page => 2,
119 :order_by => {:orders => :total},
120 :order_as => "DESC"
121 )
122
123 # Set local conditions
124 @search.conditions.email_ends_with = "binarylogic.com"
125
126 # Set conditions on relationships
127 @search.conditions.oders.line_items.created_after = Time.now # can traverse through all relationships
128
129 # Set options
130 @search.per_page = 50 # overrides the 20 set above
131 @search.order_by = [:first_name, {:user_group => :name}] # order by first name and then by the user group's name it belongs to
132 @search.order_as = "ASC"
133
134 # Set ANY of the ActiveRecord options
135 @search.group = "last_name"
136 @search.readonly = true
137 # ... see ActiveRecord documentation
138
139 # Return results just like ActiveRecord
140 @search.all
141 @search.first
dcf6ebf @binarylogic Updated readme
authored
142
c703fbe @binarylogic Updated readme
authored
143 Take the @search object and pass it right into form\_for or fields\_for (see above).
cf23760 @binarylogic Added tests, new features, and improved readme
authored
144
6449698 @binarylogic Released verversion 0.9.7
authored
145 == Calculations
cf23760 @binarylogic Added tests, new features, and improved readme
authored
146
147 Using the object from above:
148
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
149 @search.average('id')
2badebd @binarylogic Released v1.0.4 (see changelog)
authored
150 @search.count # ignores limit and offset
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
151 @search.maximum('id')
152 @search.minimum('id')
153 @search.sum('id')
154 @search.calculate(:sum, 'id')
155 # ...any of the above calculations, see ActiveRecord documentation on calculations
1f7251c @binarylogic Added association_collection
authored
156
cf23760 @binarylogic Added tests, new features, and improved readme
authored
157 Or do it from your model:
158
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
159 User.count(:conditions => {:first_name_contains => "Ben"})
160 User.sum('id', :conditions => {:first_name_contains => "Ben"})
161 # ... all other calcualtions, etc.
cf23760 @binarylogic Added tests, new features, and improved readme
authored
162
6449698 @binarylogic Released verversion 0.9.7
authored
163 == Different ways to search, take your pick
7db3b5b @binarylogic Updated readme
authored
164
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
165 Any of the options used in the above example can be used in these, but for the sake of brevity I am only using a few:
7db3b5b @binarylogic Updated readme
authored
166
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
167 User.all(:conditions => {:age_gt => 18}, :per_page => 20)
7db3b5b @binarylogic Updated readme
authored
168
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
169 User.first(:conditions => {:age_gt => 18}, :per_page => 20)
7db3b5b @binarylogic Updated readme
authored
170
2ec93e3 @binarylogic Released v 9.0.9.10. Some small bug fixes
authored
171 User.find(:all, :conditions => {:age_gt => 18}, :per_page => 20)
7db3b5b @binarylogic Updated readme
authored
172
2ec93e3 @binarylogic Released v 9.0.9.10. Some small bug fixes
authored
173 User.find(:first, :conditions => {:age_gt => 18}, :per_page => 20)
7db3b5b @binarylogic Updated readme
authored
174
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
175 search = User.new_search(:conditions => {:age_gt => 18}) # build_search is an alias
176 search.conditions.first_name_contains = "Ben"
177 search.per_page = 20
178 search.all
7db3b5b @binarylogic Updated readme
authored
179
6449698 @binarylogic Released verversion 0.9.7
authored
180 == Search with conditions only
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
181
2ec93e3 @binarylogic Released v 9.0.9.10. Some small bug fixes
authored
182 Don't need pagination, ordering, or any of the other options? Search with conditions only.
183
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
184 conditions = User.new_conditions(:age_gt => 18)
185 conditions.first_name_contains = "Ben"
186 conditions.all
187 # ... all operations above are available
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
188
189 Pass a conditions object right into ActiveRecord:
190
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
191 User.all(:conditions => conditions)
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
192
6449698 @binarylogic Released verversion 0.9.7
authored
193 == Scoped searching
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
194
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
195 @current_user.orders.find(:all, :conditions => {:total_lte => 500})
196 @current_user.orders.count(:conditions => {:total_lte => 500})
197 @current_user.orders.sum('total', :conditions => {:total_lte => 500})
198
4c13dd0 @binarylogic Release v1.0.1 (see changelog)
authored
199 search = @current_user.orders.build_search(:conditions => {:total_lte => 500})
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
200
6449698 @binarylogic Released verversion 0.9.7
authored
201 == Searching trees
cf23760 @binarylogic Added tests, new features, and improved readme
authored
202
203 For tree data structures you get a few nifty methods. Let's assume Users is a tree data structure.
204
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
205 # Child of
206 User.all(:conditions => {:child_of => User.roots.first})
207 User.all(:conditions => {:child_of => User.roots.first.id})
208
209 # Sibling of
210 User.all(:conditions => {:sibling_of => User.roots.first})
211 User.all(:conditions => {:sibling_of => User.roots.first.id})
212
213 # Descendant of (includes all recursive children: children, grand children, great grand children, etc)
214 User.all(:conditions => {:descendant_of => User.roots.first})
215 User.all(:conditions => {:descendant_of => User.roots.first.id})
216
217 # Inclusive descendant_of. Same as above but includes the root
218 User.all(:conditions => {:inclusive_descendant_of => User.roots.first})
219 User.all(:conditions => {:inclusive_descendant_of => User.roots.first.id})
220
cf23760 @binarylogic Added tests, new features, and improved readme
authored
221
c06d174 @binarylogic Released v1.0.3 (see changelog)
authored
222 == Scope support
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
223
c06d174 @binarylogic Released v1.0.3 (see changelog)
authored
224 Not only can you use searchgasm when searching, but you can use it when using scopes.
7db3b5b @binarylogic Updated readme
authored
225
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
226 class User < ActiveRecord::Base
227 named_scope :sexy, :conditions => {:first_name => "Ben", email_ends_with => "binarylogic.com"}, :per_page => 20
228 end
c06d174 @binarylogic Released v1.0.3 (see changelog)
authored
229
230 or
231
232 class User < ActiveRecord::Base
233 def self.find_sexy
234 with_scope(:find => {:conditions => {:first_name => "Ben", email_ends_with => "binarylogic.com"}, :per_page => 20}) do
235 all
236 end
237 end
238 end
590f51e @binarylogic Updated readme
authored
239
6449698 @binarylogic Released verversion 0.9.7
authored
240 == Always use protection...against SQL injections
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
241
07f8a72 Released v1.0.2
Ben Johnson authored
242 If there is one thing we all know, it's to always use protection against SQL injections. That's why searchgasm protects you by default. The new\_search and new\_conditions methods protect mass assignments by default (instantiation and search.options = {}). This means that various checks are done to ensure it is not possible to perform any type of SQL injection during mass assignments. But this also limits how you can search, meaning you can't write raw SQL. If you want to be daring and search without protection, all that you have to do is add ! to the end of the methods: new\_search! and new\_conditions!.
590f51e @binarylogic Updated readme
authored
243
6449698 @binarylogic Released verversion 0.9.7
authored
244 === Protected from SQL injections
9968056 @binarylogic Updated readme
authored
245
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
246 search = Account.new_search(params[:search])
247 conditions = Account.new_conditions(params[:conditions])
ae22512 @binarylogic Updated readme
authored
248
6449698 @binarylogic Released verversion 0.9.7
authored
249 === *NOT* protected from SQL injections
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
250
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
251 accounts = Account.find(params[:search])
252 accounts = Account.all(params[:search])
253 account = Account.first(params[:search])
254 search = Account.new_search!(params[:search])
255 conditions = Account.new_conditions!(params[:conditions])
33f192e @binarylogic Ehanced protection
authored
256
6ad1526 @binarylogic Released v1.0
authored
257 I'm sure you already knew this, but it's tempting to do this when you can pass the params hash right into these methods.
258
33f192e @binarylogic Ehanced protection
authored
259 Lesson learned: use new\_search and new\_conditions when passing in params as *ANY* of the options.
aa5b90a @binarylogic First commit
authored
260
6449698 @binarylogic Released verversion 0.9.7
authored
261 == Available Conditions
ae22512 @binarylogic Updated readme
authored
262
1271b7a @binarylogic Added gemspec
authored
263 Depending on the type, each column comes preloaded with a bunch of nifty conditions:
ae22512 @binarylogic Updated readme
authored
264
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
265 all columns
266 => :equals, :does_not_equal
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
267
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
268 :string, :text
269 => :begins_with, :contains, :keywords, :ends_with
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
270
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
271 :integer, :float, :decimal,:datetime, :timestamp, :time, :date
272 => :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to
ae22512 @binarylogic Updated readme
authored
273
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
274 tree data structures (see above "searching trees")
275 => :child_of, :sibling_of, :descendant_of, :inclusive_descendant_of
cf23760 @binarylogic Added tests, new features, and improved readme
authored
276
7db3b5b @binarylogic Updated readme
authored
277 Some of these conditions come with aliases, so you have your choice how to call the conditions. For example you can use "greater\_than" or "gt":
1f7251c @binarylogic Added association_collection
authored
278
3966652 @binarylogic Hardened some test, added more documentation
authored
279 :equals => :is
280 :does_not_equal => :is_not, :not
281 :begins_with => :starts_with, :sw, :bw, :start
282 :contains => :like, :has
283 :ends_with => :ew, :ends, :end
284 :greater_than => :gt, :after
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
285 :greater_than_or_equal_to => :at_least, :gte
3966652 @binarylogic Hardened some test, added more documentation
authored
286 :keywords => :kwords, :kw
287 :less_than => :lt, :before
288 :less_than_or_equal_to => :at_most, :lte
4fb5929 @binarylogic Added new features, cleaned up readme
authored
289
6449698 @binarylogic Released verversion 0.9.7
authored
290 For more information on each condition see Searchgasm::Condition. Each condition has it's own class and the source is pretty simple and self explanatory.
291
292 === Enhanced searching and blacklisted words
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
293
294 You will notice above there is "contains" and "keywords". The difference is that "keywords" is an enhanced search. It acts like a real keyword search. It finds those keywords, in any order, and blacklists meaningless words such as "and", "the", etc. "contains" finds the EXACT string in the column you are searching, spaces and all.
295
6449698 @binarylogic Released verversion 0.9.7
authored
296 === Roll your own conditions
e8274f3 @binarylogic Added example in readme for adding your own conditions
authored
297
298 I didn't include this function because its MySQL specific, and it's probably rarely used, but MySQL supports a "SOUNDS LIKE" function.
299
300 I want to use it, so let's add it:
301
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
302 # config/initializers/searchgasm.rb
303 # Actual function for MySQL databases only
304 class SoundsLike < Searchgasm::Condition::Base
305 class << self
306 # I pass you the column, you tell me what you want the method to be called.
307 # If you don't want to add this condition for that column, return nil
07f8a72 Released v1.0.2
Ben Johnson authored
308 # It defaults to "#{column.name}_sounds_like" (using the class name). So if thats what you want you don't even need to do this.
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
309 def name_for_column(column)
310 super
e8274f3 @binarylogic Added example in readme for adding your own conditions
authored
311 end
312
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
313 # Only do this if you want aliases for your condition
314 def aliases_for_column(column)
315 ["#{column.name}_sounds", "#{column.name}_similar_to"]
316 end
317 end
318
319 # You can return an array or a string. NOT a hash, because all of these conditions
320 # need to eventually get merged together. The array or string can be anything you would put in
07f8a72 Released v1.0.2
Ben Johnson authored
321 # the :conditions option for ActiveRecord::Base.find(). Also, for a list of methods / variables you can use check out
322 # Searchgasm::Condition::Base.
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
323 def to_conditions(value)
324 ["#{quoted_table_name}.#{quoted_column_name} SOUNDS LIKE ?", value]
325 end
326 end
327
328 Searchgasm::Conditions::Base.register_condition(SoundsLike)
e8274f3 @binarylogic Added example in readme for adding your own conditions
authored
329
330 Now test it out:
331
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
332 search = User.new_search
333 search.conditions.first_name_sounds_like = "Ben"
334 search.all # will return any user that has a first name that sounds like "Ben"
e8274f3 @binarylogic Added example in readme for adding your own conditions
authored
335
6449698 @binarylogic Released verversion 0.9.7
authored
336 Pretty nifty, huh? You can create any condition ultimately creating any SQL you want. The sky is the limit. For more information see Searchgasm::Condition::Base
337
3966652 @binarylogic Hardened some test, added more documentation
authored
338 == Under the hood
339
c06d174 @binarylogic Released v1.0.3 (see changelog)
authored
340 I'm a big fan of understanding what I'm using, so here's a quick explanation: The design behind this plugin is pretty simple and I had 1 main rule when developing this:
341
342 ActiveRecord should never know about Searchgasm
343
344 What that rule means is that any options you pass when searching get "sanitized" down into options ActiveRecord can understand. Searchgasm serves as a transparent filter between you and ActiveRecord. It doesn't dig into the ActiveRecord internals, it only uses what is publicly available. It jumps in and helps out <em>only</em> when needed, otherwise it sits back and stays completely out of the way. Between that and the extensive tests, this is a solid and fast plugin.
3966652 @binarylogic Hardened some test, added more documentation
authored
345
6449698 @binarylogic Released verversion 0.9.7
authored
346 == Reporting problems / bugs
347
348 http://binarylogic.lighthouseapp.com/projects/16601-searchgasm
e8274f3 @binarylogic Added example in readme for adding your own conditions
authored
349
6449698 @binarylogic Released verversion 0.9.7
authored
350 == Credits
4fb5929 @binarylogic Added new features, cleaned up readme
authored
351
1320254 @binarylogic Fixed some probs in readme
authored
352 Author: {Ben Johnson}[http://github.com/binarylogic] of {Binary Logic}[http://www.binarylogic.com]
9b5304b @binarylogic Updated readme
authored
353
1320254 @binarylogic Fixed some probs in readme
authored
354 Credit to {Zack Ham}[http://github.com/zackham] and {Robert Malko}[http://github.com/malkomalko/] for helping with feature suggestions.
aa5b90a @binarylogic First commit
authored
355
356
1320254 @binarylogic Fixed some probs in readme
authored
357 Copyright (c) 2008 {Ben Johnson}[http://github.com/binarylogic] of {Binary Logic}[http://www.binarylogic.com], released under the MIT license
Something went wrong with that request. Please try again.