datastax_rails 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +62 -0
- data/Rakefile +34 -0
- data/config/schema.xml +266 -0
- data/config/schema.xml.erb +70 -0
- data/config/solrconfig.xml +1564 -0
- data/config/stopwords.txt +58 -0
- data/lib/datastax_rails/associations/association.rb +224 -0
- data/lib/datastax_rails/associations/association_scope.rb +25 -0
- data/lib/datastax_rails/associations/belongs_to_association.rb +64 -0
- data/lib/datastax_rails/associations/builder/association.rb +56 -0
- data/lib/datastax_rails/associations/builder/belongs_to.rb +30 -0
- data/lib/datastax_rails/associations/builder/collection_association.rb +48 -0
- data/lib/datastax_rails/associations/builder/has_and_belongs_to_many.rb +36 -0
- data/lib/datastax_rails/associations/builder/has_many.rb +54 -0
- data/lib/datastax_rails/associations/builder/has_one.rb +52 -0
- data/lib/datastax_rails/associations/builder/singular_association.rb +56 -0
- data/lib/datastax_rails/associations/collection_association.rb +274 -0
- data/lib/datastax_rails/associations/collection_proxy.rb +118 -0
- data/lib/datastax_rails/associations/has_and_belongs_to_many_association.rb +44 -0
- data/lib/datastax_rails/associations/has_many_association.rb +58 -0
- data/lib/datastax_rails/associations/has_one_association.rb +68 -0
- data/lib/datastax_rails/associations/singular_association.rb +58 -0
- data/lib/datastax_rails/associations.rb +86 -0
- data/lib/datastax_rails/attribute_methods/definition.rb +20 -0
- data/lib/datastax_rails/attribute_methods/dirty.rb +43 -0
- data/lib/datastax_rails/attribute_methods/typecasting.rb +50 -0
- data/lib/datastax_rails/attribute_methods.rb +104 -0
- data/lib/datastax_rails/base.rb +587 -0
- data/lib/datastax_rails/batches.rb +35 -0
- data/lib/datastax_rails/callbacks.rb +37 -0
- data/lib/datastax_rails/collection.rb +9 -0
- data/lib/datastax_rails/connection.rb +21 -0
- data/lib/datastax_rails/consistency.rb +33 -0
- data/lib/datastax_rails/cql/base.rb +15 -0
- data/lib/datastax_rails/cql/column_family.rb +38 -0
- data/lib/datastax_rails/cql/consistency.rb +13 -0
- data/lib/datastax_rails/cql/create_column_family.rb +63 -0
- data/lib/datastax_rails/cql/create_keyspace.rb +30 -0
- data/lib/datastax_rails/cql/delete.rb +41 -0
- data/lib/datastax_rails/cql/drop_column_family.rb +13 -0
- data/lib/datastax_rails/cql/drop_keyspace.rb +13 -0
- data/lib/datastax_rails/cql/insert.rb +53 -0
- data/lib/datastax_rails/cql/select.rb +51 -0
- data/lib/datastax_rails/cql/truncate.rb +13 -0
- data/lib/datastax_rails/cql/update.rb +68 -0
- data/lib/datastax_rails/cql/use_keyspace.rb +13 -0
- data/lib/datastax_rails/cql.rb +25 -0
- data/lib/datastax_rails/cursor.rb +90 -0
- data/lib/datastax_rails/errors.rb +16 -0
- data/lib/datastax_rails/identity/abstract_key_factory.rb +26 -0
- data/lib/datastax_rails/identity/custom_key_factory.rb +36 -0
- data/lib/datastax_rails/identity/hashed_natural_key_factory.rb +10 -0
- data/lib/datastax_rails/identity/natural_key_factory.rb +37 -0
- data/lib/datastax_rails/identity/uuid_key_factory.rb +23 -0
- data/lib/datastax_rails/identity.rb +53 -0
- data/lib/datastax_rails/log_subscriber.rb +37 -0
- data/lib/datastax_rails/migrations/migration.rb +15 -0
- data/lib/datastax_rails/migrations.rb +36 -0
- data/lib/datastax_rails/mocking.rb +15 -0
- data/lib/datastax_rails/persistence.rb +133 -0
- data/lib/datastax_rails/railtie.rb +20 -0
- data/lib/datastax_rails/reflection.rb +472 -0
- data/lib/datastax_rails/relation/finder_methods.rb +184 -0
- data/lib/datastax_rails/relation/modification_methods.rb +80 -0
- data/lib/datastax_rails/relation/search_methods.rb +349 -0
- data/lib/datastax_rails/relation/spawn_methods.rb +107 -0
- data/lib/datastax_rails/relation.rb +393 -0
- data/lib/datastax_rails/schema/migration.rb +106 -0
- data/lib/datastax_rails/schema/migration_proxy.rb +25 -0
- data/lib/datastax_rails/schema/migrator.rb +212 -0
- data/lib/datastax_rails/schema.rb +37 -0
- data/lib/datastax_rails/scoping.rb +394 -0
- data/lib/datastax_rails/serialization.rb +6 -0
- data/lib/datastax_rails/tasks/column_family.rb +162 -0
- data/lib/datastax_rails/tasks/ds.rake +63 -0
- data/lib/datastax_rails/tasks/keyspace.rb +57 -0
- data/lib/datastax_rails/timestamps.rb +19 -0
- data/lib/datastax_rails/type.rb +16 -0
- data/lib/datastax_rails/types/array_type.rb +77 -0
- data/lib/datastax_rails/types/base_type.rb +26 -0
- data/lib/datastax_rails/types/binary_type.rb +15 -0
- data/lib/datastax_rails/types/boolean_type.rb +22 -0
- data/lib/datastax_rails/types/date_type.rb +17 -0
- data/lib/datastax_rails/types/float_type.rb +18 -0
- data/lib/datastax_rails/types/integer_type.rb +18 -0
- data/lib/datastax_rails/types/string_type.rb +16 -0
- data/lib/datastax_rails/types/text_type.rb +16 -0
- data/lib/datastax_rails/types/time_type.rb +17 -0
- data/lib/datastax_rails/types.rb +9 -0
- data/lib/datastax_rails/validations/uniqueness.rb +119 -0
- data/lib/datastax_rails/validations.rb +48 -0
- data/lib/datastax_rails/version.rb +3 -0
- data/lib/datastax_rails.rb +87 -0
- data/lib/solr_no_escape.rb +28 -0
- data/spec/datastax_rails/associations/belongs_to_association_spec.rb +7 -0
- data/spec/datastax_rails/associations/has_many_association_spec.rb +37 -0
- data/spec/datastax_rails/associations_spec.rb +22 -0
- data/spec/datastax_rails/attribute_methods_spec.rb +23 -0
- data/spec/datastax_rails/base_spec.rb +15 -0
- data/spec/datastax_rails/cql/select_spec.rb +12 -0
- data/spec/datastax_rails/cql/update_spec.rb +0 -0
- data/spec/datastax_rails/relation/finder_methods_spec.rb +54 -0
- data/spec/datastax_rails/relation/modification_methods_spec.rb +41 -0
- data/spec/datastax_rails/relation/search_methods_spec.rb +117 -0
- data/spec/datastax_rails/relation/spawn_methods_spec.rb +28 -0
- data/spec/datastax_rails/relation_spec.rb +130 -0
- data/spec/datastax_rails/validations/uniqueness_spec.rb +41 -0
- data/spec/datastax_rails_spec.rb +5 -0
- data/spec/dummy/Rakefile +8 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +47 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/datastax.yml +18 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/config/sunspot.yml +17 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/ks/migrate/20111117224534_models.rb +20 -0
- data/spec/dummy/ks/schema.json +180 -0
- data/spec/dummy/log/development.log +298 -0
- data/spec/dummy/log/production.log +0 -0
- data/spec/dummy/log/test.log +20307 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/datastax_test_hook.rb +14 -0
- data/spec/support/models.rb +72 -0
- metadata +353 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module DatastaxRails
|
2
|
+
module ModificationMethods
|
3
|
+
# Destroys the records matching +conditions+ by instantiating each
|
4
|
+
# record and calling its +destroy+ method. Each object's callbacks are
|
5
|
+
# executed (including <tt>:dependent</tt> association options and
|
6
|
+
# +before_destroy+/+after_destroy+ Observer methods). Returns the
|
7
|
+
# collection of objects that were destroyed; each will be frozen, to
|
8
|
+
# reflect that no changes should be made (since they can't be
|
9
|
+
# persisted).
|
10
|
+
#
|
11
|
+
# Note: Instantiation, callback execution, and deletion of each
|
12
|
+
# record can be time consuming when you're removing many records at
|
13
|
+
# once. However, it is necessary to perform it this way to ensure
|
14
|
+
# that the SOLR index stays in sync with the Cassandra data store.
|
15
|
+
#
|
16
|
+
# +delete_all+ is aliased to this because you can't delete without running
|
17
|
+
# the requisite callbacks. (at least not yet)
|
18
|
+
#
|
19
|
+
# ==== Parameters
|
20
|
+
#
|
21
|
+
# * +conditions+ - A string, array, or hash that specifies which records
|
22
|
+
# to destroy. If omitted, all records matching the current relation are
|
23
|
+
# destroyed. See the Conditions section in the introduction to
|
24
|
+
# DatastaxRails::Base for more information.
|
25
|
+
#
|
26
|
+
# ==== Examples
|
27
|
+
#
|
28
|
+
# Person.destroy_all(:status => "inactive")
|
29
|
+
# Person.where(:age => 0..18).destroy_all
|
30
|
+
# Person.where_not(:status => "active").destroy_all
|
31
|
+
def destroy_all(conditions = nil)
|
32
|
+
if conditions
|
33
|
+
ret = where(conditions).destroy_all
|
34
|
+
else
|
35
|
+
ret = to_a.each {|object| object.destroy }
|
36
|
+
end
|
37
|
+
reset
|
38
|
+
ret
|
39
|
+
end
|
40
|
+
# TODO: Find a way to delete from both without instantiating
|
41
|
+
alias :delete_all :destroy_all
|
42
|
+
|
43
|
+
# Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
|
44
|
+
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
45
|
+
# in-efficient since it actually has to instantiate the object just to delte it but allows cleanup
|
46
|
+
# methods such as the ones that remove the object from SOLR to be run.
|
47
|
+
#
|
48
|
+
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
49
|
+
# from the attributes, and then calls destroy on it.
|
50
|
+
#
|
51
|
+
# Note: this will only find objects that are matched by the current relation even if the ID would
|
52
|
+
# otherwise be valid.
|
53
|
+
#
|
54
|
+
# +delete+ is aliased to this because you can't delete without running
|
55
|
+
# the requisite callbacks. (at least not yet)
|
56
|
+
#
|
57
|
+
# ==== Parameters
|
58
|
+
#
|
59
|
+
# * +id+ - Can be either an Integer or an Array of Integers.
|
60
|
+
#
|
61
|
+
# ==== Examples
|
62
|
+
#
|
63
|
+
# # Destroy a single object
|
64
|
+
# Todo.destroy(1)
|
65
|
+
#
|
66
|
+
# # Destroy multiple objects
|
67
|
+
# todos = [1,2,3]
|
68
|
+
# Todo.destroy(todos)
|
69
|
+
def destroy(id)
|
70
|
+
if id.is_a?(Array)
|
71
|
+
ret = id.map { |one_id| destroy(one_id) }
|
72
|
+
else
|
73
|
+
ret = find(id).destroy
|
74
|
+
end
|
75
|
+
reset
|
76
|
+
ret
|
77
|
+
end
|
78
|
+
alias :delete :destroy
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,349 @@
|
|
1
|
+
module DatastaxRails
|
2
|
+
module SearchMethods
|
3
|
+
|
4
|
+
# Normally special characters (other than wild cards) are escaped before the search
|
5
|
+
# is submitted. If you want to handle escaping yourself because you need to use
|
6
|
+
# those special characters, then just include this in your chain.
|
7
|
+
#
|
8
|
+
# Model.dont_escape.where("(some stuff I don\'t want escaped)")
|
9
|
+
#
|
10
|
+
# Note that fulltext searches are NEVER escaped. Use Relation.solr_escape if you
|
11
|
+
# want that done.
|
12
|
+
def dont_escape
|
13
|
+
clone.tap do |r|
|
14
|
+
r.escape_value = false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Used to extend a scope with additional methods, either through
|
19
|
+
# a module or a block provided
|
20
|
+
#
|
21
|
+
# The object returned is a relation which can be further extended
|
22
|
+
def extending(*modules)
|
23
|
+
modules << Module.new(&Proc.new) if block_given?
|
24
|
+
|
25
|
+
return self if modules.empty?
|
26
|
+
|
27
|
+
clone.tap do |r|
|
28
|
+
r.send(:apply_modules, modules.flatten)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Limit a single page to +value+ records
|
33
|
+
#
|
34
|
+
# Model.limit(1)
|
35
|
+
# Model.per_page(50)
|
36
|
+
#
|
37
|
+
# Normally DatastaxRails searches are paginated at a really high number
|
38
|
+
# so as to effectively disable pagination. However, you can cause
|
39
|
+
# all requests to be paginated on a per-model basis by overriding the
|
40
|
+
# +default_page_size+ class method in your model:
|
41
|
+
#
|
42
|
+
# class Model < DatastaxRails::Base
|
43
|
+
# def self.default_page_size
|
44
|
+
# 30
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
def limit(value)
|
48
|
+
clone.tap do |r|
|
49
|
+
r.per_page_value = value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
alias :per_page :limit
|
53
|
+
|
54
|
+
# Sets the page number to retrieve
|
55
|
+
#
|
56
|
+
# Model.page(2)
|
57
|
+
def page(value)
|
58
|
+
clone.tap do |r|
|
59
|
+
r.page_value = value
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# WillPaginate compatible method for paginating
|
64
|
+
#
|
65
|
+
# Model.paginate(:page => 2, :per_page => 10)
|
66
|
+
def paginate(options = {})
|
67
|
+
options = options.reverse_merge({:page => 1, :per_page => 30})
|
68
|
+
clone.tap do |r|
|
69
|
+
r.page_value = options[:page]
|
70
|
+
r.per_page_value = options[:per_page]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Group results by one or more attributes only returning the top result
|
75
|
+
# for each group.
|
76
|
+
#
|
77
|
+
# Model.group(:program_id)
|
78
|
+
def group(*attrs)
|
79
|
+
return self if attrs.blank?
|
80
|
+
|
81
|
+
clone.tap do |r|
|
82
|
+
r.group_values += args.flatten
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Orders the result set by a particular attribute. Note that text fields
|
87
|
+
# may not be used for ordering as they are tokenized. Valid candidates
|
88
|
+
# are fields of type +string+, +integer+, +long+, +float+, +double+, and
|
89
|
+
# +time+. In addition, the symbol +:score+ can be used to sort on the
|
90
|
+
# relevance rating returned by Solr. The default direction is ascending
|
91
|
+
# but may be reversed by passing a hash where the field is the key and
|
92
|
+
# the value is :desc
|
93
|
+
#
|
94
|
+
# Model.order(:name)
|
95
|
+
# Model.order(:name => :desc)
|
96
|
+
def order(attribute)
|
97
|
+
return self if attribute.blank?
|
98
|
+
|
99
|
+
clone.tap do |r|
|
100
|
+
order_by = attribute.is_a?(Hash) ? attribute.dup : {attribute => :asc}
|
101
|
+
|
102
|
+
r.order_values << order_by
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Works in two unique ways.
|
107
|
+
#
|
108
|
+
# _First_: takes a block so it can be used just like Array#select.
|
109
|
+
#
|
110
|
+
# Model.scoped.select { |m| m.field == value }
|
111
|
+
#
|
112
|
+
# This will build an array of objects from the database for the scope,
|
113
|
+
# converting them into an array and iterating through them using Array#select.
|
114
|
+
#
|
115
|
+
# _Second_: Modifies the query so that only certain fields are retrieved:
|
116
|
+
#
|
117
|
+
# >> Model.select(:field)
|
118
|
+
# => [#<Model field:value>]
|
119
|
+
#
|
120
|
+
# Although in the above example it looks as though this method returns an
|
121
|
+
# array, it actually returns a relation object and can have other query
|
122
|
+
# methods appended to it, such as the other methods in DatastaxRails::SearchMethods.
|
123
|
+
#
|
124
|
+
# This method will also take multiple parameters:
|
125
|
+
#
|
126
|
+
# >> Model.select(:field, :other_field, :and_one_more)
|
127
|
+
# => [#<Model field: "value", other_field: "value", and_one_more: "value">]
|
128
|
+
#
|
129
|
+
# Any attributes that do not have fields retrieved by a select
|
130
|
+
# will return `nil` when the getter method for that attribute is used:
|
131
|
+
#
|
132
|
+
# >> Model.select(:field).first.other_field
|
133
|
+
# => nil
|
134
|
+
#
|
135
|
+
# The exception to this rule is when an attribute is lazy-loaded (e.g., binary).
|
136
|
+
# In that case, it is never retrieved until you call the getter method.
|
137
|
+
def select(value = Proc.new)
|
138
|
+
if block_given?
|
139
|
+
to_a.select {|*block_args| value.call(*block_args) }
|
140
|
+
else
|
141
|
+
clone.tap do |r|
|
142
|
+
r.select_values += Array.wrap(value)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Reverses the order of the results
|
148
|
+
#
|
149
|
+
# Model.order(:name).reverse_order
|
150
|
+
# is equivalent to
|
151
|
+
# Model.order(:name => :desc)
|
152
|
+
#
|
153
|
+
# Model.order(:name).reverse_order.reverse_order
|
154
|
+
# is equivalent to
|
155
|
+
# Model.order(:name => :asc)
|
156
|
+
def reverse_order
|
157
|
+
clone.tap do |r|
|
158
|
+
r.reverse_order_value == !r.reverse_order_value
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# By default, DatastaxRails uses the LuceneQueryParser. Note that this
|
163
|
+
# is a change from the underlying Sunspot gem. Sunspot defaults to the
|
164
|
+
# +disMax+ query parser. If you want to use that, then pass that in here.
|
165
|
+
#
|
166
|
+
# *This only applies to fulltext queries*
|
167
|
+
#
|
168
|
+
# Model.query_parser('disMax').fulltext("john smith")
|
169
|
+
def query_parser(attribute)
|
170
|
+
return self if attribute.blank?
|
171
|
+
|
172
|
+
clone.tap do |r|
|
173
|
+
r.query_parser_value = attribute
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# By default, DatastaxRails will try to pick the right method of performing
|
178
|
+
# a search. You can use this method to force it to make the query via SOLR.
|
179
|
+
#
|
180
|
+
# NOTE that the time between when a record is placed into Cassandra and when
|
181
|
+
# it becomes available in SOLR is not guaranteed to be insignificant. It's
|
182
|
+
# very possible to insert a new record and not find it when immediately doing
|
183
|
+
# a SOLR search for it.
|
184
|
+
def with_solr
|
185
|
+
clone.tap do |r|
|
186
|
+
r.use_solr_value = true
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# By default, DatastaxRails will try to pick the right method of performing
|
191
|
+
# a search. You can use this method to force it to make the query via
|
192
|
+
# cassandra.
|
193
|
+
#
|
194
|
+
# NOTE that this method assumes that you have all the proper secondary indexes
|
195
|
+
# in place before you attempt to use it. If not, you will get an error.
|
196
|
+
def with_cassandra
|
197
|
+
clone.tap do |r|
|
198
|
+
r.use_solr_value = false
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# Specifies restrictions (scoping) on the result set. Expects a hash
|
203
|
+
# in the form +attribute => value+ for equality comparisons.
|
204
|
+
#
|
205
|
+
# Model.where(:group_id => '1234', :active => 'Y')
|
206
|
+
#
|
207
|
+
# The value of the comparison does not need to be a scalar. For example:
|
208
|
+
#
|
209
|
+
# Model.where(:name => ["Bob", "Tom", "Sally"])
|
210
|
+
# Model.where(:age => 18..65)
|
211
|
+
#
|
212
|
+
# Inequality comparisons such as greater_than and less_than are
|
213
|
+
# specified via chaining:
|
214
|
+
#
|
215
|
+
# Model.where(:created_at).greater_than(1.day.ago)
|
216
|
+
# Model.where(:age).less_than(65)
|
217
|
+
#
|
218
|
+
# NOTE: Due to the way SOLR handles range queries, all greater/less than
|
219
|
+
# queries are actually greater/less than or equal to queries.
|
220
|
+
# There is no way to perform a strictly greater/less than query.
|
221
|
+
def where(attribute)
|
222
|
+
return self if attribute.blank?
|
223
|
+
|
224
|
+
if attribute.is_a?(Symbol)
|
225
|
+
WhereProxy.new(self, attribute)
|
226
|
+
else
|
227
|
+
attributes = attribute.dup
|
228
|
+
attributes.each do |k,v|
|
229
|
+
attributes[k] = solr_format(v)
|
230
|
+
end
|
231
|
+
clone.tap do |r|
|
232
|
+
r.where_values << attributes
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Specifies restrictions (scoping) that should not match the result set.
|
238
|
+
# Expects a hash in the form +attribute => value+.
|
239
|
+
#
|
240
|
+
# Model.where_not(:group_id => '1234', :active => 'N')
|
241
|
+
#
|
242
|
+
# Passing an array will search for records where none of the array entries
|
243
|
+
# are present
|
244
|
+
#
|
245
|
+
# Model.where_not(:group_id => ['1234', '5678'])
|
246
|
+
#
|
247
|
+
# The above would find all models where group id is neither 1234 or 5678.
|
248
|
+
def where_not(attribute)
|
249
|
+
return self if attribute.blank?
|
250
|
+
|
251
|
+
attributes = []
|
252
|
+
attribute.each do |k,v|
|
253
|
+
if v.is_a?(Array)
|
254
|
+
v.each do |val|
|
255
|
+
attributes << {k => solr_format(val)}
|
256
|
+
end
|
257
|
+
else
|
258
|
+
attributes << {k => solr_format(v)}
|
259
|
+
end
|
260
|
+
end
|
261
|
+
clone.tap do |r|
|
262
|
+
r.where_not_values += attributes
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# Specifies a full text search string to be processed by SOLR
|
267
|
+
#
|
268
|
+
# Model.fulltext("john smith")
|
269
|
+
#
|
270
|
+
# You can also pass in an options hash with the following options:
|
271
|
+
#
|
272
|
+
# * :fields => list of fields to search instead of the default of all fields
|
273
|
+
# * :highlight => List of fields to retrieve highlights for. Note that highlighted fields *must* be +:stored+
|
274
|
+
#
|
275
|
+
# Model.fulltext("john smith", :fields => [:title])
|
276
|
+
# Model.fulltext("john smith", :hightlight => [:body])
|
277
|
+
def fulltext(query, opts = {})
|
278
|
+
return self if query.blank?
|
279
|
+
|
280
|
+
opts[:query] = query
|
281
|
+
|
282
|
+
clone.tap do |r|
|
283
|
+
r.fulltext_values << opts
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# See documentation for +where+
|
288
|
+
def less_than(value)
|
289
|
+
raise ArgumentError, "#less_than can only be called after an appropriate where call. e.g. where(:created_at).less_than(1.day.ago)"
|
290
|
+
end
|
291
|
+
|
292
|
+
# See documentation for +where+
|
293
|
+
def greater_than(value)
|
294
|
+
raise ArgumentError, "#greater_than can only be called after an appropriate where call. e.g. where(:created_at).greater_than(1.day.ago)"
|
295
|
+
end
|
296
|
+
|
297
|
+
def solr_format(value)
|
298
|
+
case
|
299
|
+
when value.is_a?(Date), value.is_a?(Time)
|
300
|
+
value.strftime('%Y-%m-%dT%H\:%M\:%SZ')
|
301
|
+
when value.is_a?(Array)
|
302
|
+
value.collect {|v| v.gsub(/ /,"\\ ") }.join(" OR ")
|
303
|
+
when value.is_a?(Fixnum)
|
304
|
+
value < 0 ? "\\#{value}" : value
|
305
|
+
when value.is_a?(String)
|
306
|
+
value.gsub(/ /,"\\ ")
|
307
|
+
else
|
308
|
+
value
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
protected
|
313
|
+
def find_by_attributes(match, attributes, *args) #:nodoc:
|
314
|
+
conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
|
315
|
+
result = where(conditions).send(match.finder)
|
316
|
+
|
317
|
+
if match.blank? && result.blank?
|
318
|
+
raise RecordNotFound, "Couldn't find #{klass.name} with #{conditions.to_a.collect {|p| p.join('=')}.join(', ')}"
|
319
|
+
else
|
320
|
+
yield(result) if block_given?
|
321
|
+
result
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
class WhereProxy #:nodoc:
|
326
|
+
def initialize(relation, attribute) #:nodoc:
|
327
|
+
@relation, @attribute = relation, attribute
|
328
|
+
end
|
329
|
+
|
330
|
+
def equal_to(value) #:nodoc:
|
331
|
+
@relation.clone.tap do |r|
|
332
|
+
r.where_values << {@attribute => r.solr_format(value)}
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def greater_than(value) #:nodoc:
|
337
|
+
@relation.clone.tap do |r|
|
338
|
+
r.greater_than_values << {@attribute => r.solr_format(value)}
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
def less_than(value) #:nodoc:
|
343
|
+
@relation.clone.tap do |r|
|
344
|
+
r.less_than_values << {@attribute => r.solr_format(value)}
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module DatastaxRails
|
2
|
+
module SpawnMethods
|
3
|
+
# def scoped #:nodoc:
|
4
|
+
# self
|
5
|
+
# end
|
6
|
+
|
7
|
+
def merge(r) #:nodoc:
|
8
|
+
return self unless r
|
9
|
+
return to_a & r if r.is_a?(Array)
|
10
|
+
|
11
|
+
merged_relation = clone
|
12
|
+
|
13
|
+
(Relation::MULTI_VALUE_METHODS - [:where, :where_not]).each do |method|
|
14
|
+
value = r.send(:"#{method}_values")
|
15
|
+
merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present?
|
16
|
+
end
|
17
|
+
|
18
|
+
merged_wheres = {}
|
19
|
+
# This will merge all the where clauses into a single hash. If the same attribute is
|
20
|
+
# specified multiple times, the last one will win.
|
21
|
+
(@where_values + r.where_values).each { |w| merged_wheres.merge!(w)}
|
22
|
+
|
23
|
+
merged_relation.where_values = [merged_wheres] unless merged_wheres.empty?
|
24
|
+
|
25
|
+
merged_where_nots = {}
|
26
|
+
# This will merge all the where not clauses into a single hash. If the same attribute is
|
27
|
+
# specified multiple times, the last one will win.
|
28
|
+
(@where_not_values + r.where_not_values).each { |w| merged_where_nots.merge!(w)}
|
29
|
+
|
30
|
+
merged_relation.where_not_values = [merged_where_nots] unless merged_where_nots.empty?
|
31
|
+
|
32
|
+
(Relation::SINGLE_VALUE_METHODS).each do |method|
|
33
|
+
value = r.send(:"#{method}_value")
|
34
|
+
merged_relation.send(:"#{method}_value=", value) unless value.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
merged_relation
|
38
|
+
end
|
39
|
+
|
40
|
+
# Removes from the query the condition(s) specified in +skips+.
|
41
|
+
#
|
42
|
+
# Example:
|
43
|
+
#
|
44
|
+
# Post.where(:active => true).order('id').except(:order) # discards the order condition
|
45
|
+
# Post.where(:active => true).order('id').except(:where) # discards the where condition but keeps the order
|
46
|
+
def except(*skips)
|
47
|
+
result = self.class.new(@klass, table)
|
48
|
+
result.default_scoped = default_scoped
|
49
|
+
|
50
|
+
((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - skips).each do |method|
|
51
|
+
result.send(:"#{method}_values=", send(:"#{method}_values"))
|
52
|
+
end
|
53
|
+
|
54
|
+
(Relation::SINGLE_VALUE_METHODS - skips).each do |method|
|
55
|
+
result.send(:"#{method}_value=", send(:"#{method}_value"))
|
56
|
+
end
|
57
|
+
|
58
|
+
# Apply scope extension modules
|
59
|
+
result.send(:apply_modules, extensions)
|
60
|
+
|
61
|
+
result
|
62
|
+
end
|
63
|
+
|
64
|
+
# Removes any condition from the query other than the one(s) specified in +onlies+.
|
65
|
+
#
|
66
|
+
# Example:
|
67
|
+
#
|
68
|
+
# Post.order('id').only(:where) # discards the order condition
|
69
|
+
# Post.order('id').only(:where, :order) # uses the specified order
|
70
|
+
#
|
71
|
+
def only(*onlies)
|
72
|
+
result = self.class.new(@klass, table)
|
73
|
+
result.default_scoped = default_scoped
|
74
|
+
|
75
|
+
((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) & onlies).each do |method|
|
76
|
+
result.send(:"#{method}_values=", send(:"#{method}_values"))
|
77
|
+
end
|
78
|
+
|
79
|
+
(Relation::SINGLE_VALUE_METHODS & onlies).each do |method|
|
80
|
+
result.send(:"#{method}_value=", send(:"#{method}_value"))
|
81
|
+
end
|
82
|
+
|
83
|
+
# Apply scope extension modules
|
84
|
+
result.send(:apply_modules, extensions)
|
85
|
+
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
VALID_FIND_OPTIONS = [:conditions, :limit, :offset, :order, :group, :page, :per_page]
|
90
|
+
def apply_finder_options(options) #:nodoc:
|
91
|
+
relation = clone
|
92
|
+
return relation unless options
|
93
|
+
|
94
|
+
options.assert_valid_keys(VALID_FIND_OPTIONS)
|
95
|
+
finders = options.dup
|
96
|
+
finders.delete_if { |key, value| value.nil? }
|
97
|
+
|
98
|
+
([:group, :order, :limit, :offset, :page, :per_page] & finders.keys).each do |finder|
|
99
|
+
relation = relation.send(finder, finders[finder])
|
100
|
+
end
|
101
|
+
|
102
|
+
relation.where(finders[:conditions]) if options.has_key?(:conditions)
|
103
|
+
|
104
|
+
relation
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|