Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 340 lines (234 sloc) 13.796 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
6449698 @binarylogic Released verversion 0.9.7
authored
7 == Under the hood
1271b7a @binarylogic Added gemspec
authored
8
6449698 @binarylogic Released verversion 0.9.7
authored
9 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. The search object "sanitizes" down into the options passed into ActiveRecord::Base.find(). It serves as a transparent filter between you and ActiveRecord::Base.find(). This filter provides "enhancements" that get translated into options that ActiveRecord::Base.find() can understand. 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 lets ActiveRecord do all of the work. Between that and the extensive tests, this is a solid and fast plugin.
cf23760 @binarylogic Added tests, new features, and improved readme
authored
10
6449698 @binarylogic Released verversion 0.9.7
authored
11 == Quicklinks
c4a3956 @binarylogic Updated helpers, cleaned up code, started documentation
authored
12
85bfebf @binarylogic Updated readme, Github has problems parsing rdoc
authored
13 * <b>Documentation:</b> http://searchgasm.rubyforge.org
14 * <b>Easy pagination, ordering, and searching tutorial:</b> http://www.binarylogic.com/2008/9/7/tutorial-pagination-ordering-and-searching-with-searchgasm
15 * <b>The tutorial above, live:</b> http://searchgasm_example.binarylogic.com
6449698 @binarylogic Released verversion 0.9.7
authored
16
17 == Install and use
18
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
19 sudo gem install searchgasm
6449698 @binarylogic Released verversion 0.9.7
authored
20
3b7f5ae @binarylogic Updated installation guide in readme
authored
21 For rails
6449698 @binarylogic Released verversion 0.9.7
authored
22
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
23 $ cd vendor/plugins
24 $ sudo gem unpack searchgasm
6449698 @binarylogic Released verversion 0.9.7
authored
25
26 Or as a plugin
27
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
28 script/plugin install git://github.com/binarylogic/searchgasm.git
6449698 @binarylogic Released verversion 0.9.7
authored
29
30 Now try out some of the examples below:
31
32 <b>For all examples, let's assume the following relationships: User => Orders => Line Items</b>
33
34 == The beauty of searchgasm
35
36 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 let Searchgasm handle it. Check it out:
24a7d88 @binarylogic Updated readme
authored
37
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
38 # app/controllers/users_controller.rb
39 def index
40 @search = User.new_search(params[:search])
41 @users, @users_count = @search.all, @search.count
42 end
24a7d88 @binarylogic Updated readme
authored
43
fc301c4 @binarylogic Updated readme
authored
44 Now your view. Things to note in this view:
45
46 1. Passing a search object right into form\_for and fields\_for
47 2. The built in conditions for each column and how you can traverse the relationships and set conditions on them
48 3. The order_by helper
49 4. The page and per_page helpers
50 5. All of your search logic is in 1 spot: your view. Nice and DRY.
f66721b @binarylogic Github needs to fix the escaping bug for readme
authored
51
28444ca @binarylogic Updated readme
authored
52 Your view:
dc39ad7 @binarylogic Updated readme
authored
53
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
54 # app/views/users/index.html.haml
55 - form_for @search do |f|
56 - f.fields_for @search.conditions do |users|
57 = users.text_field :first_name_contains
58 = users.calendar_date_select :created_after # nice rails plugin for replacing date_select
59 - users.fields_for users.object.orders do |orders|
60 = orders.select :total_gt, (1..100)
61 = f.submit "Search"
62
63 %table
64 %tr
65 %th= order_by :first_name
66 %th= order_by :last_name
67 %th= order_by :email
68 - @users.each do |user|
69 %tr
70 %td= user.first_name
71 %td= user.last_name
72 %td= user.email
73
74 Per page:
75 = per_page
76 Page:
77 = pages
24a7d88 @binarylogic Updated readme
authored
78
a1e2d41 @binarylogic Fixed some probs in readme
authored
79 <b>See this example live: http://searchgasm_example.binarylogic.com</b>
cf23760 @binarylogic Added tests, new features, and improved readme
authored
80
6578ffd @binarylogic Updated readme
authored
81 You're probably saying, this is great, but I want to do all of this via AJAX. No problem. Check out the {live tutorial}(http://searchgasm_example.binarylogic.com/orders) based on this example, there is a link for an {AJAX example}(http://searchgasm_example.binarylogic.com/orders).
82
83 This is really just the tip of the iceberg. See below for more examples or {check out the documentation}[http://searchgasm.rubyforge.org] for options and explanations.
84
6449698 @binarylogic Released verversion 0.9.7
authored
85 == Simple Searching Example
7db3b5b @binarylogic Updated readme
authored
86
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
87 User.all(
88 :conditions => {
89 :first_name_contains => "Ben", # first_name like '%Ben%'
90 :email_ends_with => "binarylogic.com" # email like '%binarylogic.com'
91 },
92 :per_page => 20 # limit 20
93 :page => 3 # offset 40, which starts us on page 3
94 )
1271b7a @binarylogic Added gemspec
authored
95
6449698 @binarylogic Released verversion 0.9.7
authored
96 Instead of using the "all" method you could use any search method: first, find(:all), find(:first), count, sum, average, etc, just like ActiveRecord
4065f0d @binarylogic Updated readme
authored
97
6449698 @binarylogic Released verversion 0.9.7
authored
98 == Exhaustive Example w/ Object Based Searching (great for form_for or fields_for)
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
99
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
100 # Instantiate
101 @search = User.new_search(
102 :conditions => {
103 :first_name_contains => "Ben",
104 :age_gt => 18,
105 :orders => {:total_lt => 100}
106 },
107 :per_page => 20,
108 :page => 2,
109 :order_by => {:orders => :total},
110 :order_as => "DESC"
111 )
112
113 # Set local conditions
114 @search.conditions.email_ends_with = "binarylogic.com"
115
116 # Set conditions on relationships
117 @search.conditions.oders.line_items.created_after = Time.now # can traverse through all relationships
118
119 # Set options
120 @search.per_page = 50 # overrides the 20 set above
121 @search.order_by = [:first_name, {:user_group => :name}] # order by first name and then by the user group's name it belongs to
122 @search.order_as = "ASC"
123
124 # Set ANY of the ActiveRecord options
125 @search.group = "last_name"
126 @search.readonly = true
127 # ... see ActiveRecord documentation
128
129 # Return results just like ActiveRecord
130 @search.all
131 @search.first
dcf6ebf @binarylogic Updated readme
authored
132
c703fbe @binarylogic Updated readme
authored
133 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
134
6449698 @binarylogic Released verversion 0.9.7
authored
135 == Calculations
cf23760 @binarylogic Added tests, new features, and improved readme
authored
136
137 Using the object from above:
138
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
139 @search.average('id')
140 @search.count
141 @search.maximum('id')
142 @search.minimum('id')
143 @search.sum('id')
144 @search.calculate(:sum, 'id')
145 # ...any of the above calculations, see ActiveRecord documentation on calculations
1f7251c @binarylogic Added association_collection
authored
146
cf23760 @binarylogic Added tests, new features, and improved readme
authored
147 Or do it from your model:
148
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
149 User.count(:conditions => {:first_name_contains => "Ben"})
150 User.sum('id', :conditions => {:first_name_contains => "Ben"})
151 # ... all other calcualtions, etc.
cf23760 @binarylogic Added tests, new features, and improved readme
authored
152
6449698 @binarylogic Released verversion 0.9.7
authored
153 == Different ways to search, take your pick
7db3b5b @binarylogic Updated readme
authored
154
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
155 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
156
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
157 User.all(:conditions => {:age_gt => 18}, :per_page => 20)
7db3b5b @binarylogic Updated readme
authored
158
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
159 User.first(:conditions => {:age_gt => 18}, :per_page => 20)
7db3b5b @binarylogic Updated readme
authored
160
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
161 User.find(:all, :conditions => {::age_gt => 18}, :per_page => 20)
7db3b5b @binarylogic Updated readme
authored
162
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
163 User.find(:first, :conditions => {::age_gt => 18}, :per_page => 20)
7db3b5b @binarylogic Updated readme
authored
164
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
165 search = User.new_search(:conditions => {:age_gt => 18}) # build_search is an alias
166 search.conditions.first_name_contains = "Ben"
167 search.per_page = 20
168 search.all
7db3b5b @binarylogic Updated readme
authored
169
6449698 @binarylogic Released verversion 0.9.7
authored
170 If you want to use Searchgasm directly:
7db3b5b @binarylogic Updated readme
authored
171
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
172 search = Searchgasm::Search::Base.new(User, :conditions => {:age_gt => 18})
173 search.conditions.first_name_contains = "Ben"
174 search.per_page = 20
175 search.all
7db3b5b @binarylogic Updated readme
authored
176
6449698 @binarylogic Released verversion 0.9.7
authored
177 == Search with conditions only
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
178
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
179 conditions = User.new_conditions(:age_gt => 18)
180 conditions.first_name_contains = "Ben"
181 conditions.all
182 # ... all operations above are available
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
183
184 Pass a conditions object right into ActiveRecord:
185
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
186 User.all(:conditions => conditions)
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
187
6449698 @binarylogic Released verversion 0.9.7
authored
188 Again, if you want to use Searchgasm directly:
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
189
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
190 conditions = Searchgasm::Conditions::Base.new(User, :age_gt => 18)
191 conditions.first_name_contains = "Ben"
192 conditions.all
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
193
28cd46c @binarylogic Updated readme
authored
194 Now pass the conditions object right into form\_for or fields\_for (see above for example).
195
6449698 @binarylogic Released verversion 0.9.7
authored
196 == Scoped searching
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
197
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
198 @current_user.orders.find(:all, :conditions => {:total_lte => 500})
199 @current_user.orders.count(:conditions => {:total_lte => 500})
200 @current_user.orders.sum('total', :conditions => {:total_lte => 500})
201
202 search = @current_user.orders.build_search('total', :conditions => {:total_lte => 500})
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
203
6449698 @binarylogic Released verversion 0.9.7
authored
204 == Searching trees
cf23760 @binarylogic Added tests, new features, and improved readme
authored
205
206 For tree data structures you get a few nifty methods. Let's assume Users is a tree data structure.
207
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
208 # Child of
209 User.all(:conditions => {:child_of => User.roots.first})
210 User.all(:conditions => {:child_of => User.roots.first.id})
211
212 # Sibling of
213 User.all(:conditions => {:sibling_of => User.roots.first})
214 User.all(:conditions => {:sibling_of => User.roots.first.id})
215
216 # Descendant of (includes all recursive children: children, grand children, great grand children, etc)
217 User.all(:conditions => {:descendant_of => User.roots.first})
218 User.all(:conditions => {:descendant_of => User.roots.first.id})
219
220 # Inclusive descendant_of. Same as above but includes the root
221 User.all(:conditions => {:inclusive_descendant_of => User.roots.first})
222 User.all(:conditions => {:inclusive_descendant_of => User.roots.first.id})
223
cf23760 @binarylogic Added tests, new features, and improved readme
authored
224
6449698 @binarylogic Released verversion 0.9.7
authored
225 == Available anywhere (relationships & scopes)
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
226
6449698 @binarylogic Released verversion 0.9.7
authored
227 Not only can you use searchgasm when searching, but you can use it when setting up relationships or scopes. Anywhere you specify conditions in ActiveRecord.
7db3b5b @binarylogic Updated readme
authored
228
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
229 class User < ActiveRecord::Base
230 has_many :expensive_pending_orders, :conditions => {:total_greater_than => 1_000_000, :state => :pending}, :per_page => 20
231 named_scope :sexy, :conditions => {:first_name => "Ben", email_ends_with => "binarylogic.com"}, :per_page => 20
232 end
590f51e @binarylogic Updated readme
authored
233
6449698 @binarylogic Released verversion 0.9.7
authored
234 == Always use protection...against SQL injections
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
235
6449698 @binarylogic Released verversion 0.9.7
authored
236 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 are protected by default. This means that various checks are done to ensure it is not possible to perform any type of SQL injection. 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
237
6449698 @binarylogic Released verversion 0.9.7
authored
238 === Protected from SQL injections
9968056 @binarylogic Updated readme
authored
239
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
240 search = Account.new_search(params[:search])
241 conditions = Account.new_conditions(params[:conditions])
ae22512 @binarylogic Updated readme
authored
242
6449698 @binarylogic Released verversion 0.9.7
authored
243 === *NOT* protected from SQL injections
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
244
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
245 accounts = Account.find(params[:search])
246 accounts = Account.all(params[:search])
247 account = Account.first(params[:search])
248 search = Account.new_search!(params[:search])
249 conditions = Account.new_conditions!(params[:conditions])
33f192e @binarylogic Ehanced protection
authored
250
251 Lesson learned: use new\_search and new\_conditions when passing in params as *ANY* of the options.
aa5b90a @binarylogic First commit
authored
252
6449698 @binarylogic Released verversion 0.9.7
authored
253 == Available Conditions
ae22512 @binarylogic Updated readme
authored
254
1271b7a @binarylogic Added gemspec
authored
255 Depending on the type, each column comes preloaded with a bunch of nifty conditions:
ae22512 @binarylogic Updated readme
authored
256
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
257 all columns
258 => :equals, :does_not_equal
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
259
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
260 :string, :text
261 => :begins_with, :contains, :keywords, :ends_with
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
262
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
263 :integer, :float, :decimal,:datetime, :timestamp, :time, :date
264 => :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to
ae22512 @binarylogic Updated readme
authored
265
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
266 tree data structures (see above "searching trees")
267 => :child_of, :sibling_of, :descendant_of, :inclusive_descendant_of
cf23760 @binarylogic Added tests, new features, and improved readme
authored
268
7db3b5b @binarylogic Updated readme
authored
269 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
270
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
271 :equals => :is
272 :does_not_equal => :is_not, :not
273 :begins_with => :starts_with, :bw, :start
274 :contains => :like, :has
275 :ends_with => :ew, :ends, :end
276 :greater_than => :gt, :after
277 :greater_than_or_equal_to => :at_least, :gte
278 :keywords => :kwords, :kw
279 :less_than => :lt, :before
280 :less_than_or_equal_to => :at_most, :lte
4fb5929 @binarylogic Added new features, cleaned up readme
authored
281
6449698 @binarylogic Released verversion 0.9.7
authored
282 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.
283
284 === Enhanced searching and blacklisted words
bec7f7d @binarylogic Reffeactored and changed everything, much nicer
authored
285
286 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.
287
6449698 @binarylogic Released verversion 0.9.7
authored
288 === Roll your own conditions
e8274f3 @binarylogic Added example in readme for adding your own conditions
authored
289
290 I didn't include this function because its MySQL specific, and it's probably rarely used, but MySQL supports a "SOUNDS LIKE" function.
291
292 I want to use it, so let's add it:
293
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
294 # config/initializers/searchgasm.rb
295 # Actual function for MySQL databases only
296 class SoundsLike < Searchgasm::Condition::Base
297 class << self
298 # I pass you the column, you tell me what you want the method to be called.
299 # If you don't want to add this condition for that column, return nil
300 # It defaults to "#{column.name}_sounds_like". So if thats what you want you don't even need to do this.
301 def name_for_column(column)
302 super
e8274f3 @binarylogic Added example in readme for adding your own conditions
authored
303 end
304
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
305 # Only do this if you want aliases for your condition
306 def aliases_for_column(column)
307 ["#{column.name}_sounds", "#{column.name}_similar_to"]
308 end
309 end
310
311 # You can return an array or a string. NOT a hash, because all of these conditions
312 # need to eventually get merged together. The array or string can be anything you would put in
313 # the :conditions option for ActiveRecord::Base.find()
314 def to_conditions(value)
315 ["#{quoted_table_name}.#{quoted_column_name} SOUNDS LIKE ?", value]
316 end
317 end
318
319 Searchgasm::Conditions::Base.register_condition(SoundsLike)
e8274f3 @binarylogic Added example in readme for adding your own conditions
authored
320
321 Now test it out:
322
1d815aa @binarylogic Fixed bug when setting per_page to nil
authored
323 search = User.new_search
324 search.conditions.first_name_sounds_like = "Ben"
325 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
326
6449698 @binarylogic Released verversion 0.9.7
authored
327 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
328
329 == Reporting problems / bugs
330
331 http://binarylogic.lighthouseapp.com/projects/16601-searchgasm
e8274f3 @binarylogic Added example in readme for adding your own conditions
authored
332
6449698 @binarylogic Released verversion 0.9.7
authored
333 == Credits
4fb5929 @binarylogic Added new features, cleaned up readme
authored
334
1320254 @binarylogic Fixed some probs in readme
authored
335 Author: {Ben Johnson}[http://github.com/binarylogic] of {Binary Logic}[http://www.binarylogic.com]
9b5304b @binarylogic Updated readme
authored
336
1320254 @binarylogic Fixed some probs in readme
authored
337 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
338
339
1320254 @binarylogic Fixed some probs in readme
authored
340 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.