datastax_rails 1.1.0.3 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.rdoc +13 -13
- data/Rakefile +1 -0
- data/config/schema.xml.erb +0 -1
- data/config/{solrconfig.xml → solrconfig.xml.erb} +1 -1
- data/lib/blankslate.rb +1 -1
- data/lib/datastax_rails/associations/collection_proxy.rb +6 -2
- data/lib/datastax_rails/attribute_assignment.rb +114 -0
- data/lib/datastax_rails/attribute_methods/definition.rb +8 -2
- data/lib/datastax_rails/attribute_methods/typecasting.rb +2 -5
- data/lib/datastax_rails/attribute_methods.rb +9 -7
- data/lib/datastax_rails/base.rb +127 -109
- data/lib/datastax_rails/callbacks.rb +11 -7
- data/lib/datastax_rails/cassandra_only_model.rb +27 -0
- data/lib/datastax_rails/collection.rb +3 -1
- data/lib/datastax_rails/cql/base.rb +4 -0
- data/lib/datastax_rails/cql/select.rb +12 -2
- data/lib/datastax_rails/identity/abstract_key_factory.rb +1 -0
- data/lib/datastax_rails/identity/custom_key_factory.rb +1 -0
- data/lib/datastax_rails/identity/natural_key_factory.rb +1 -0
- data/lib/datastax_rails/identity/uuid_key_factory.rb +4 -0
- data/lib/datastax_rails/identity.rb +2 -1
- data/lib/datastax_rails/inheritance.rb +61 -0
- data/lib/datastax_rails/payload_model.rb +2 -5
- data/lib/datastax_rails/persistence.rb +63 -20
- data/lib/datastax_rails/railtie.rb +5 -1
- data/lib/datastax_rails/relation/batches.rb +2 -2
- data/lib/datastax_rails/relation/facet_methods.rb +56 -5
- data/lib/datastax_rails/relation/finder_methods.rb +55 -1
- data/lib/datastax_rails/relation/search_methods.rb +103 -32
- data/lib/datastax_rails/relation/spawn_methods.rb +3 -1
- data/lib/datastax_rails/relation/stats_methods.rb +1 -1
- data/lib/datastax_rails/relation.rb +166 -30
- data/lib/datastax_rails/schema/cassandra.rb +165 -0
- data/lib/datastax_rails/schema/migrator.rb +85 -193
- data/lib/datastax_rails/schema/solr.rb +158 -0
- data/lib/datastax_rails/schema.rb +2 -30
- data/lib/datastax_rails/scoping/default.rb +142 -0
- data/lib/datastax_rails/scoping/named.rb +200 -0
- data/lib/datastax_rails/scoping.rb +106 -349
- data/lib/datastax_rails/tasks/ds.rake +41 -42
- data/lib/datastax_rails/types/array_type.rb +1 -1
- data/lib/datastax_rails/types/base_type.rb +2 -2
- data/lib/datastax_rails/types/binary_type.rb +1 -1
- data/lib/datastax_rails/types/boolean_type.rb +1 -1
- data/lib/datastax_rails/types/date_type.rb +1 -1
- data/lib/datastax_rails/types/float_type.rb +4 -4
- data/lib/datastax_rails/types/integer_type.rb +3 -3
- data/lib/datastax_rails/types/string_type.rb +1 -1
- data/lib/datastax_rails/types/text_type.rb +1 -1
- data/lib/datastax_rails/types/time_type.rb +3 -3
- data/lib/datastax_rails/validations/uniqueness.rb +1 -1
- data/lib/datastax_rails/version.rb +1 -1
- data/lib/datastax_rails/wide_storage_model.rb +44 -0
- data/lib/datastax_rails.rb +16 -18
- data/spec/datastax_rails/associations_spec.rb +7 -3
- data/spec/datastax_rails/attribute_methods_spec.rb +23 -0
- data/spec/datastax_rails/base_spec.rb +1 -6
- data/spec/datastax_rails/inheritance_spec.rb +41 -0
- data/spec/datastax_rails/persistence_spec.rb +13 -3
- data/spec/datastax_rails/relation/batches_spec.rb +1 -1
- data/spec/datastax_rails/relation/facet_methods_spec.rb +52 -0
- data/spec/datastax_rails/relation/finder_methods_spec.rb +22 -1
- data/spec/datastax_rails/relation/search_methods_spec.rb +51 -1
- data/spec/datastax_rails/relation_spec.rb +14 -3
- data/spec/datastax_rails/schema/migrator_spec.rb +92 -0
- data/spec/datastax_rails/schema/solr_spec.rb +34 -0
- data/spec/datastax_rails/scoping/default_spec.rb +17 -0
- data/spec/datastax_rails/types/float_type_spec.rb +5 -9
- data/spec/datastax_rails/types/integer_type_spec.rb +5 -9
- data/spec/datastax_rails/types/time_type_spec.rb +28 -0
- data/spec/datastax_rails/validations/uniqueness_spec.rb +3 -1
- data/spec/dummy/config/application.rb +1 -4
- data/spec/dummy/config/datastax.yml +1 -1
- data/spec/dummy/config/environments/test.rb +2 -0
- data/spec/dummy/config/solr/articles-schema.xml.erb +1 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +2 -0
- data/spec/dummy/log/production.log +2 -0
- data/spec/dummy/log/test.log +523 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/models.rb +14 -0
- metadata +66 -22
- data/lib/datastax_rails/log_subscriber.rb +0 -37
- data/lib/datastax_rails/migrations/migration.rb +0 -15
- data/lib/datastax_rails/migrations.rb +0 -36
- data/lib/datastax_rails/mocking.rb +0 -15
- data/lib/datastax_rails/schema/migration.rb +0 -106
- data/lib/datastax_rails/schema/migration_proxy.rb +0 -25
- data/lib/datastax_rails/tasks/column_family.rb +0 -329
- data/lib/datastax_rails/tasks/keyspace.rb +0 -57
- data/spec/support/connection_double.rb +0 -6
@@ -1,6 +1,23 @@
|
|
1
1
|
module DatastaxRails
|
2
2
|
module SearchMethods
|
3
3
|
|
4
|
+
# By default, Cassandra will throw an error if you try to set a where condition
|
5
|
+
# on either a column with no index or on more than one column that isn't part
|
6
|
+
# of the primary key. If you are confident that the number of records that need
|
7
|
+
# to be searched is low, then you can instruct it to ignore the warning. Generally
|
8
|
+
# you only want to do this when either the number of records in the table is very
|
9
|
+
# small or when one of the other where conditions that has an index will reduce the
|
10
|
+
# number of records to a small number.
|
11
|
+
#
|
12
|
+
# Model.where(:name => 'johndoe', :active => true).allow_filtering
|
13
|
+
#
|
14
|
+
# @return [DatastaxRails::Relation] a new Relation object
|
15
|
+
def allow_filtering
|
16
|
+
clone.tap do |r|
|
17
|
+
r.allow_filtering_value = true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
4
21
|
# The default consistency level for DSR is QUORUM when searching by ID.
|
5
22
|
# For all searches using SOLR, the default consistency is ONE. Use this
|
6
23
|
# to override it in either case.
|
@@ -157,15 +174,41 @@ module DatastaxRails
|
|
157
174
|
# Model.order(:name)
|
158
175
|
# Model.order(:name => :desc)
|
159
176
|
#
|
177
|
+
# WARNING: If this call is combined with #with_cassandra, you can only
|
178
|
+
# order on the clustering column of the primary key. If this doesn't
|
179
|
+
# mean anything to you, then you probably don't want to use these together.
|
180
|
+
#
|
160
181
|
# @param attribute [Symbol, String, Hash] the attribute to sort by and optionally the direction to sort in
|
161
182
|
# @return [DatastaxRails::Relation] a new Relation object
|
162
183
|
def order(attribute)
|
163
184
|
return self if attribute.blank?
|
164
185
|
|
165
186
|
clone.tap do |r|
|
166
|
-
|
167
|
-
|
168
|
-
|
187
|
+
r.order_values << (attribute.is_a?(Hash) ? attribute : {attribute.to_sym => :asc})
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Orders the result set in memory after all matching records have been
|
192
|
+
# retrieved.
|
193
|
+
#
|
194
|
+
# This means that limit is ignored until the end. ALL matching records WILL
|
195
|
+
# be retrieved and sorted before taking #limit records and returning them to
|
196
|
+
# the caller.
|
197
|
+
#
|
198
|
+
# Why would you do this? If you are retrieving records from a cassandra index
|
199
|
+
# but don't have the appropriate clustering order you can use this, but you should
|
200
|
+
# only do so if you are confident that the number of records returned will be low.
|
201
|
+
#
|
202
|
+
# A warning will be printed to the log if this results in a very inefficient operation.
|
203
|
+
#
|
204
|
+
# USE WITH CARE!!!!!!
|
205
|
+
#
|
206
|
+
# @param attribute [Symbol, String, Hash] the attribute to sort by and optionally the direction to sort in
|
207
|
+
# @return [DatastaxRails::Relation] a new Relation object
|
208
|
+
def slow_order(attribute)
|
209
|
+
return self if attribute.blank?
|
210
|
+
clone.tap do |r|
|
211
|
+
r.slow_order_values << (attribute.is_a?(Hash) ? attribute : {attribute.to_sym => :asc})
|
169
212
|
end
|
170
213
|
end
|
171
214
|
|
@@ -404,15 +447,12 @@ module DatastaxRails
|
|
404
447
|
# You can also pass in an options hash with the following options:
|
405
448
|
#
|
406
449
|
# * :fields => list of fields to search instead of the default of all fields
|
407
|
-
# * :highlight => List of fields to retrieve highlights for. Note that highlighted fields *must* be +:stored+
|
408
450
|
#
|
409
451
|
# Model.fulltext("john smith", :fields => [:title])
|
410
|
-
# Model.fulltext("john smith", :hightlight => [:body])
|
411
452
|
#
|
412
453
|
# @param query [String] a fulltext query to pass to solr
|
413
454
|
# @param opts [Hash] an optional options hash to modify the fulltext query
|
414
455
|
# @option opts [Array] :fields list of fields to search instead of the default of all text fields (not-implemented)
|
415
|
-
# @option opts [Array] :highlight list of fields to retrieve highlights for (not-implemented)
|
416
456
|
# @return [DatastaxRails::Relation] a new Relation object
|
417
457
|
def fulltext(query, opts = {})
|
418
458
|
return self if query.blank?
|
@@ -424,6 +464,56 @@ module DatastaxRails
|
|
424
464
|
end
|
425
465
|
end
|
426
466
|
|
467
|
+
# Enables highlighting on specific fields when used with full
|
468
|
+
# text searching. In order for highlighting to work, the highlighted
|
469
|
+
# field(s) *must* be +:stored+
|
470
|
+
#
|
471
|
+
# Model.fulltext("ruby on rails").highlight(:tags, :body)
|
472
|
+
# Model.fulltext("pizza").highlight(:description, snippets: 3, fragsize: 150)
|
473
|
+
#
|
474
|
+
# In addition to the array of field names to highlight, you can pass in an
|
475
|
+
# options hash with the following options:
|
476
|
+
#
|
477
|
+
# * :snippets => number of highlight snippets to return
|
478
|
+
# * :fragsize => number of characters for each snippet length
|
479
|
+
# * :pre_tag => text which appears before a highlighted term
|
480
|
+
# * :post_tag => text which appears after a highlighted term
|
481
|
+
# * :merge_contiguous => collapse contiguous fragments into a single fragment
|
482
|
+
# * :use_fast_vector => enables the Solr FastVectorHighlighter
|
483
|
+
#
|
484
|
+
# Note: When enabling +:use_fast_vector+, the highlighted fields must be also have
|
485
|
+
# +:term_vectors+, +:term_positions+, and +:term_offsets+ enabled.
|
486
|
+
# For more information about these options, refer to Solr's wiki
|
487
|
+
# on HighlightingParameters[http://http://wiki.apache.org/solr/HighlightingParameters].
|
488
|
+
#
|
489
|
+
# @overload highlight(*args, opts)
|
490
|
+
# Highlights the full text search terms for the specified fields with the
|
491
|
+
# given options
|
492
|
+
# @param [Array] args list of field names to be highlighted
|
493
|
+
# @param [Hash] opts an options hash to configure the Solr highlighter
|
494
|
+
# @option opts [Integer] :snippets number of highlighted snippets to return
|
495
|
+
# @option opts [Integer] :fragsize number of characters for each snippet length
|
496
|
+
# @option opts [String] :pre_tag text which appears before a highlighted term
|
497
|
+
# @option opts [String] :post_tag text which appears after a highlighted term
|
498
|
+
# @option opts [true, false] :merge_contiguous collapse contiguous fragments into a single fragment
|
499
|
+
# @option opts [true, false] :use_fast_vector enables the Solr FastVectorHighlighter
|
500
|
+
# @return [DatastaxRails::Relation] a new Relation object
|
501
|
+
# @overload highlight(*args)
|
502
|
+
# Highlights the full text search terms for the specified fields
|
503
|
+
# @param [Array] args list of field names to be highlighted
|
504
|
+
# @return [DatastaxRails::Relation] a new Relation object
|
505
|
+
def highlight(*args)
|
506
|
+
return self if args.blank?
|
507
|
+
|
508
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
509
|
+
|
510
|
+
clone.tap do |r|
|
511
|
+
opts[:fields] = r.highlight_options[:fields] || []
|
512
|
+
opts[:fields] |= args # Union unique field names
|
513
|
+
r.highlight_options.merge! opts
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
427
517
|
# @see where
|
428
518
|
def less_than(value)
|
429
519
|
raise ArgumentError, "#less_than can only be called after an appropriate where call. e.g. where(:created_at).less_than(1.day.ago)"
|
@@ -438,10 +528,14 @@ module DatastaxRails
|
|
438
528
|
def solr_format(value)
|
439
529
|
return value unless use_solr_value
|
440
530
|
case
|
441
|
-
when value.is_a?(
|
442
|
-
value.strftime(
|
531
|
+
when value.is_a?(Time)
|
532
|
+
value.utc.strftime(DatastaxRails::Types::TimeType::FORMAT)
|
533
|
+
when value.is_a?(DateTime)
|
534
|
+
value.to_time.utc.strftime(DatastaxRails::Types::TimeType::FORMAT)
|
535
|
+
when value.is_a?(Date)
|
536
|
+
value.strftime(DatastaxRails::Types::TimeType::FORMAT)
|
443
537
|
when value.is_a?(Array)
|
444
|
-
value.collect {|v| v.gsub(/ /,"\\ ") }.join(" OR ")
|
538
|
+
value.collect {|v| v.to_s.gsub(/ /,"\\ ") }.join(" OR ")
|
445
539
|
when value.is_a?(Fixnum)
|
446
540
|
value < 0 ? "\\#{value}" : value
|
447
541
|
when value.is_a?(Range)
|
@@ -455,29 +549,6 @@ module DatastaxRails
|
|
455
549
|
end
|
456
550
|
end
|
457
551
|
|
458
|
-
protected
|
459
|
-
def find_by_attributes(match, attributes, *args) #:nodoc:
|
460
|
-
conditions = {}
|
461
|
-
Hash[attributes.map {|a| [a, args[attributes.index(a)]]}].each do |k,v|
|
462
|
-
if(v.is_a?(String))
|
463
|
-
conditions[k] = v.gsub(/(\W)/, '\\\\\1')
|
464
|
-
else
|
465
|
-
conditions[k] = v
|
466
|
-
end
|
467
|
-
end
|
468
|
-
|
469
|
-
self.where_values << conditions
|
470
|
-
result = self.send(match.finder)
|
471
|
-
#result = where(conditions).send(match.finder)
|
472
|
-
|
473
|
-
if match.blank? && result.blank?
|
474
|
-
raise RecordNotFound, "Couldn't find #{klass.name} with #{conditions.to_a.collect {|p| p.join('=')}.join(', ')}"
|
475
|
-
else
|
476
|
-
yield(result) if block_given?
|
477
|
-
result
|
478
|
-
end
|
479
|
-
end
|
480
|
-
|
481
552
|
class WhereProxy #:nodoc:
|
482
553
|
def initialize(relation, attribute, invert = false) #:nodoc:
|
483
554
|
@relation, @attribute, @invert = relation, attribute, invert
|
@@ -10,6 +10,8 @@ module DatastaxRails
|
|
10
10
|
|
11
11
|
merged_relation = clone
|
12
12
|
|
13
|
+
r = r.with_default_scope if r.default_scoped? && r.klass != klass
|
14
|
+
|
13
15
|
(Relation::MULTI_VALUE_METHODS - [:where, :where_not]).each do |method|
|
14
16
|
value = r.send(:"#{method}_values")
|
15
17
|
merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present?
|
@@ -31,7 +33,7 @@ module DatastaxRails
|
|
31
33
|
|
32
34
|
(Relation::SINGLE_VALUE_METHODS).each do |method|
|
33
35
|
value = r.send(:"#{method}_value")
|
34
|
-
merged_relation.send(:"#{method}_value=", value) unless value.nil?
|
36
|
+
merged_relation.send(:"#{method}_value=", value) unless value.nil? || value == :default
|
35
37
|
end
|
36
38
|
|
37
39
|
merged_relation
|
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'rsolr'
|
2
|
+
require 'pp' if ENV['DEBUG_SOLR'] == 'true'
|
2
3
|
|
3
4
|
module DatastaxRails
|
4
5
|
class Relation
|
5
|
-
MULTI_VALUE_METHODS = [:order, :where, :where_not, :fulltext, :greater_than, :less_than, :select, :stats]
|
6
|
-
SINGLE_VALUE_METHODS = [:page, :per_page, :reverse_order, :query_parser, :consistency, :ttl, :use_solr, :escape, :group]
|
6
|
+
MULTI_VALUE_METHODS = [:order, :where, :where_not, :fulltext, :greater_than, :less_than, :select, :stats, :field_facet, :range_facet, :slow_order]
|
7
|
+
SINGLE_VALUE_METHODS = [:page, :per_page, :reverse_order, :query_parser, :consistency, :ttl, :use_solr, :escape, :group, :allow_filtering]
|
7
8
|
|
8
9
|
SOLR_CHAR_RX = /([\+\!\(\)\[\]\^\"\~\:\'\=\/]+)/
|
9
10
|
|
@@ -14,6 +15,7 @@ module DatastaxRails
|
|
14
15
|
attr_accessor :"#{m}_value"
|
15
16
|
end
|
16
17
|
attr_accessor :create_with_value, :default_scoped
|
18
|
+
attr_accessor :highlight_options
|
17
19
|
|
18
20
|
include SearchMethods
|
19
21
|
include ModificationMethods
|
@@ -21,6 +23,7 @@ module DatastaxRails
|
|
21
23
|
include SpawnMethods
|
22
24
|
include StatsMethods
|
23
25
|
include Batches
|
26
|
+
include FacetMethods
|
24
27
|
|
25
28
|
attr_reader :klass, :column_family, :loaded, :cql
|
26
29
|
alias :loaded? :loaded
|
@@ -41,14 +44,14 @@ module DatastaxRails
|
|
41
44
|
|
42
45
|
SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)}
|
43
46
|
MULTI_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_values", [])}
|
47
|
+
@highlight_options = {}
|
44
48
|
@per_page_value = @klass.default_page_size
|
45
49
|
@page_value = 1
|
46
|
-
@use_solr_value =
|
50
|
+
@use_solr_value = :default
|
47
51
|
@extensions = []
|
48
52
|
@create_with_value = {}
|
49
|
-
@escape_value =
|
53
|
+
@escape_value = :default
|
50
54
|
@stats = {}
|
51
|
-
apply_default_scope
|
52
55
|
end
|
53
56
|
|
54
57
|
# Returns true if the two relations have the same query parameters
|
@@ -87,11 +90,8 @@ module DatastaxRails
|
|
87
90
|
# matching documents
|
88
91
|
#
|
89
92
|
# Compare with #size.
|
90
|
-
#
|
91
|
-
# XXX: Count via CQL is useless unless criteria has been applied.
|
92
|
-
# Otherwise you get everything that has ever been in the CF.
|
93
93
|
def count
|
94
|
-
@count ||=
|
94
|
+
@count ||= (with_default_scope.path_decision == :solr) ? with_default_scope.count_via_solr : with_default_scope.count_via_cql
|
95
95
|
end
|
96
96
|
|
97
97
|
def stats
|
@@ -118,10 +118,16 @@ module DatastaxRails
|
|
118
118
|
|
119
119
|
# Gets a default scope with no conditions or search attributes set.
|
120
120
|
def default_scope
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
121
|
+
klass.scoped.with_default_scope
|
122
|
+
end
|
123
|
+
|
124
|
+
def with_default_scope #:nodoc:
|
125
|
+
if default_scoped? && default_scope = klass.send(:build_default_scope)
|
126
|
+
default_scope = default_scope.merge(self)
|
127
|
+
default_scope.default_scoped = false
|
128
|
+
default_scope
|
129
|
+
else
|
130
|
+
self
|
125
131
|
end
|
126
132
|
end
|
127
133
|
|
@@ -207,11 +213,11 @@ module DatastaxRails
|
|
207
213
|
# Returns a standard array thus no more methods may be chained.
|
208
214
|
def to_a
|
209
215
|
return @results if loaded?
|
210
|
-
if
|
211
|
-
@results = query_via_solr
|
216
|
+
if with_default_scope.path_decision == :solr
|
217
|
+
@results = with_default_scope.query_via_solr
|
212
218
|
@count = @group_value ? @results.total_for_all : @results.total_entries
|
213
219
|
else
|
214
|
-
@results = query_via_cql
|
220
|
+
@results = with_default_scope.query_via_cql
|
215
221
|
end
|
216
222
|
@loaded = true
|
217
223
|
@results
|
@@ -236,16 +242,56 @@ module DatastaxRails
|
|
236
242
|
super
|
237
243
|
end
|
238
244
|
|
239
|
-
|
240
|
-
|
241
|
-
|
245
|
+
def path_decision
|
246
|
+
return :cassandra if klass <= DatastaxRails::CassandraOnlyModel
|
247
|
+
case use_solr_value
|
248
|
+
when false
|
249
|
+
return :cassandra
|
250
|
+
when true
|
251
|
+
return :solr
|
252
|
+
else
|
253
|
+
# If we've already decided to use cassandra, just go with it.
|
254
|
+
return :cassandra unless use_solr_value
|
255
|
+
[order_values, where_not_values, fulltext_values, greater_than_values, less_than_values, field_facet_values,
|
256
|
+
range_facet_values, group_value].each do |solr_only_stuff|
|
257
|
+
return :solr unless solr_only_stuff.blank?
|
258
|
+
end
|
259
|
+
return :solr unless group_value.blank?
|
260
|
+
return :solr unless page_value == 1
|
261
|
+
@where_values.each do |wv|
|
262
|
+
wv.each do |k,v|
|
263
|
+
next if k.to_sym == :id
|
264
|
+
if(klass.attribute_definitions[k].indexed == :solr || !klass.attribute_definitions[k].indexed)
|
265
|
+
return :solr
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
# If we get here, we can safely run this query via Cassandra
|
270
|
+
return :cassandra
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# If we index something into both cassandra and solr, we rename the cassandra
|
275
|
+
# column. This method maps the column names as necessary
|
276
|
+
def map_cassandra_columns(conditions)
|
277
|
+
{}.tap do |mapped|
|
278
|
+
conditions.each do |k,v|
|
279
|
+
if(klass.attribute_definitions[k].indexed == :both)
|
280
|
+
mapped["__#{k}"] = v
|
281
|
+
else
|
282
|
+
mapped[k] = v
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
242
288
|
def count_via_cql
|
243
|
-
|
244
|
-
cql = @cql.select(select_columns)
|
289
|
+
cql = @cql.select(['count(*)'])
|
245
290
|
cql.using(@consistency_value) if @consistency_value
|
246
291
|
@where_values.each do |wv|
|
247
292
|
cql.conditions(wv)
|
248
293
|
end
|
294
|
+
cql.allow_filtering if @allow_filtering_value
|
249
295
|
CassandraCQL::Result.new(cql.execute).fetch['count']
|
250
296
|
end
|
251
297
|
|
@@ -254,10 +300,10 @@ module DatastaxRails
|
|
254
300
|
# For ad-hoc queries, you will have to use Solr.
|
255
301
|
def query_via_cql
|
256
302
|
select_columns = select_values.empty? ? (@klass.attribute_definitions.keys - @klass.lazy_attributes) : select_values.flatten
|
257
|
-
cql = @cql.select(select_columns +
|
303
|
+
cql = @cql.select((select_columns + @klass.key_factory.key_columns).uniq)
|
258
304
|
cql.using(@consistency_value) if @consistency_value
|
259
305
|
@where_values.each do |wv|
|
260
|
-
cql.conditions(wv)
|
306
|
+
cql.conditions(Hash[wv.map {|k,v| [(k.to_sym == :id ? :key : k), v]}])
|
261
307
|
end
|
262
308
|
@greater_than_values.each do |gtv|
|
263
309
|
gtv.each do |k,v|
|
@@ -270,13 +316,47 @@ module DatastaxRails
|
|
270
316
|
if(@per_page_value)
|
271
317
|
cql.limit(@per_page_value)
|
272
318
|
end
|
319
|
+
cql.allow_filtering if @allow_filtering_value
|
273
320
|
results = []
|
274
|
-
|
275
|
-
|
321
|
+
begin
|
322
|
+
CassandraCQL::Result.new(cql.execute).fetch do |row|
|
323
|
+
results << @klass.instantiate(row['key'], row.to_hash, select_columns)
|
324
|
+
end
|
325
|
+
rescue CassandraCQL::Error::InvalidRequestException => e
|
326
|
+
# If we get an exception about an empty key, ignore it. We'll return an empty set.
|
327
|
+
if e.message =~ /Key may not be empty/
|
328
|
+
# No-Op
|
329
|
+
else
|
330
|
+
raise
|
331
|
+
end
|
332
|
+
end
|
333
|
+
if(@slow_order_values.any?)
|
334
|
+
results.sort! do |a,b|
|
335
|
+
values = slow_ordering(a,b)
|
336
|
+
values[0] <=> values[1]
|
337
|
+
end
|
276
338
|
end
|
277
339
|
results
|
278
340
|
end
|
279
341
|
|
342
|
+
def slow_ordering(obj1, obj2)
|
343
|
+
[[],[]].tap do |values|
|
344
|
+
i=0
|
345
|
+
@slow_order_values.each do |ordering|
|
346
|
+
ordering.each do |k,v|
|
347
|
+
if v == :asc
|
348
|
+
values[0][i] = obj1.send(k)
|
349
|
+
values[1][i] = obj2.send(k)
|
350
|
+
else
|
351
|
+
values[1][i] = obj1.send(k)
|
352
|
+
values[0][i] = obj2.send(k)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
i += 1
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
280
360
|
# Runs the query with a limit of 1 just to grab the total results attribute off
|
281
361
|
# the result set.
|
282
362
|
def count_via_solr
|
@@ -354,10 +434,9 @@ module DatastaxRails
|
|
354
434
|
q = "*:*"
|
355
435
|
else
|
356
436
|
q = @fulltext_values.collect {|ftv| "(" + ftv[:query] + ")"}.join(' AND ')
|
437
|
+
hl_fields = @fulltext_values.collect { |ftv| ftv[:highlight].join(",") if ftv[:highlight].present? }.join(",")
|
357
438
|
end
|
358
439
|
|
359
|
-
#TODO highlighting and fielded queries of fulltext
|
360
|
-
|
361
440
|
params = {:q => q}
|
362
441
|
unless sort.empty?
|
363
442
|
params[:sort] = sort
|
@@ -366,6 +445,54 @@ module DatastaxRails
|
|
366
445
|
unless filter_queries.empty?
|
367
446
|
params[:fq] = filter_queries
|
368
447
|
end
|
448
|
+
|
449
|
+
# Facets
|
450
|
+
# facet=true to enable faceting, facet.field=<field_name> (can appear more than once for multiple fields)
|
451
|
+
# Additional options: f.<field_name>.facet.<option> [e.g. f.author.facet.sort=index]
|
452
|
+
|
453
|
+
# Facet Fields
|
454
|
+
unless field_facet_values.empty?
|
455
|
+
params['facet'] = 'true'
|
456
|
+
facet_fields = []
|
457
|
+
field_facet_values.each do |facet|
|
458
|
+
facet_field = facet[:field]
|
459
|
+
facet_fields << facet_field
|
460
|
+
facet[:options].each do |key,value|
|
461
|
+
params["f.#{facet_field}.facet.#{key}"] = value.to_s
|
462
|
+
end
|
463
|
+
end
|
464
|
+
params['facet.field'] = facet_fields
|
465
|
+
end
|
466
|
+
|
467
|
+
# Facet Ranges
|
468
|
+
unless range_facet_values.empty?
|
469
|
+
params['facet'] = 'true'
|
470
|
+
facet_fields = []
|
471
|
+
range_facet_values.each do |facet|
|
472
|
+
facet_field = facet[:field]
|
473
|
+
facet_fields << facet_field
|
474
|
+
facet[:options].each do |key,value|
|
475
|
+
params["f.#{facet_field}.facet.range.#{key}"] = value.to_s
|
476
|
+
end
|
477
|
+
end
|
478
|
+
params['facet.range'] = facet_fields
|
479
|
+
end
|
480
|
+
|
481
|
+
if @highlight_options[:fields].present?
|
482
|
+
params[:hl] = true
|
483
|
+
params['hl.fl'] = @highlight_options[:fields]
|
484
|
+
params['hl.snippets'] = @highlight_options[:snippets] if @highlight_options[:snippets]
|
485
|
+
params['hl.fragsize'] = @highlight_options[:fragsize] if @highlight_options[:fragsize]
|
486
|
+
if @highlight_options[:use_fast_vector]
|
487
|
+
params['hl.useFastVectorHighlighter'] = true
|
488
|
+
params['hl.tag.pre'] = @highlight_options[:pre_tag] if @highlight_options[:pre_tag].present?
|
489
|
+
params['hl.tag.post'] = @highlight_options[:post_tag] if @highlight_options[:post_tag].present?
|
490
|
+
else
|
491
|
+
params['hl.mergeContiguous'] = !!@highlight_options[:merge_contiguous]
|
492
|
+
params['hl.simple.pre'] = @highlight_options[:pre_tag] if @highlight_options[:pre_tag].present?
|
493
|
+
params['hl.simple.post'] = @highlight_options[:post_tag] if @highlight_options[:post_tag].present?
|
494
|
+
end
|
495
|
+
end
|
369
496
|
|
370
497
|
select_columns = select_values.empty? ? (@klass.attribute_definitions.keys - @klass.lazy_attributes) : select_values.flatten
|
371
498
|
select_columns << "id"
|
@@ -387,10 +514,10 @@ module DatastaxRails
|
|
387
514
|
params['group.field'] = @group_value
|
388
515
|
params['group.limit'] = @per_page_value
|
389
516
|
params['group.offset'] = (@page_value - 1) * @per_page_value
|
390
|
-
params['group.ngroups'] = '
|
517
|
+
params['group.ngroups'] = 'false' # must be false due to issues with solr sharding
|
391
518
|
solr_response = rsolr.post('select', :data => params)
|
392
519
|
response = solr_response["grouped"][@group_value.to_s]
|
393
|
-
results.total_groups = response['
|
520
|
+
results.total_groups = response['groups'].size
|
394
521
|
results.total_for_all = response['matches'].to_i
|
395
522
|
results.total_entries = 0
|
396
523
|
response['groups'].each do |group|
|
@@ -400,11 +527,19 @@ module DatastaxRails
|
|
400
527
|
else
|
401
528
|
solr_response = rsolr.paginate(@page_value, @per_page_value, 'select', :data => params, :method => :post)
|
402
529
|
response = solr_response["response"]
|
530
|
+
pp solr_response if ENV['DEBUG_SOLR'] == 'true'
|
403
531
|
results = parse_docs(response, select_columns)
|
532
|
+
results.highlights = solr_response['highlighting']
|
404
533
|
end
|
405
534
|
if solr_response["stats"]
|
406
535
|
@stats = solr_response["stats"]["stats_fields"].with_indifferent_access
|
407
536
|
end
|
537
|
+
# Apply Facets if they exist
|
538
|
+
if solr_response['facet_counts']
|
539
|
+
results.facets = {}
|
540
|
+
results.facets = results.facets.merge(solr_response['facet_counts']['facet_fields'].to_h)
|
541
|
+
results.facets = results.facets.merge(solr_response['facet_counts']['facet_ranges'].to_h)
|
542
|
+
end
|
408
543
|
pp params if ENV['DEBUG_SOLR'] == 'true'
|
409
544
|
results
|
410
545
|
end
|
@@ -426,6 +561,7 @@ module DatastaxRails
|
|
426
561
|
obj = @klass.with_cassandra.consistency(@consistency_value).find_by_id(id)
|
427
562
|
results << obj if obj
|
428
563
|
else
|
564
|
+
#byebug
|
429
565
|
results << @klass.instantiate(id, doc, select_columns)
|
430
566
|
end
|
431
567
|
end
|
@@ -493,7 +629,7 @@ module DatastaxRails
|
|
493
629
|
protected
|
494
630
|
|
495
631
|
def method_missing(method, *args, &block) #:nodoc:
|
496
|
-
if
|
632
|
+
if DatastaxRails::Collection.method_defined?(method)
|
497
633
|
to_a.send(method, *args, &block)
|
498
634
|
elsif @klass.respond_to?(method, true)
|
499
635
|
scoping { @klass.send(method, *args, &block) }
|