datastax_rails 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|