Module: ActiveModel::Datastore::ClassMethods

Defined in:
lib/active_model/datastore.rb

Overview

Methods defined here will be class methods when ‘include ActiveModel::Datastore’.

Instance Method Summary collapse

Instance Method Details

#all(options = {}) ⇒ Array<Model>, String

Queries entities from Cloud Datastore by named kind and using the provided options. When a limit option is provided queries up to the limit and returns results with a cursor.

This method may make several API calls until all query results are retrieved. The ‘run` method returns a QueryResults object, which is a special case Array with additional values. QueryResults are returned in batches, and the batch size is determined by the Datastore API. Batch size is not guaranteed. It will be affected by the size of the data being returned, and by other forces such as how distributed and/or consistent the data in Datastore is. Calling `all` on the QueryResults retrieves all results by repeatedly loading #next until #next? returns false. The `all` method returns an enumerator which from_entities iterates on.

Be sure to use as narrow a search criteria as possible. Please use with caution.

or if options was provided:

Parameters:

  • options (Hash) (defaults to: {})

    The options to construct the query with.

Options Hash (options):

  • :ancestor (Google::Cloud::Datastore::Key)

    Filter for inherited results.

  • :cursor (String)

    Sets the cursor to start the results at.

  • :limit (Integer)

    Sets a limit to the number of results to be returned.

  • :order (String)

    Sort the results by property name.

  • :desc_order (String)

    Sort the results by descending property name.

  • :select (Array)

    Retrieve only select properties from the matched entities.

  • :where (Array)

    Adds a property filter of arrays in the format [name, operator, value].

Returns:

  • (Array<Model>, String)

    An array of ActiveModel results

  • (Array<Model>, String)

    An array of ActiveModel results and a cursor that can be used to query for additional results.



311
312
313
314
315
316
317
318
319
320
# File 'lib/active_model/datastore.rb', line 311

def all(options = {})
  next_cursor = nil
  query = build_query(options)
  query_results = retry_on_exception { CloudDatastore.dataset.run query }
  if options[:limit]
    next_cursor = query_results.cursor if query_results.size == options[:limit]
    return from_entities(query_results.all), next_cursor
  end
  from_entities(query_results.all)
end

#build_query(options = {}) ⇒ Query

Constructs a Google::Cloud::Datastore::Query.

Parameters:

  • options (Hash) (defaults to: {})

    The options to construct the query with.

Options Hash (options):

  • :ancestor (Google::Cloud::Datastore::Key)

    Filter for inherited results.

  • :cursor (String)

    Sets the cursor to start the results at.

  • :limit (Integer)

    Sets a limit to the number of results to be returned.

  • :order (String)

    Sort the results by property name.

  • :desc_order (String)

    Sort the results by descending property name.

  • :select (Array)

    Retrieve only select properties from the matched entities.

  • :where (Array)

    Adds a property filter of arrays in the format [name, operator, value].

Returns:

  • (Query)

    A datastore query.



423
424
425
426
# File 'lib/active_model/datastore.rb', line 423

def build_query(options = {})
  query = CloudDatastore.dataset.query name
  query_options(query, options)
end

#exclude_from_index(entity, boolean) ⇒ Object



401
402
403
404
405
# File 'lib/active_model/datastore.rb', line 401

def exclude_from_index(entity, boolean)
  entity.properties.to_h.keys.each do |value|
    entity.exclude_from_indexes! value, boolean
  end
end

#find(*ids, parent: nil) ⇒ Model, ...

Find entity by id - this can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). The parent key is optional.

Parameters:

  • ids (Integer)

    One or more ids to retrieve.

  • parent (Google::Cloud::Datastore::Key) (defaults to: nil)

    The parent key of the entity.

Returns:

  • (Model, nil)

    An ActiveModel object or nil for a single id.

  • (Array<Model>)

    An array of ActiveModel objects for more than one id.



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/active_model/datastore.rb', line 332

def find(*ids, parent: nil)
  expects_array = ids.first.is_a?(Array)
  ids = ids.flatten.compact.uniq.map(&:to_i)

  case ids.size
  when 0
    raise EntityError, "Couldn't find #{name} without an ID"
  when 1
    entity = find_entity(ids.first, parent)
    model_entity = from_entity(entity)
    expects_array ? [model_entity].compact : model_entity
  else
    lookup_results = find_all_entities(ids, parent)
    from_entities(lookup_results.all)
  end
end

#find_all_entities(ids_or_names, parent) ⇒ Object

Finds entities by keys using the provided array items. Results provided by the dataset ‘find_all` is a Dataset::LookupResults object.

