Class: DatastaxRails::Relation

Inherits:
Object
  • Object
show all
Includes:
Batches, FacetMethods, FinderMethods, ModificationMethods, SearchMethods, SpawnMethods, StatsMethods
Defined in:
lib/datastax_rails/relation.rb

Overview

DatastaxRails Relation

Constant Summary collapse

MULTI_VALUE_METHODS =
%i(order where where_not fulltext greater_than less_than select stats field_facet
range_facet slow_order)
SINGLE_VALUE_METHODS =
%i(page per_page reverse_order query_parser consistency ttl use_solr escape group
allow_filtering facet_threads)
SOLR_CHAR_RX =
%r{([\+\!\(\)\[\]\^\"\~\:\'\=\/]+)}
SOLR_DATE_REGEX =
/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)/i

Constants included from FacetMethods

FacetMethods::BY_DAY, FacetMethods::BY_MONTH, FacetMethods::BY_YEAR

Constants included from StatsMethods

StatsMethods::STATS_FIELDS

Constants included from SpawnMethods

SpawnMethods::VALID_FIND_OPTIONS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from FacetMethods

#facet_threads, #field_facet, #range_facet

Methods included from Batches

#find_each, #find_each_with_index, #find_in_batches

Methods included from StatsMethods

#average, #grouped_average, #grouped_maximum, #grouped_minimum, #grouped_stddev, #grouped_sum, #maximum, #minimum, #stddev, #sum

Methods included from SpawnMethods

#apply_finder_options, #except, #merge, #only

Methods included from FinderMethods

#find, #find_by, #find_by!, #first, #first!, #last, #last!

Methods included from ModificationMethods

#delete_all, #destroy, #destroy_all

Methods included from SearchMethods

#allow_filtering, #compute_stats, #consistency, #dont_escape, #extending, #fulltext, #greater_than, #group, #highlight, #less_than, #limit, #order, #page, #paginate, #query_parser, #reverse_order, #select, #slow_order, #solr_format, #where, #where_not, #with_cassandra, #with_solr

Constructor Details

#initialize(klass, column_family) ⇒ Relation

Initializes the Relation. Defaults page value to 1, per_page to the class default, and solr use to true. Everything else gets defaulted to nil or empty.

Parameters:

  • klass (Class)

    the child of DatastaxRails::Base that this relation searches

  • column_family (String, Symbol)

    the name of the column family this relation searches


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/datastax_rails/relation.rb', line 41

def initialize(klass, column_family)
  @klass, @column_family = klass, column_family
  @loaded = false
  @results = []
  @default_scoped = false
  @cql = DatastaxRails::Cql.for_class(klass)

  SINGLE_VALUE_METHODS.each { |v| instance_variable_set(:"@#{v}_value", nil) }
  MULTI_VALUE_METHODS.each { |v| instance_variable_set(:"@#{v}_values", []) }
  @highlight_options = {}
  @per_page_value = @klass.default_page_size
  @page_value = 1
  @use_solr_value = :default
  @extensions = []
  @create_with_value = {}
  @escape_value = :default
  @stats = {}
  if @klass.default_query_parser
    @query_parser_value = if @klass.default_query_parser.is_a?(Hash)
                            @klass.default_query_parser
                          else
                            { @klass.default_query_parser => {} }
                          end
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object (protected)

:nodoc:


680
681
682
683
684
685
686
687
688
# File 'lib/datastax_rails/relation.rb', line 680

def method_missing(method, *args, &block) #:nodoc:
  if DatastaxRails::Collection.method_defined?(method)
    to_a.send(method, *args, &block)
  elsif @klass.respond_to?(method, true)
    scoping { @klass.send(method, *args, &block) }
  else
    super
  end
end

Instance Attribute Details

#column_familyObject (readonly)

Returns the value of attribute column_family


31
32
33
# File 'lib/datastax_rails/relation.rb', line 31

def column_family
  @column_family
end

#cqlObject (readonly)

Returns the value of attribute cql


31
32
33
# File 'lib/datastax_rails/relation.rb', line 31

def cql
  @cql
end

#create_with_valueObject

Returns the value of attribute create_with_value


20
21
22
# File 'lib/datastax_rails/relation.rb', line 20

def create_with_value
  @create_with_value
end

#default_scopedObject Also known as: default_scoped?

Returns the value of attribute default_scoped


20
21
22
# File 'lib/datastax_rails/relation.rb', line 20

def default_scoped
  @default_scoped
end

#highlight_optionsObject

Returns the value of attribute highlight_options


21
22
23
# File 'lib/datastax_rails/relation.rb', line 21

def highlight_options
  @highlight_options
end

#klassObject (readonly)

Returns the value of attribute klass


31
32
33
# File 'lib/datastax_rails/relation.rb', line 31

def klass
  @klass
end

#loadedObject (readonly) Also known as: loaded?

Returns the value of attribute loaded


31
32
33
# File 'lib/datastax_rails/relation.rb', line 31

def loaded
  @loaded
end

Instance Method Details

#==(other) ⇒ Object

Returns true if the two relations have the same query parameters


68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/datastax_rails/relation.rb', line 68

def ==(other)
  case other
  when Relation
    # This is not a valid implementation.  It's a placeholder till I figure out the right way.
    MULTI_VALUE_METHODS.each do |m|
      return false unless other.send("#{m}_values") == send("#{m}_values")
    end
    SINGLE_VALUE_METHODS.each do |m|
      return false unless other.send("#{m}_value") == send("#{m}_value")
    end
    return true
  when Array
    to_a == other
  end
end

#any?Boolean Also known as: exists?

Returns true if there are any results given the current criteria

Returns:

  • (Boolean)

85
86
87
88
89
90
91
# File 'lib/datastax_rails/relation.rb', line 85

def any?
  if block_given?
    to_a.any? { |*block_args| yield(*block_args) }
  else
    !empty?
  end
end

#cloneObject

Performs a deep copy using Marshal when cloning.


191
192
193
194
195
196
197
198
199
200
# File 'lib/datastax_rails/relation.rb', line 191

def clone
  dup.tap do |r|
    MULTI_VALUE_METHODS.each do |m|
      r.send("#{m}_values=", Marshal.load(Marshal.dump(send("#{m}_values"))))
    end
    SINGLE_VALUE_METHODS.each do |m|
      r.send("#{m}_value=", Marshal.load(Marshal.dump(send("#{m}_value")))) if send("#{m}_value")
    end
  end
end

#commit_solrObject

Sends a commit message to SOLR


652
653
654
# File 'lib/datastax_rails/relation.rb', line 652

def commit_solr
  rsolr.commit commit_attributes: {}
end

#countObject

Returns the total number of entries that match the given search. This means the total number of matches regardless of page size. If the relation has not been populated yet, a limit of 1 will be placed on the query before it is executed.

For a grouped query, this still returns the total number of matching documents

Compare with #size.


103
104
105
106
107
108
109
# File 'lib/datastax_rails/relation.rb', line 103

def count
  @count ||= if with_default_scope.path_decision == :solr
               with_default_scope.count_via_solr
             else
               with_default_scope.count_via_cql
             end
end

#count_via_cqlObject


295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/datastax_rails/relation.rb', line 295

def count_via_cql
  cql = @cql.select(['count(*)'])
  cql.using(@consistency_value) if @consistency_value
  @where_values.each do |wv|
    wv.each do |k, v|
      attr = (k.to_s == 'id' ? @klass.primary_key : k)
      col = klass.column_for_attribute(attr)
      if col.primary
        v.compact! if v.respond_to?(:compact)
        return 0 if v.blank?
      end
      values = Array(v).map do |val|
        col.type_cast_for_cql3(val)
      end
      cql.conditions(attr => values)
    end
  end
  cql.allow_filtering if @allow_filtering_value
  cql.execute.first['count']
end

#count_via_solrObject

Runs the query with a limit of 1 just to grab the total results attribute off the result set.


387
388
389
390
# File 'lib/datastax_rails/relation.rb', line 387

def count_via_solr
  results = limit(1).select(:id).to_a
  @group_value ? results.total_for_all : results.total_entries
end

#create(*args, &block) ⇒ Object

Create a new object with all of the criteria from this relation applied


241
242
243
# File 'lib/datastax_rails/relation.rb', line 241

def create(*args, &block)
  scoping { @klass.create(*args, &block) }
end

#create!(*args, &block) ⇒ Object

Like create but throws an exception on failure


246
247
248
# File 'lib/datastax_rails/relation.rb', line 246

def create!(*args, &block)
  scoping { @klass.create!(*args, &block) }
end

#current_pageObject

Returns the current page for will_paginate compatibility


117
118
119
# File 'lib/datastax_rails/relation.rb', line 117

def current_page
  page_value.try(:to_i)
end

#default_scopeObject

Gets a default scope with no conditions or search attributes set.


132
133
134
# File 'lib/datastax_rails/relation.rb', line 132

def default_scope
  klass.scoped.with_default_scope
end

#downcase_query(value) ⇒ Object

Everything that gets indexed into solr is downcased as part of the analysis phase. Normally, this is done to the query as well, but if your query includes wildcards then analysis isn't performed. This means that the query does not get downcased. We therefore need to perform the downcasing ourselves. This does it while still leaving boolean operations (AND, OR, NOT, TO) and dates upcased.


662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
# File 'lib/datastax_rails/relation.rb', line 662

def downcase_query(value)
  # rubocop:disable Style/MultilineBlockChain
  if value.is_a?(String)
    value.split(/\bAND\b/).map do |a|
      a.split(/\bOR\b/).map do |o|
        o.split(/\bNOT\b/).map do |n|
          n.split(/\bTO\b/).map(&:downcase).join('TO')
        end.join('NOT')
      end.join('OR')
    end.join('AND').gsub(SOLR_DATE_REGEX) { Regexp.last_match[1].upcase }
  else
    value
  end
  # rubocop:enable Style/MultilineBlockChain
end

#empty?Boolean

Returns true if there are no results given the current criteria

Returns:

  • (Boolean)

147
148
149
150
151
152
# File 'lib/datastax_rails/relation.rb', line 147

def empty?
  return @results.empty? if loaded?

  c = count
  c.respond_to?(:zero?) ? c.zero? : c.empty?
end

#full_solr_range(attr) ⇒ Object


403
404
405
406
407
408
409
# File 'lib/datastax_rails/relation.rb', line 403

def full_solr_range(attr)
  if klass.attribute_definitions[attr]
    klass.attribute_definitions[attr].full_solr_range
  else
    '[\"\" TO *]'
  end
end

#initialize_copy(_other) ⇒ Object

Copies will have changes made to the criteria and so need to be reset.


185
186
187
188
# File 'lib/datastax_rails/relation.rb', line 185

def initialize_copy(_other)
  reset
  @search = nil
end

#inspect(just_me = false) ⇒ Object

Inspects the results of the search instead of the Relation itself. Passing true causes the Relation to be inspected.

Parameters:

  • just_me (Boolean) (defaults to: false)

    if true, inspect the Relation, otherwise the results


622
623
624
# File 'lib/datastax_rails/relation.rb', line 622

def inspect(just_me = false)
  just_me ? super() : to_a.inspect
end

#many?Boolean

Returns true if there are multiple results given the current criteria

Returns:

  • (Boolean)

155
156
157
158
159
160
161
# File 'lib/datastax_rails/relation.rb', line 155

def many?
  if block_given?
    to_a.many? { |*block_args| yield(*block_args) }
  else
    count > 1
  end
end

#map_cassandra_columns(conditions) ⇒ Object

If we index something into both cassandra and solr, we rename the cassandra column. This method maps the column names as necessary


283
284
285
286
287
288
289
290
291
292
293
# File 'lib/datastax_rails/relation.rb', line 283

def map_cassandra_columns(conditions)
  {}.tap do |mapped|
    conditions.each do |k, v|
      if (klass.attribute_definitions[k].indexed == :both)
        mapped["__#{k}"] = v
      else
        mapped[k] = v
      end
    end
  end
end

#new(*args, &block) ⇒ Object

Constructs a new instance of the class this relation points to with any criteria from this relation applied


165
166
167
# File 'lib/datastax_rails/relation.rb', line 165

def new(*args, &block)
  scoping { @klass.new(*args, &block) }
end

#next_pageObject

current_page + 1 or nil if there is no next page


127
128
129
# File 'lib/datastax_rails/relation.rb', line 127

def next_page
  current_page < total_pages ? (current_page + 1) : nil
end

#path_decisionObject


255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/datastax_rails/relation.rb', line 255

def path_decision
  return :cassandra if klass <= DatastaxRails::CassandraOnlyModel
  case use_solr_value
  when false
    return :cassandra
  when true
    return :solr
  else
    [order_values, where_not_values, fulltext_values, greater_than_values, less_than_values, field_facet_values,
     range_facet_values, group_value, facet_threads_value].each do |solr_only_stuff|
       return :solr unless solr_only_stuff.blank?
     end
    return :solr unless group_value.blank?
    return :solr unless page_value == 1
    @where_values.each do |wv|
      wv.each do |k, _v|
        col = klass.column_for_attribute(k)
        next if col && (col.options[:cql_index] || col.primary)
        return :solr
      end
    end
    # If we get here, we can safely run this query via Cassandra
    return :cassandra
  end
end

#previous_pageObject

current_page - 1 or nil if there is no previous page


122
123
124
# File 'lib/datastax_rails/relation.rb', line 122

def previous_page
  current_page > 1 ? (current_page - 1) : nil
end

#query_via_cqlObject

Constructs a CQL query and runs it against Cassandra directly. For this to work, you need to run against either the primary key or a secondary index. For ad-hoc queries, you will have to use Solr. rubocop:disable Metrics/MethodLength rubocop:disable Metrics/PerceivedComplexity rubocop:disable Metrics/CyclomaticComplexity


322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/datastax_rails/relation.rb', line 322

def query_via_cql
  select_columns =
    select_values.empty? ? (@klass.attribute_definitions.keys - @klass.lazy_attributes) : select_values.flatten
  cql = @cql.select((select_columns + [@klass.primary_key]).uniq)
  cql.using(@consistency_value) if @consistency_value
  @where_values.each do |wv|
    wv.each do |k, v|
      attr = (k.to_s == 'id' ? @klass.primary_key : k)
      col = klass.column_for_attribute(attr)
      if col.primary
        v.compact! if v.respond_to?(:compact!)
        return [] if v.blank?
      end
      values = Array(v).map do |val|
        col.type_cast_for_cql3(val)
      end
      cql.conditions(attr => values)
    end
  end
  @greater_than_values.each do |gtv|
    gtv.each do |k, v|
      # Special case if inequality is equal to the primary key (we're paginating)
      cql.paginate(v) if (k.to_s == @klass.primary_key)
    end
  end
  cql.limit(@per_page_value) if @per_page_value
  cql.allow_filtering if @allow_filtering_value
  results = []
  begin
    cql.execute.each do |row|
      results << @klass.instantiate(row[@klass.primary_key], row, select_columns)
    end
  rescue Cassandra::Errors::ValidationError => e
    # If we get an exception about an empty key, ignore it.  We'll return an empty set.
    raise unless e.message =~ /Key may not be empty/
  end
  if @slow_order_values.any?
    results.sort! do |a, b|
      values = slow_ordering(a, b)
      values[0] <=> values[1]
    end
  end
  results
end

#query_via_solrObject

Constructs a solr query to run against SOLR.

It's worth noting that where and where_not make use of individual filter_queries. If that's not what you want, you might be better off constructing your own fulltext query and sending that in.

TODO: break this apart into multiple methods


418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
# File 'lib/datastax_rails/relation.rb', line 418

def query_via_solr # rubocop:disable all
  filter_queries = []
  orders = []
  @where_values.each do |wv|
    wv.each do |k, v|
      # If v is blank but not false, check that there is no value for the field in the document
      if v.blank? && v != false
        filter_queries << "-#{k}:#{full_solr_range(k)}"
      else
        filter_queries << "#{k}:(#{solr_format(k, v)})"
      end
    end
  end

  @where_not_values.each do |wnv|
    wnv.each do |k, v|
      # If v is blank but not false, check for any value in the field in the document
      if v.blank? && v != false
        filter_queries << "#{k}:#{full_solr_range(k)}"
      else
        filter_queries << "-#{k}:(#{solr_format(k, v)})"
      end
    end
  end

  @greater_than_values.each do |gtv|
    gtv.each do |k, v|
      filter_queries << "#{k}:[#{solr_format(k, v)} TO *]"
    end
  end

  @less_than_values.each do |ltv|
    ltv.each do |k, v|
      filter_queries << "#{k}:[* TO #{solr_format(k, v)}]"
    end
  end

  @order_values.each do |ov|
    ov.each do |k, v|
      if @reverse_order_value
        orders << "#{k} #{v == :asc ? 'desc' : 'asc'}"
      else
        orders << "#{k} #{v == :asc ? 'asc' : 'desc'}"
      end
    end
  end

  sort = orders.join(',')

  q = @fulltext_values.empty? ? '*:*' : @fulltext_values.map { |ftv| '(' + ftv[:query] + ')' }.join(' AND ')

  params = { q: q, commit: true }
  params[:sort] = sort
  params[:fq] = filter_queries unless filter_queries.empty?
  if @query_parser_value
    params['defType'] = @query_parser_value.keys.first
    params.merge(@query_parser_value.values.first)
  end

  # Facets
  # facet=true to enable faceting,  facet.field=<field_name> (can appear more than once for multiple fields)
  # Additional options: f.<field_name>.facet.<option> [e.g. f.author.facet.sort=index]

  # Facet Fields
  unless field_facet_values.empty?
    params['facet'] = 'true'
    params['facet.threads'] = facet_threads_value if facet_threads_value.present?

    facet_fields = []
    field_facet_values.each do |facet|
      facet_field = facet[:field]
      facet_fields << facet_field
      facet[:options].each do |key, value|
        params["f.#{facet_field}.facet.#{key}"] = value.to_s
      end
    end
    params['facet.field'] = facet_fields
  end

  # Facet Ranges
  unless range_facet_values.empty?
    params['facet'] = 'true'
    facet_fields = []
    range_facet_values.each do |facet|
      facet_field = facet[:field]
      facet_fields << facet_field
      facet[:options].each do |key, value|
        params["f.#{facet_field}.facet.range.#{key}"] = value.to_s
      end
    end
    params['facet.range'] = facet_fields
  end

  if @highlight_options[:fields].present?
    params[:hl] = true
    params['hl.fl'] = @highlight_options[:fields]
    params['hl.snippets'] = @highlight_options[:snippets] if @highlight_options[:snippets]
    params['hl.fragsize'] = @highlight_options[:fragsize] if @highlight_options[:fragsize]
    params['hl.requireFieldMatch'] =
      @highlight_options[:require_field_match] if @highlight_options[:require_field_match].present?
    if @highlight_options[:use_fast_vector]
      params['hl.useFastVectorHighlighter'] = true
      params['hl.tag.pre'] = @highlight_options[:pre_tag] if @highlight_options[:pre_tag].present?
      params['hl.tag.post'] = @highlight_options[:post_tag] if @highlight_options[:post_tag].present?
    else
      params['hl.mergeContiguous'] = @highlight_options[:merge_contiguous].present?
      params['hl.simple.pre'] = @highlight_options[:pre_tag] if @highlight_options[:pre_tag].present?
      params['hl.simple.post'] = @highlight_options[:post_tag] if @highlight_options[:post_tag].present?
      params['hl.maxAnalyzedChars'] =
        @highlight_options[:max_analyzed_chars] if @highlight_options[:max_analyzed_chars].present?
    end
  end

  select_columns =
    select_values.empty? ? (@klass.attribute_definitions.keys - @klass.lazy_attributes) : select_values.flatten
  select_columns << @klass.primary_key
  select_columns.map! { |c| @klass.column_for_attribute(c).try(:type) == :map ? "#{c}*" : c.to_s }
  params[:fl] = select_columns.uniq.join(',')
  unless @stats_values.empty?
    params[:stats] = 'true'
    @stats_values.flatten.each do |sv|
      params['stats.field'] = sv
    end
    params['stats.facet'] = @group_value
  end
  solr_response = nil

  if @group_value
    results = DatastaxRails::GroupedCollection.new
    params[:group] = 'true'
    params[:rows] = 10_000
    params['group.field'] = @group_value
    params['group.limit'] = @per_page_value
    params['group.offset'] = (@page_value - 1) * @per_page_value
    params['group.ngroups'] = 'false' # must be false due to issues with solr sharding
    ActiveSupport::Notifications.instrument(
      'solr.datastax_rails',
      name:   'Search',
      klass:  @klass.name,
      search: params) do
      solr_response = rsolr.post('select', data: params)
      response = solr_response['grouped'][@group_value.to_s]
      results.total_groups = response['groups'].size
      results.total_for_all = response['matches'].to_i
      results.total_entries = 0
      response['groups'].each do |group|
        results[group['groupValue']] = parse_docs(group['doclist'], select_columns)
        if results[group['groupValue']].total_entries > results.total_entries
          results.total_entries = results[group['groupValue']].total_entries
        end
      end
    end
  else
    ActiveSupport::Notifications.instrument(
      'solr.datastax_rails',
      name:   'Search',
      klass:  @klass.name,
      search: params.merge(page: @page_value, per_page: @per_page_value)) do
      solr_response = rsolr.paginate(@page_value, @per_page_value, 'select', data: params, method: :post)
      response = solr_response['response']
      results = parse_docs(response, select_columns)
      results.highlights = solr_response['highlighting']
    end
  end
  if solr_response['stats']
    @stats = solr_response['stats']['stats_fields'].with_indifferent_access
  end
  # Apply Facets if they exist
  if solr_response['facet_counts']
    results.facets = {}
    results.facets = results.facets.merge(solr_response['facet_counts']['facet_fields'].to_h)
    results.facets = results.facets.merge(solr_response['facet_counts']['facet_ranges'].to_h)
  end
  results
end

#reloadObject

Reloads the results from cassandra or solr as appropriate


170
171
172
173
174
# File 'lib/datastax_rails/relation.rb', line 170

def reload
  reset
  to_a
  self
end

#resetObject

Empties out the current results. The next call to to_a will re-run the query.


178
179
180
181
182
# File 'lib/datastax_rails/relation.rb', line 178

def reset
  @loaded = @first = @last = @scope_for_create = @count = nil
  @stats = {}
  @results = []
end

#respond_to?(method, include_private = false) ⇒ Boolean

Override respond_to? so that it matches method_missing

Returns:

  • (Boolean)

251
252
253
# File 'lib/datastax_rails/relation.rb', line 251

def respond_to?(method, include_private = false)
  Array.method_defined?(method) || @klass.respond_to?(method, include_private) || super
end

#scope_for_createObject

Creates a scope that includes all of the where values plus anything that is in create_with_value.


647
648
649
# File 'lib/datastax_rails/relation.rb', line 647

def scope_for_create
  @scope_for_create ||= where_values_hash.merge(create_with_value)
end

#scopingObject

Scope all queries to the current scope.

Example

Comment.where(:post_id => 1).scoping do
  Comment.first # SELECT * FROM comments WHERE post_id = 1
end

Please check unscoped if you want to remove all previous scopes (including the default_scope) during the execution of a block.


636
637
638
# File 'lib/datastax_rails/relation.rb', line 636

def scoping
  @klass.send(:with_scope, self, :overwrite) { yield }
end

#sizeObject

Returns the size of the total result set for the given criteria NOTE that this takes pagination into account so will only return the number of results in the current page. DatastaxRails models can have a default_page_size set which will cause them to be paginated all the time.

For a grouped query, this returns the size of the largest group.

Compare with #count


211
212
213
214
215
# File 'lib/datastax_rails/relation.rb', line 211

def size
  return @results.size if loaded? && !@group_value
  total_entries = count
  (per_page_value && total_entries > per_page_value) ? per_page_value : total_entries
end

#slow_ordering(obj1, obj2) ⇒ Object


367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/datastax_rails/relation.rb', line 367

def slow_ordering(obj1, obj2)
  [[], []].tap do |values|
    i = 0
    @slow_order_values.each do |ordering|
      ordering.each do |k, v|
        if v == :asc
          values[0][i] = obj1.send(k)
          values[1][i] = obj2.send(k)
        else
          values[1][i] = obj1.send(k)
          values[0][i] = obj2.send(k)
        end
      end
      i += 1
    end
  end
end

#solr_escape(str) ⇒ Object

Escapes values that might otherwise mess up the URL or confuse SOLR. If you want to handle escaping yourself for a particular query then SearchMethods#dont_escape is what you're looking for.


395
396
397
398
399
400
401
# File 'lib/datastax_rails/relation.rb', line 395

def solr_escape(str)
  if str.is_a?(String) && escape_value
    str.gsub(SOLR_CHAR_RX, '\\\\\1')
  else
    str
  end
end

#statsObject


111
112
113
114
# File 'lib/datastax_rails/relation.rb', line 111

def stats
  loaded? || to_a
  @stats
end

#to_aObject Also known as: all, results

Actually executes the query if not already executed. Returns a standard array thus no more methods may be chained.


226
227
228
229
230
231
232
233
234
235
236
# File 'lib/datastax_rails/relation.rb', line 226

def to_a
  return @results if loaded?
  if with_default_scope.path_decision == :solr
    @results = with_default_scope.query_via_solr
    @count = @group_value ? @results.total_for_all : @results.total_entries
  else
    @results = with_default_scope.query_via_cql
  end
  @loaded = true
  @results
end

#total_pagesObject

Returns the total number of pages required to display the results given the current page size. Used by will_paginate.


219
220
221
222
# File 'lib/datastax_rails/relation.rb', line 219

def total_pages
  return 1 unless @per_page_value
  (count / @per_page_value.to_f).ceil
end

#where_values_hashObject

Merges all of the where values together into a single hash


641
642
643
# File 'lib/datastax_rails/relation.rb', line 641

def where_values_hash
  where_values.reduce({}) { |a, e| a.merge(e) }
end

#with_default_scopeObject

:nodoc:


136
137
138
139
140
141
142
143
144
# File 'lib/datastax_rails/relation.rb', line 136

def with_default_scope #:nodoc:
  if default_scoped? && (default_scope = klass.send(:build_default_scope))
    default_scope = default_scope.merge(self)
    default_scope.default_scoped = false
    default_scope
  else
    self
  end
end