lib/mongoid/contextual/mongo.rb in mongoid-3.0.0.rc vs lib/mongoid/contextual/mongo.rb in mongoid-3.0.0
- old
+ new
@@ -31,10 +31,22 @@
def blank?
count == 0
end
alias :empty? :blank?
+ # Is the context cached?
+ #
+ # @example Is the context cached?
+ # context.cached?
+ #
+ # @return [ true, false ] If the context is cached.
+ #
+ # @since 3.0.0
+ def cached?
+ !!@cache
+ end
+
# Get the number of documents matching the query.
#
# @example Get the number of matching documents.
# context.count
#
@@ -112,29 +124,19 @@
# end
#
# @return [ Enumerator ] The enumerator.
#
# @since 3.0.0
- def each
+ def each(&block)
if block_given?
reset_length
selecting do
- if eager_loadable?
- docs = query.map{ |doc| Factory.from_db(klass, doc) }
- eager_load(docs)
- docs.each do |doc|
- yield doc
- increment_length
- end
- docs
- else
- query.each do |doc|
- yield Factory.from_db(klass, doc)
- increment_length
- end
- self
+ documents_for_iteration.each do |doc|
+ yield_and_increment(doc, &block)
end
+ @cache_loaded = true
+ eager_loadable? ? docs : self
end
else
to_enum
end
end
@@ -172,10 +174,11 @@
# @param [ Hash ] update The updates.
# @param [ Hash ] options The command options.
#
# @option options [ true, false ] :new Return the updated document.
# @option options [ true, false ] :remove Delete the first document.
+ # @option options [ true, false ] :upsert Create the document if it doesn't exist.
#
# @return [ Document ] The result of the command.
#
# @since 3.0.0
def find_and_modify(update, options = {})
@@ -205,11 +208,11 @@
#
# @param [ Criteria ] criteria The criteria.
#
# @since 3.0.0
def initialize(criteria)
- @criteria, @klass = criteria, criteria.klass
+ @criteria, @klass, @cache = criteria, criteria.klass, criteria.options[:cache]
add_type_selection
@query = klass.collection.find(criteria.selector)
apply_options
end
@@ -302,20 +305,20 @@
end
# Update all the matching documents atomically.
#
# @example Update all the matching documents.
- # context.update(name: "Smiths")
+ # context.update({ "$set" => { name: "Smiths" }})
#
# @param [ Hash ] attributes The new attributes for each document.
#
# @return [ nil, false ] False if no attributes were provided.
#
# @since 3.0.0
def update(attributes = nil)
return false unless attributes
- query.update_all({ "$set" => attributes })
+ query.update_all(attributes.__consolidate__)
end
alias :update_all :update
private
@@ -386,10 +389,22 @@
if spec = criteria.options[:sort]
query.sort(spec)
end
end
+ # Apply the hint option
+ #
+ # @example Apply the hint params.
+ # context.apply_hint
+ #
+ # @since 3.0.0
+ def apply_hint
+ if spec = criteria.options[:hint]
+ query.hint(spec)
+ end
+ end
+
# Map the inverse sort symbols to the correct MongoDB values.
#
# @example Apply the inverse sorting params.
# context.apply_inverse_sorting
#
@@ -400,10 +415,81 @@
else
query.sort({_id: -1})
end
end
+ # Is the cache able to be added to?
+ #
+ # @api private
+ #
+ # @example Is the context cacheable?
+ # context.cacheable?
+ #
+ # @return [ true, false ] If caching, and the cache isn't loaded.
+ #
+ # @since 3.0.0
+ def cacheable?
+ cached? && !cache_loaded?
+ end
+
+ # Is the cache fully loaded? Will be true if caching after one full
+ # iteration.
+ #
+ # @api private
+ #
+ # @example Is the cache loaded?
+ # context.cache_loaded?
+ #
+ # @return [ true, false ] If the cache is loaded.
+ #
+ # @since 3.0.0
+ def cache_loaded?
+ !!@cache_loaded
+ end
+
+ # Get the documents for cached queries.
+ #
+ # @api private
+ #
+ # @example Get the cached documents.
+ # context.documents
+ #
+ # @return [ Array<Document> ] The documents.
+ #
+ # @since 3.0.0
+ def documents
+ @documents ||= []
+ end
+
+ # Get the documents the context should iterate. This follows 3 rules:
+ #
+ # 1. If the query is cached, and we already have documents loaded, use
+ # them.
+ # 2. If we are eager loading, then eager load the documents and use
+ # those.
+ # 3. Use the query.
+ #
+ # @api private
+ #
+ # @example Get the documents for iteration.
+ # context.documents_for_iteration
+ #
+ # @return [ Array<Document>, Moped::Query ] The docs to iterate.
+ #
+ # @since 3.0.0
+ def documents_for_iteration
+ if cached? && !documents.empty?
+ documents
+ elsif eager_loadable?
+ docs = query.map{ |doc| Factory.from_db(klass, doc) }
+ eager_load(docs)
+ docs
+ else
+ query
+ end
+ end
+
# Eager load the inclusions for the provided documents.
#
# @example Eager load the inclusions.
# context.eager_load(docs)
#
@@ -412,33 +498,46 @@
# @return [ true ] Always true.
#
# @since 3.0.0
def eager_load(docs)
criteria.inclusions.reject! do |metadata|
- unless docs.empty?
- if metadata.stores_foreign_key?
- child_ids = load_ids(metadata.foreign_key).flatten
- metadata.eager_load(child_ids)
- else
- parent_ids = docs.map(&:id)
- metadata.eager_load(parent_ids)
- end
- end
+ metadata.eager_load(eager_loaded_ids(docs, metadata)) if !docs.empty?
end
self.eager_loaded = true
end
+ # Get the ids that to be used to eager load documents.
+ #
+ # @api private
+ #
+ # @example Get the ids.
+ # context.eager_load(docs, metadata)
+ #
+ # @param [ Array<Document> ] docs The pre-loaded documents.
+ # @param [ Metadata ] metadata The relation metadata.
+ #
+ # @return [ Array<Object> ] The ids.
+ #
+ # @since 3.0.0
+ def eager_loaded_ids(docs, metadata)
+ if metadata.stores_foreign_key?
+ load_ids(metadata.foreign_key).flatten
+ else
+ docs.map(&:id)
+ end
+ end
+
# Is this context able to be eager loaded?
#
# @example Is the context eager loadable?
# context.eager_loadable?
#
# @return [ true, false ] If the context is able to be eager loaded.
#
# @since 3.0.0
def eager_loadable?
- !eager_loaded && criteria.inclusions.any?
+ !eager_loaded && !criteria.inclusions.empty?
end
# Increment the length of the results.
#
# @api private
@@ -473,11 +572,11 @@
# @example Load the related ids.
# criteria.load_ids("person_id")
#
# @param [ String ] key The id or foriegn key string.
#
- # @return [ Array<String, BSON::ObjectId> ] The ids to load.
+ # @return [ Array<String, Moped::BSON::ObjectId> ] The ids to load.
#
# @since 3.0.0
def load_ids(key)
query.select(key => 1).map do |doc|
doc[key]
@@ -493,10 +592,11 @@
def apply_options
apply_fields
apply_limit
apply_skip
apply_sorting
+ apply_hint
end
# If we are limiting results, we need to set the field limitations on a
# thread local to avoid overriding the default values.
#
@@ -535,9 +635,28 @@
return nil unless document
doc = Factory.from_db(klass, document)
eager_load([ doc ]) if eager_loadable?
doc
end
+ end
+
+ # Yield to the document and increment the length.
+ #
+ # @api private
+ #
+ # @example Yield and increment.
+ # context.yield_and_increment(doc) do |doc|
+ # ...
+ # end
+ #
+ # @param [ Document ] document The document to yield to.
+ #
+ # @since 3.0.0
+ def yield_and_increment(document, &block)
+ doc = document.respond_to?(:_id) ? document : Factory.from_db(klass, document)
+ yield(doc)
+ increment_length
+ documents.push(doc) if cacheable?
end
end
end
end