Parameters:

  • ids_or_names (Array<Integer>, Array<String>)

    An array of ids or names.



513
514
515
516
517
# File 'lib/active_model/datastore.rb', line 513

def find_all_entities(ids_or_names, parent)
  keys = ids_or_names.map { |id| CloudDatastore.dataset.key name, id }
  keys.map { |key| key.parent = parent } if parent.present?
  retry_on_exception { CloudDatastore.dataset.find_all keys }
end

#find_by(args) ⇒ Model?

Finds the first entity matching the specified condition.

Examples:

User.find_by(name: 'Joe')
User.find_by(name: 'Bryce', ancestor: parent)

Parameters:

  • args (Hash)

    In which the key is the property and the value is the value to look for.

Options Hash (args):

  • :ancestor (Google::Cloud::Datastore::Key)

    filter for inherited results

Returns:

  • (Model, nil)

    An ActiveModel object or nil.



361
362
363
364
365
366
367
368
# File 'lib/active_model/datastore.rb', line 361

def find_by(args)
  query = CloudDatastore.dataset.query name
  query.ancestor(args[:ancestor]) if args[:ancestor]
  query.limit(1)
  query.where(args.keys[0].to_s, '=', args.values[0])
  query_results = retry_on_exception { CloudDatastore.dataset.run query }
  from_entity(query_results.first)
end

#find_entities(*ids_or_names, parent: nil) ⇒ Array<Entity>

Retrieves the entities for the provided ids by key and by an optional parent. The find_all method returns LookupResults, which is a special case Array with additional values. LookupResults are returned in batches, and the batch size is determined by the Datastore API. Batch size is not guaranteed. It will be affected by the size of the data being returned, and by other forces such as how distributed and/or consistent the data in Datastore is. Calling ‘all` on the LookupResults retrieves all results by repeatedly loading #next until #next? returns false. The `all` method returns an enumerator unless passed a block. We iterate on the enumerator to return the model entity objects.

Parameters:

  • ids_or_names (Integer, String)

    One or more ids to retrieve.

  • parent (Google::Cloud::Datastore::Key) (defaults to: nil)

    The parent Key of the entity.

Returns:

  • (Array<Entity>)

    an array of Google::Cloud::Datastore::Entity objects.



273
274
275
276
277
# File 'lib/active_model/datastore.rb', line 273

def find_entities(*ids_or_names, parent: nil)
  ids_or_names = ids_or_names.flatten.compact.uniq
  lookup_results = find_all_entities(ids_or_names, parent)
  lookup_results.all.collect { |x| x }
end

#find_entity(id_or_name, parent = nil) ⇒ Entity?

Retrieves an entity by id or name and by an optional parent.

Parameters:

  • id_or_name (Integer or String)

    The id or name value of the entity Key.

  • parent (Google::Cloud::Datastore::Key) (defaults to: nil)

    The parent Key of the entity.

Returns:

  • (Entity, nil)

    a Google::Cloud::Datastore::Entity object or nil.



251
252
253
254
255
# File 'lib/active_model/datastore.rb', line 251

def find_entity(id_or_name, parent = nil)
  key = CloudDatastore.dataset.key name, id_or_name
  key.parent = parent if parent.present?
  retry_on_exception { CloudDatastore.dataset.find key }
end

#from_entities(entities) ⇒ Object

Translates an Enumerator of Datastore::Entity objects to ActiveModel::Model objects.

Results provided by the dataset ‘find_all` or `run query` will be a Dataset::LookupResults or Dataset::QueryResults object. Invoking `all` on those objects returns an enumerator.

