module ActiveModel::Datastore::ClassMethods
Methods defined here will be class methods when 'include ActiveModel::Datastore'.
Public Instance Methods
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.
@param [Hash] options The options to construct the query with.
@option options [Google::Cloud::Datastore::Key] :ancestor Filter for inherited results. @option options [String] :cursor Sets the cursor to start the results at. @option options [Integer] :limit Sets a limit to the number of results to be returned. @option options [String] :order Sort the results by property name. @option options [String] :desc_order Sort the results by descending property name. @option options [Array] :select Retrieve only select properties from the matched entities. @option options [Array] :distinct_on Group results by a list of properties. @option options [Array] :where Adds a property filter of arrays in the format
[name, operator, value].
@return [Array<Model>, String] An array of ActiveModel
results
or if options was provided:
@return [Array<Model>, String] An array of ActiveModel
results and a cursor that
can be used to query for additional results.
# File lib/active_model/datastore.rb, line 290 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
# File lib/active_model/datastore.rb, line 497 def build_model(entity) 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? model_entity.parent_key_id = entity.key.parent.id if entity.key.parent.present? model_entity end
Constructs a Google::Cloud::Datastore::Query.
@param [Hash] options The options to construct the query with.
@option options [Google::Cloud::Datastore::Key] :ancestor Filter for inherited results. @option options [String] :cursor Sets the cursor to start the results at. @option options [Integer] :limit Sets a limit to the number of results to be returned. @option options [String] :order Sort the results by property name. @option options [String] :desc_order Sort the results by descending property name. @option options [Array] :select Retrieve only select properties from the matched entities. @option options [Array] :distinct_on Group results by a list of properties. @option options [Array] :where Adds a property filter of arrays in the format
[name, operator, value].
@return [Query] A datastore query.
# File lib/active_model/datastore.rb, line 398 def build_query(options = {}) query = CloudDatastore.dataset.query name query_options(query, options) end
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.
@param [Integer] ids One or more ids to retrieve. @param [Google::Cloud::Datastore::Key] parent The parent key of the entity.
@return [Model, nil] An ActiveModel
object or nil for a single id. @return [Array<Model>] An array of ActiveModel
objects for more than one id.
# File lib/active_model/datastore.rb, line 311 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
Finds entities by keys using the provided array items. Results provided by the dataset `find_all` is a Dataset::LookupResults object.
@param [Array<Integer>, Array<String>] ids_or_names An array of ids or names.
# File lib/active_model/datastore.rb, line 491 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
Finds the first entity matching the specified condition.
@param [Hash] args In which the key is the property and the value is the value to look for. @option args [Google::Cloud::Datastore::Key] :ancestor filter for inherited results
@return [Model, nil] An ActiveModel
object or nil.
@example
User.find_by(name: 'Joe') User.find_by(name: 'Bryce', ancestor: parent_key)
# File lib/active_model/datastore.rb, line 340 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
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.
@param [Integer, String] ids_or_names One or more ids to retrieve. @param [Google::Cloud::Datastore::Key] parent The parent Key of the entity.
@return [Array<Entity>] an array of Google::Cloud::Datastore::Entity objects.
# File lib/active_model/datastore.rb, line 251 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
Retrieves an entity by id or name and by an optional parent.
@param [Integer or String] id_or_name The id or name value of the entity Key. @param [Google::Cloud::Datastore::Key] parent The parent Key of the entity.
@return [Entity, nil] a Google::Cloud::Datastore::Entity object or nil.
# File lib/active_model/datastore.rb, line 229 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
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.
@param [Enumerator] entities An enumerator representing the datastore entities.
# File lib/active_model/datastore.rb, line 357 def from_entities(entities) raise ArgumentError, 'Entities param must be an Enumerator' unless entities.is_a? Enumerator entities.map { |entity| from_entity(entity) } end
Translates between Datastore::Entity objects and ActiveModel::Model objects.
@param [Entity] entity Entity from Cloud Datastore
. @return [Model] The translated ActiveModel
object.
# File lib/active_model/datastore.rb, line 369 def from_entity(entity) return if entity.nil? model_entity = build_model(entity) model_entity.entity_property_values = entity.properties.to_h entity.properties.to_h.each do |name, value| model_entity.send "#{name}=", value if model_entity.respond_to? "#{name}=" end model_entity.reload! model_entity end
# File lib/active_model/datastore.rb, line 437 def log_google_cloud_error yield rescue Google::Cloud::Error => e puts "\e[33m[#{e.message.inspect}]\e[0m" raise e end
A default parent key for specifying an ancestor path and creating an entity group.
# File lib/active_model/datastore.rb, line 217 def parent_key(parent_id) CloudDatastore.dataset.key('Parent' + name, parent_id.to_i) end
**************** private ****************
# File lib/active_model/datastore.rb, line 446 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.distinct_on(*options[:distinct_on]) if options[:distinct_on] query_property_filter(query, options) end
Adds property filters to the query if included in the options. Accepts individual or nested Arrays:
[['superseded', '=', false], ['email', '=', 'something']]
# File lib/active_model/datastore.rb, line 470 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
Adds sorting to the results by a property name if included in the options.
# File lib/active_model/datastore.rb, line 459 def query_sort(query, options) query.order(options[:order]) if options[:order] query.order(options[:desc_order], :desc) if options[:desc_order] query end
# File lib/active_model/datastore.rb, line 420 def retry_on_exception(max_retry_count = 5) retries = 0 sleep_time = 0.25 begin yield rescue Google::Cloud::Error => 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
# File lib/active_model/datastore.rb, line 403 def retry_on_exception?(max_retry_count = 5) retries = 0 sleep_time = 0.25 begin yield rescue Google::Cloud::Error => 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