Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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