Parameters:

  • entities (Enumerator)

    An enumerator representing the datastore entities.

Raises:

  • (ArgumentError)


378
379
380
381
# File 'lib/active_model/datastore.rb', line 378

def from_entities(entities)
  raise ArgumentError, 'Entities param must be an Enumerator' unless entities.is_a? Enumerator
  entities.map { |entity| from_entity(entity) }
end

#from_entity(entity) ⇒ Model

Translates between Datastore::Entity objects and ActiveModel::Model objects.

Parameters:

  • entity (Entity)

    Entity from Cloud Datastore.

Returns:

  • (Model)

    The translated ActiveModel object.



389
390
391
392
393
394
395
396
397
398
399
# File 'lib/active_model/datastore.rb', line 389

def from_entity(entity)
  return if entity.nil?
  model_entity = new
  model_entity.id = entity.key.id unless entity.key.id.nil?
  model_entity.id = entity.key.name unless entity.key.name.nil?
  entity.properties.to_hash.each do |name, value|
    model_entity.send "#{name}=", value if model_entity.respond_to? "#{name}="
  end
  model_entity.reload!
  model_entity
end

#log_google_cloud_errorObject



460
461
462
463
464
465
# File 'lib/active_model/datastore.rb', line 460

def log_google_cloud_error
  yield
rescue Google::Cloud::Error => e
  puts "\e[33m[#{e.message.inspect}]\e[0m"
  raise e
end

#query_options(query, options) ⇒ Object

**************** private ****************



469
470
471
472
473
474
475
476
# File 'lib/active_model/datastore.rb', line 469

def query_options(query, options)
  query.ancestor(options[:ancestor]) if options[:ancestor]
  query.cursor(options[:cursor]) if options[:cursor]
  query.limit(options[:limit]) if options[:limit]
  query_sort(query, options)
  query.select(options[:select]) if options[:select]
  query_property_filter(query, options)
end

#query_property_filter(query, options) ⇒ Object

Adds property filters to the query if included in the options. Accepts individual or nested Arrays:

[['superseded', '=', false], ['email', '=', 'something']]


492
493
494
495
496
497
498
499
500
501
502
503
504
# File 'lib/active_model/datastore.rb', line 492

def query_property_filter(query, options)
  if options[:where]
    opts = options[:where]
    if opts[0].is_a?(Array)
      opts.each do |opt|
        query.where(opt[0], opt[1], opt[2]) unless opt.nil?
      end
    else
      query.where(opts[0], opts[1], opts[2])
    end
  end
  query
end

#query_sort(query, options) ⇒ Object

Adds sorting to the results by a property name if included in the options.



481
482
483
484
485
# File 'lib/active_model/datastore.rb', line 481

def query_sort(query, options)
  query.order(options[:order]) if options[:order]
  query.order(options[:desc_order], :desc) if options[:desc_order]
  query
end

#retry_on_exception(max_retry_count = 5) ⇒ Object



444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/active_model/datastore.rb', line 444

def retry_on_exception(max_retry_count = 5)
  retries = 0
  sleep_time = 0.25
  begin
    yield
  rescue => e
    raise e if retries >= max_retry_count
    puts "\e[33mRescued exception #{e.message.inspect}, retrying in #{sleep_time}\e[0m"
    # 0.25, 0.5, 1, 2, and 4 second between retries.
    sleep sleep_time
    retries += 1
    sleep_time *= 2
    retry
  end
end

#retry_on_exception?(max_retry_count = 5) ⇒ Boolean

Returns:

  • (Boolean)


428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/active_model/datastore.rb', line 428

def retry_on_exception?(max_retry_count = 5)
  retries = 0
  sleep_time = 0.25
  begin
    yield
  rescue => e
    return false if retries >= max_retry_count
    puts "\e[33mRescued exception #{e.message.inspect}, retrying in #{sleep_time}\e[0m"
    # 0.25, 0.5, 1, 2, and 4 second between retries.
    sleep sleep_time
    retries += 1
    sleep_time *= 2
    retry
  end
end