datastax_rails 1.2.3 → 2.0.3
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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -8
- data/config/schema.xml.erb +22 -19
- data/config/solrconfig.xml.erb +1 -1
- data/lib/cql-rb_extensions.rb +27 -0
- data/lib/datastax_rails.rb +13 -17
- data/lib/datastax_rails/associations/association.rb +1 -4
- data/lib/datastax_rails/associations/collection_proxy.rb +0 -13
- data/lib/datastax_rails/attribute_assignment.rb +28 -91
- data/lib/datastax_rails/attribute_methods.rb +109 -44
- data/lib/datastax_rails/attribute_methods/before_type_cast.rb +71 -0
- data/lib/datastax_rails/attribute_methods/dirty.rb +52 -11
- data/lib/datastax_rails/attribute_methods/primary_key.rb +87 -0
- data/lib/datastax_rails/attribute_methods/read.rb +120 -0
- data/lib/datastax_rails/attribute_methods/typecasting.rb +52 -21
- data/lib/datastax_rails/attribute_methods/write.rb +59 -0
- data/lib/datastax_rails/base.rb +227 -236
- data/lib/datastax_rails/cassandra_only_model.rb +25 -19
- data/lib/datastax_rails/column.rb +384 -0
- data/lib/datastax_rails/connection.rb +12 -13
- data/lib/datastax_rails/cql/alter_column_family.rb +0 -1
- data/lib/datastax_rails/cql/base.rb +15 -3
- data/lib/datastax_rails/cql/column_family.rb +2 -2
- data/lib/datastax_rails/cql/create_column_family.rb +7 -18
- data/lib/datastax_rails/cql/delete.rb +4 -9
- data/lib/datastax_rails/cql/insert.rb +2 -8
- data/lib/datastax_rails/cql/select.rb +4 -4
- data/lib/datastax_rails/cql/update.rb +8 -17
- data/lib/datastax_rails/dynamic_model.rb +98 -0
- data/lib/datastax_rails/payload_model.rb +19 -31
- data/lib/datastax_rails/persistence.rb +39 -54
- data/lib/datastax_rails/railtie.rb +1 -0
- data/lib/datastax_rails/reflection.rb +1 -1
- data/lib/datastax_rails/relation.rb +20 -20
- data/lib/datastax_rails/relation/batches.rb +18 -16
- data/lib/datastax_rails/relation/facet_methods.rb +1 -1
- data/lib/datastax_rails/relation/finder_methods.rb +6 -10
- data/lib/datastax_rails/relation/search_methods.rb +62 -48
- data/lib/datastax_rails/rsolr_client_wrapper.rb +1 -1
- data/lib/datastax_rails/schema/cassandra.rb +34 -62
- data/lib/datastax_rails/schema/migrator.rb +9 -24
- data/lib/datastax_rails/schema/solr.rb +13 -30
- data/lib/datastax_rails/schema_cache.rb +67 -0
- data/lib/datastax_rails/timestamps.rb +84 -11
- data/lib/datastax_rails/types/dirty_collection.rb +88 -0
- data/lib/datastax_rails/types/dynamic_list.rb +14 -0
- data/lib/datastax_rails/types/dynamic_map.rb +32 -0
- data/lib/datastax_rails/types/dynamic_set.rb +10 -0
- data/lib/datastax_rails/util/solr_repair.rb +4 -5
- data/lib/datastax_rails/validations.rb +6 -12
- data/lib/datastax_rails/validations/uniqueness.rb +0 -4
- data/lib/datastax_rails/version.rb +1 -1
- data/lib/datastax_rails/wide_storage_model.rb +13 -29
- data/lib/schema_migration.rb +4 -0
- data/spec/datastax_rails/associations_spec.rb +0 -1
- data/spec/datastax_rails/attribute_methods_spec.rb +9 -6
- data/spec/datastax_rails/base_spec.rb +26 -0
- data/spec/datastax_rails/column_spec.rb +238 -0
- data/spec/datastax_rails/cql/select_spec.rb +1 -1
- data/spec/datastax_rails/cql/update_spec.rb +2 -2
- data/spec/datastax_rails/persistence_spec.rb +29 -15
- data/spec/datastax_rails/relation/batches_spec.rb +5 -5
- data/spec/datastax_rails/relation/finder_methods_spec.rb +0 -20
- data/spec/datastax_rails/relation/search_methods_spec.rb +8 -0
- data/spec/datastax_rails/relation_spec.rb +7 -0
- data/spec/datastax_rails/schema/migrator_spec.rb +5 -10
- data/spec/datastax_rails/schema/solr_spec.rb +1 -1
- data/spec/datastax_rails/types/dynamic_list_spec.rb +20 -0
- data/spec/datastax_rails/types/dynamic_map_spec.rb +22 -0
- data/spec/datastax_rails/types/dynamic_set_spec.rb +16 -0
- data/spec/dummy/config/application.rb +2 -1
- data/spec/dummy/config/datastax.yml +6 -3
- data/spec/dummy/config/environments/development.rb +4 -5
- data/spec/dummy/config/environments/test.rb +0 -5
- data/spec/dummy/log/development.log +18 -0
- data/spec/dummy/log/test.log +36 -0
- data/spec/feature/dynamic_fields_spec.rb +9 -0
- data/spec/feature/overloaded_tables_spec.rb +24 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/default_consistency_shared_examples.rb +2 -2
- data/spec/support/models.rb +28 -14
- metadata +212 -188
- data/lib/datastax_rails/identity.rb +0 -64
- data/lib/datastax_rails/identity/abstract_key_factory.rb +0 -29
- data/lib/datastax_rails/identity/custom_key_factory.rb +0 -37
- data/lib/datastax_rails/identity/hashed_natural_key_factory.rb +0 -10
- data/lib/datastax_rails/identity/natural_key_factory.rb +0 -39
- data/lib/datastax_rails/identity/uuid_key_factory.rb +0 -27
- data/lib/datastax_rails/type.rb +0 -16
- data/lib/datastax_rails/types.rb +0 -9
- data/lib/datastax_rails/types/array_type.rb +0 -86
- data/lib/datastax_rails/types/base_type.rb +0 -42
- data/lib/datastax_rails/types/binary_type.rb +0 -19
- data/lib/datastax_rails/types/boolean_type.rb +0 -22
- data/lib/datastax_rails/types/date_type.rb +0 -23
- data/lib/datastax_rails/types/float_type.rb +0 -18
- data/lib/datastax_rails/types/integer_type.rb +0 -18
- data/lib/datastax_rails/types/string_type.rb +0 -16
- data/lib/datastax_rails/types/text_type.rb +0 -15
- data/lib/datastax_rails/types/time_type.rb +0 -23
- data/spec/datastax_rails/types/float_type_spec.rb +0 -31
- data/spec/datastax_rails/types/integer_type_spec.rb +0 -31
- data/spec/datastax_rails/types/time_type_spec.rb +0 -28
@@ -1,27 +1,33 @@
|
|
1
1
|
module DatastaxRails
|
2
|
-
# A
|
3
|
-
# Classes that
|
2
|
+
# A module designed for models that will only interact with Cassandra.
|
3
|
+
# Classes that include this will not generate Solr schemas or have
|
4
4
|
# any communication with Solr. If an application only uses these models
|
5
5
|
# then it should be possible to run with pure Cassandra and no Solr at all.
|
6
|
-
|
7
|
-
|
6
|
+
#
|
7
|
+
# If you want to search by anything other than primary_key, you will need
|
8
|
+
# to add CQL indexes as they are not created by default.
|
9
|
+
#
|
10
|
+
# class Model < DatastaxRails::Base
|
11
|
+
# include DatastaxRails::CassandraOnlyModel
|
12
|
+
#
|
13
|
+
# uuid :id
|
14
|
+
# string :name, :cql_index => true
|
15
|
+
# end
|
16
|
+
module CassandraOnlyModel
|
17
|
+
extend ActiveSupport::Concern
|
18
|
+
|
19
|
+
included do
|
20
|
+
default_scope -> {with_cassandra}
|
21
|
+
self.storage_method = :cql
|
22
|
+
end
|
8
23
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
encoded[column_name.to_s] = encoded[column_name.to_s][0..-2]
|
16
|
-
elsif attribute_definitions[column_name.to_sym].coder.options[:cassandra_type] == 'int'
|
17
|
-
encoded[column_name.to_s] = encoded[column_name.to_s].to_i
|
18
|
-
elsif attribute_definitions[column_name.to_sym].coder.options[:cassandra_type] == 'boolean'
|
19
|
-
encoded[column_name.to_s] = encoded[column_name.to_s] == '1'
|
20
|
-
end
|
24
|
+
module ClassMethods
|
25
|
+
def attribute(name, options)
|
26
|
+
opts = options.update(:solr_index => false, :solr_store => false,
|
27
|
+
:multi_valued => false, :sortable => false,
|
28
|
+
:tokenized => false, :fulltext => false)
|
29
|
+
super(name, opts)
|
21
30
|
end
|
22
|
-
encoded
|
23
31
|
end
|
24
|
-
|
25
|
-
default_scope with_cassandra
|
26
32
|
end
|
27
33
|
end
|
@@ -0,0 +1,384 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module DatastaxRails
|
4
|
+
class Column
|
5
|
+
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set
|
6
|
+
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set
|
7
|
+
|
8
|
+
module Format
|
9
|
+
ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
|
10
|
+
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
|
11
|
+
SOLR_TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ".force_encoding('utf-8').freeze
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :name, :type, :cql_type, :solr_type, :options
|
15
|
+
attr_accessor :primary, :coder, :default
|
16
|
+
|
17
|
+
alias :encoded? :coder
|
18
|
+
|
19
|
+
# Instantiates a new column in the table.
|
20
|
+
#
|
21
|
+
# +name+ is the column's name as specified in the schema. e.g., 'first_name' in
|
22
|
+
# <tt>first_name text</tt>.
|
23
|
+
# +default+ is the type-casted default value that will be applied to a new record
|
24
|
+
# if no value is given.
|
25
|
+
# +type+ is the type of the column. Usually this will match the cql_type, but
|
26
|
+
# there are exceptions (e.g., date)
|
27
|
+
# +cql_type+ is the type of column as specified in the schema. e.g., 'text' in
|
28
|
+
# <tt>first_name text</tt>.
|
29
|
+
# +solr_type+ overrides the normal CQL <-> SOLR type mapping (uncommon)
|
30
|
+
def initialize(name, default, type, options = {})
|
31
|
+
@name = name
|
32
|
+
@type = type.to_sym
|
33
|
+
raise ArgumentError, "Unknown type #{type}" unless self.klass
|
34
|
+
options[:holds] = 'string' if collection? && options[:holds].blank?
|
35
|
+
@options = configure_options(@type, options).with_indifferent_access
|
36
|
+
@cql_type = compute_cql_type(@type, @options)
|
37
|
+
@solr_type = compute_solr_type(@type, @options)
|
38
|
+
@default = extract_default(default)
|
39
|
+
@primary = nil
|
40
|
+
@coder = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def configure_options(type, options)
|
44
|
+
case type.to_sym
|
45
|
+
when :set, :list, :map then
|
46
|
+
configure_options(options[:holds], options).merge(:multi_valued => true)
|
47
|
+
when :binary then
|
48
|
+
{:solr_index => false, :solr_store => false,
|
49
|
+
:multi_valued => false, :sortable => false,
|
50
|
+
:tokenized => false, :fulltext => false,
|
51
|
+
:cql_index => false}
|
52
|
+
when :boolean, :date, :time, :timestamp, :datetime, :float, :integer, :uuid then
|
53
|
+
{:solr_index => true, :solr_store => true,
|
54
|
+
:multi_valued => false, :sortable => true,
|
55
|
+
:tokenized => false, :fulltext => false,
|
56
|
+
:cql_index => false}
|
57
|
+
when :string then
|
58
|
+
{:solr_index => true, :solr_store => true,
|
59
|
+
:multi_valued => false, :sortable => true,
|
60
|
+
:tokenized => false, :fulltext => true,
|
61
|
+
:cql_index => false}
|
62
|
+
when :text then
|
63
|
+
{:solr_index => true, :solr_store => true,
|
64
|
+
:multi_valued => false, :sortable => false,
|
65
|
+
:tokenized => true, :fulltext => true,
|
66
|
+
:cql_index => false}
|
67
|
+
else
|
68
|
+
raise ArgumentError, "Unknown Type: #{type.to_s}"
|
69
|
+
end.merge(options)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns +true+ if the column is either of type ascii or text.
|
73
|
+
def text?
|
74
|
+
[:ascii, :text].include?(type)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns +true+ if the column is either of type integer, float or decimal.
|
78
|
+
def number?
|
79
|
+
[:decimal, :double, :float, :integer].include?(type)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns +true+ if the column is of type binary
|
83
|
+
def binary?
|
84
|
+
[:binary].include?(type)
|
85
|
+
end
|
86
|
+
|
87
|
+
def collection?
|
88
|
+
[:set, :list, :map].include?(type)
|
89
|
+
end
|
90
|
+
|
91
|
+
def has_default?
|
92
|
+
!default.nil?
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the Ruby class that corresponds to the abstract data type.
|
96
|
+
def klass
|
97
|
+
case type
|
98
|
+
when :integer then Fixnum
|
99
|
+
when :float then Float
|
100
|
+
when :decimal, :double then BigDecimal
|
101
|
+
when :timestamp, :time, :datetime then Time
|
102
|
+
when :date then Date
|
103
|
+
when :text, :string, :binary, :ascii then String
|
104
|
+
when :boolean then Object
|
105
|
+
when :uuid then ::Cql::TimeUuid
|
106
|
+
when :list then DatastaxRails::Types::DynamicList
|
107
|
+
when :set then DatastaxRails::Types::DynamicSet
|
108
|
+
when :map then DatastaxRails::Types::DynamicMap
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Casts value (which can be a String) to an appropriate instance.
|
113
|
+
def type_cast(value, record = nil, dest_type = nil)
|
114
|
+
return nil if value.nil?
|
115
|
+
return coder.load(value) if encoded?
|
116
|
+
|
117
|
+
klass = self.class
|
118
|
+
|
119
|
+
case dest_type || type
|
120
|
+
when :string, :text then value.to_s
|
121
|
+
when :ascii then value.force_encoding('ascii')
|
122
|
+
when :integer then klass.value_to_integer(value)
|
123
|
+
when :float then value.to_f
|
124
|
+
when :decimal then klass.value_to_decimal(value)
|
125
|
+
when :datetime, :timestamp then klass.string_to_time(value)
|
126
|
+
when :time then klass.string_to_dummy_time(value)
|
127
|
+
when :date then klass.value_to_date(value)
|
128
|
+
when :binary then klass.binary_to_string(value)
|
129
|
+
when :boolean then klass.value_to_boolean(value)
|
130
|
+
when :uuid, :timeuuid then klass.value_to_uuid(value)
|
131
|
+
when :list, :set then wrap_collection(value.collect {|v| type_cast(v, record, @options[:holds])}, record)
|
132
|
+
when :map then wrap_collection(value.each {|k,v| value[k] = type_cast(v, record, @options[:holds])}.stringify_keys, record)
|
133
|
+
else value
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def wrap_collection(collection, record)
|
138
|
+
klass.new(record, name, collection)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Cql-rb does a really good job of typecasting, so for the most part we
|
142
|
+
# just pass in the native types. The only exceptions are for UUIDs that
|
143
|
+
# are passed in as strings and dates.
|
144
|
+
def type_cast_for_cql3(value, dest_type = nil)
|
145
|
+
return nil if value.nil?
|
146
|
+
return coder.dump(value) if encoded?
|
147
|
+
|
148
|
+
case (dest_type || type)
|
149
|
+
when :uuid then value.is_a?(::Cql::Uuid) ? value : self.class.value_to_uuid(value)
|
150
|
+
when :date then value.to_time
|
151
|
+
when :list, :set then list_to_cql3_value(value)
|
152
|
+
when :map then map_to_cql3_value(value)
|
153
|
+
else value
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# By contrast, since Solr isn't doing things like prepared statements
|
158
|
+
# it doesn't know what the types are so we have to handle any casting
|
159
|
+
# or encoding ourselves.
|
160
|
+
def type_cast_for_solr(value, dest_type = nil)
|
161
|
+
return nil if value.nil?
|
162
|
+
return coder.dump(value) if encoded?
|
163
|
+
|
164
|
+
case (dest_type || type)
|
165
|
+
when :boolean then value ? 'true' : 'false'
|
166
|
+
when :date, :time, :datetime, :timestamp then value.strftime(Format::SOLR_TIME_FORMAT)
|
167
|
+
when :list, :set then self.list_to_solr_value(value)
|
168
|
+
when :map then self.map_to_solr_value(value)
|
169
|
+
else value
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def list_to_solr_value(value)
|
174
|
+
value.map {|v| type_cast_for_solr(v, @options[:holds])}
|
175
|
+
end
|
176
|
+
|
177
|
+
def map_to_solr_value(value)
|
178
|
+
value.each {|k,v| value[k] = type_cast_for_solr(v, @options[:holds])}
|
179
|
+
end
|
180
|
+
|
181
|
+
def list_to_cql3_value(value)
|
182
|
+
value.map {|v| type_cast_for_cql3(v, @options[:holds])}
|
183
|
+
end
|
184
|
+
|
185
|
+
def map_to_cql3_value(value)
|
186
|
+
value.dup.each {|k,v| value[k] = type_cast_for_cql3(v, @options[:holds])}
|
187
|
+
value
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns the human name of the column name.
|
191
|
+
#
|
192
|
+
# ===== Examples
|
193
|
+
# Column.new('sales_stage', ...).human_name # => 'Sales stage'
|
194
|
+
def human_name
|
195
|
+
Base.human_attribute_name(@name)
|
196
|
+
end
|
197
|
+
|
198
|
+
def extract_default(default)
|
199
|
+
case type
|
200
|
+
when :map then {}#lambda {|rec| DatastaxRails::Types::DynamicMap.new(rec, self.name.to_s, {})}
|
201
|
+
when :list then []#lambda {|rec| DatastaxRails::Types::DynamicList.new(rec, self.name.to_s, [])}
|
202
|
+
when :set then Set.new#lambda {|set| DatastaxRails::Types::DynamicSet.new(rec, self.name.to_s, Set.new)}
|
203
|
+
else default
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Used to convert from Strings to BLOBs
|
208
|
+
def string_to_binary(value)
|
209
|
+
self.class.string_to_binary(value)
|
210
|
+
end
|
211
|
+
|
212
|
+
def full_solr_range
|
213
|
+
if solr_type == 'date'
|
214
|
+
'[* TO *]'
|
215
|
+
else
|
216
|
+
'[\"\" TO *]'
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
class << self
|
221
|
+
# Used to convert from Strings to BLOBs
|
222
|
+
def string_to_binary(value)
|
223
|
+
# TODO: Figure out what Cassandra's blobs look like
|
224
|
+
value
|
225
|
+
end
|
226
|
+
|
227
|
+
# Used to convert from BLOBs to Strings
|
228
|
+
def binary_to_string(value)
|
229
|
+
# TODO: Figure out what Cassandra's blobs look like
|
230
|
+
value
|
231
|
+
end
|
232
|
+
|
233
|
+
def value_to_date(value)
|
234
|
+
if value.is_a?(String)
|
235
|
+
return nil if value.empty?
|
236
|
+
fast_string_to_date(value) || fallback_string_to_date(value)
|
237
|
+
elsif value.respond_to?(:to_date)
|
238
|
+
value.to_date
|
239
|
+
else
|
240
|
+
value
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def string_to_time(string)
|
245
|
+
return string unless string.is_a?(String)
|
246
|
+
return nil if string.empty?
|
247
|
+
|
248
|
+
fast_string_to_time(string) || fallback_string_to_time(string)
|
249
|
+
end
|
250
|
+
|
251
|
+
def string_to_dummy_time(string)
|
252
|
+
return string unless string.is_a?(String)
|
253
|
+
return nil if string.empty?
|
254
|
+
|
255
|
+
dummy_time_string = "2000-01-01 #{string}"
|
256
|
+
|
257
|
+
fast_string_to_time(dummy_time_string) || begin
|
258
|
+
time_hash = Date._parse(dummy_time_string)
|
259
|
+
return nil if time_hash[:hour].nil?
|
260
|
+
new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# convert something to a boolean
|
265
|
+
def value_to_boolean(value)
|
266
|
+
if value.is_a?(String) && value.empty?
|
267
|
+
nil
|
268
|
+
else
|
269
|
+
TRUE_VALUES.include?(value)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# Used to convert values to integer.
|
274
|
+
def value_to_integer(value)
|
275
|
+
case value
|
276
|
+
when TrueClass, FalseClass
|
277
|
+
value ? 1 : 0
|
278
|
+
else
|
279
|
+
value.to_i rescue nil
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# convert something to a BigDecimal
|
284
|
+
def value_to_decimal(value)
|
285
|
+
# Using .class is faster than .is_a? and
|
286
|
+
# subclasses of BigDecimal will be handled
|
287
|
+
# in the else clause
|
288
|
+
if value.class == BigDecimal
|
289
|
+
value
|
290
|
+
elsif value.respond_to?(:to_d)
|
291
|
+
value.to_d
|
292
|
+
else
|
293
|
+
value.to_s.to_d
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# convert something to a TimeUuid
|
298
|
+
def value_to_uuid(value)
|
299
|
+
if value.is_a?(::Cql::Uuid)
|
300
|
+
value
|
301
|
+
else
|
302
|
+
::Cql::TimeUuid.new(value) rescue nil
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
protected
|
307
|
+
# '0.123456' -> 123456
|
308
|
+
# '1.123456' -> 123456
|
309
|
+
def microseconds(time)
|
310
|
+
time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0
|
311
|
+
end
|
312
|
+
|
313
|
+
def new_date(year, mon, mday)
|
314
|
+
if year && year != 0
|
315
|
+
Date.new(year, mon, mday) rescue nil
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
|
320
|
+
# Treat 0000-00-00 00:00:00 as nil.
|
321
|
+
return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)
|
322
|
+
|
323
|
+
if offset
|
324
|
+
time = Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
|
325
|
+
return nil unless time
|
326
|
+
|
327
|
+
time -= offset
|
328
|
+
Base.default_timezone == :utc ? time : time.getlocal
|
329
|
+
else
|
330
|
+
Time.public_send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
def fast_string_to_date(string)
|
335
|
+
if string =~ Format::ISO_DATE
|
336
|
+
new_date $1.to_i, $2.to_i, $3.to_i
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
# Doesn't handle time zones.
|
341
|
+
def fast_string_to_time(string)
|
342
|
+
if string =~ Format::ISO_DATETIME
|
343
|
+
microsec = ($7.to_r * 1_000_000).to_i
|
344
|
+
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def fallback_string_to_date(string)
|
349
|
+
new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
|
350
|
+
end
|
351
|
+
|
352
|
+
def fallback_string_to_time(string)
|
353
|
+
time_hash = Date._parse(string)
|
354
|
+
time_hash[:sec_fraction] = microseconds(time_hash)
|
355
|
+
|
356
|
+
new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
private
|
361
|
+
def compute_cql_type(field_type, options)
|
362
|
+
options[:cql_type] || case field_type.to_sym
|
363
|
+
when :integer then 'int'
|
364
|
+
when :time, :date, :timestamp, :datetime then 'timestamp'
|
365
|
+
when :binary then 'blob'
|
366
|
+
when :list then "list<#{compute_cql_type(options[:holds], options)}>"
|
367
|
+
when :set then "set<#{compute_cql_type(options[:holds], options)}>"
|
368
|
+
when :map then "map<text, #{compute_cql_type(options[:holds], options)}>"
|
369
|
+
when :string then 'text'
|
370
|
+
else field_type.to_s
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
def compute_solr_type(field_type, options)
|
375
|
+
options[:solr_type] || case field_type.to_sym
|
376
|
+
when :integer then 'int'
|
377
|
+
when :decimal then 'double'
|
378
|
+
when :timestamp, :time, :datetime then 'date'
|
379
|
+
when :list, :set, :map then compute_solr_type(options[:holds], options)
|
380
|
+
else field_type.to_s
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# require 'datastax_rails/rsolr_client_wrapper'
|
2
2
|
require 'rsolr/client_cert'
|
3
3
|
require 'rest_client'
|
4
|
-
require
|
4
|
+
require 'cql'
|
5
|
+
|
5
6
|
module DatastaxRails
|
6
7
|
# The connection module holds all the code for establishing and maintaining a connection to
|
7
8
|
# Datastax Exterprise. This includes both the Cassandra and Solr connections.
|
@@ -27,12 +28,7 @@ module DatastaxRails
|
|
27
28
|
#
|
28
29
|
# @return [String] the hostname or ip address of the current server
|
29
30
|
def current_server
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
# Returns the thrift client object
|
34
|
-
def thrift_client
|
35
|
-
self.connection.instance_variable_get(:@connection)
|
31
|
+
connection.current_connection.instance_variable_get(:@connection).host
|
36
32
|
end
|
37
33
|
|
38
34
|
# Establish a Cassandra connection to DSE. datastax.yml will be read and the current environment's
|
@@ -41,14 +37,13 @@ module DatastaxRails
|
|
41
37
|
# The following is an example production configuration document. Assume that your setup consists
|
42
38
|
# of three datacenters each with three servers and RF=3 (i.e., you're storing your data 9 times)
|
43
39
|
#
|
44
|
-
# servers: ["10.1.2.5
|
40
|
+
# servers: ["10.1.2.5"]
|
41
|
+
# port: 9042
|
45
42
|
# keyspace: "datastax_rails_production"
|
46
43
|
# strategy_class: "org.apache.cassandra.locator.NetworkTopologyStrategy"
|
47
44
|
# strategy_options: {"DS1": "3", "DS2": "3", "DS3": "3"}
|
48
45
|
# connection_options:
|
49
46
|
# timeout: 10
|
50
|
-
# retries: 2
|
51
|
-
# server_max_requests: 1000
|
52
47
|
# solr:
|
53
48
|
# port: 8983
|
54
49
|
# path: /solr
|
@@ -58,8 +53,12 @@ module DatastaxRails
|
|
58
53
|
# key: config/datastax_rails.key
|
59
54
|
# keypass: changeme
|
60
55
|
#
|
61
|
-
# The +servers+ entry should be a list of all
|
62
|
-
#
|
56
|
+
# The +servers+ entry should be a list of all seed nodes for servers you wish to connect to. DSR
|
57
|
+
# will automatically connect to all nodes in the cluster or in the datacenter if you are using multiple
|
58
|
+
# datacenters. You can safely just list all nodes in a particular datacenter if you would like.
|
59
|
+
#
|
60
|
+
# The port to connect to, this port will be used for all nodes. Because the `system.peers` table does not contain the port that the nodes are
|
61
|
+
# listening on, the port must be the same for all nodes.
|
63
62
|
#
|
64
63
|
# Since we're using the NetworkTopologyStrategy for our locator, it is important that you configure
|
65
64
|
# cassandra-topology.properties. See the DSE documentation at http://www.datastax.com for more
|
@@ -94,7 +93,7 @@ module DatastaxRails
|
|
94
93
|
DatastaxRails::Base.config = spec.with_indifferent_access
|
95
94
|
spec.reverse_merge!(DEFAULT_OPTIONS)
|
96
95
|
connection_options = spec[:connection_options] || {}
|
97
|
-
self.connection =
|
96
|
+
self.connection = ::Cql::Client.connect(:hosts => spec[:servers], :keyspace => spec[:keyspace], :connection_timeout => spec[:connection_options][:timeout])
|
98
97
|
end
|
99
98
|
|
100
99
|
# Returns the base portion of the URL for connecting to SOLR based on the current Cassandra server.
|