class Mongoid::Contextual::Mongo
Constants
- OPTIONS
Options constant.
@since 5.0.0
Attributes
@attribute [r] view The Mongo
collection view.
Public Class Methods
Create the new Mongo
context. This delegates operations to the underlying driver.
@example Create the new context.
Mongo.new(criteria)
@param [ Criteria
] criteria The criteria.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 364 def initialize(criteria) @criteria, @klass, @cache = criteria, criteria.klass, criteria.options[:cache] @collection = @klass.collection criteria.send(:merge_type_selection) @view = collection.find(criteria.selector, session: _session) apply_options end
Public Instance Methods
Is the context cached?
@example Is the context cached?
context.cached?
@return [ true, false ] If the context is cached.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 49 def cached? !!@cache end
Get the number of documents matching the query.
@example Get the number of matching documents.
context.count
@example Get the count of documents with the provided options.
context.count(limit: 1)
@example Get the count for where the provided block is true.
context.count do |doc| doc.likes > 1 end
@param [ Hash ] options The options, such as skip and limit to be factored
into the count.
@return [ Integer ] The number of matches.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 72 def count(options = {}, &block) return super(&block) if block_given? try_cache(:count) { view.count_documents(options) } end
Delete all documents in the database that match the selector.
@example Delete all the documents.
context.delete
@return [ nil ] Nil.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 104 def delete view.delete_many.deleted_count end
Destroy all documents in the database that match the selector.
@example Destroy all the documents.
context.destroy
@return [ nil ] Nil.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 117 def destroy each.inject(0) do |count, doc| doc.destroy count += 1 if acknowledged_write? count end end
Get the distinct values in the db for the provided field.
@example Get the distinct values.
context.distinct(:name)
@param [ String, Symbol ] field The name of the field.
@return [ Array<Object> ] The distinct values for the field.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 136 def distinct(field) view.distinct(klass.database_field_name(field)).map do |value| value.class.demongoize(value) end end
Iterate over the context. If provided a block, yield to a Mongoid
document for each, otherwise return an enum.
@example Iterate over the context.
context.each do |doc| puts doc.name end
@return [ Enumerator ] The enumerator.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 153 def each(&block) if block_given? documents_for_iteration.each do |doc| yield_document(doc, &block) end @cache_loaded = true self else to_enum end end
Get the estimated number of documents matching the query.
Unlike count, estimated_count
does not take a block because it is not traditionally defined (with a block) on Enumarable like count is.
@example Get the estimated number of matching documents.
context.estimated_count
@param [ Hash ] options The options, such as maxTimeMS to be factored
into the count.
@return [ Integer ] The number of matches.
# File lib/mongoid/contextual/mongo.rb, line 89 def estimated_count(options = {}) unless self.criteria.selector.empty? raise Mongoid::Errors::InvalidEstimatedCountCriteria.new(self.klass) end try_cache(:estimated_count) { view.estimated_document_count(options) } end
Do any documents exist for the context.
@example Do any documents exist for the context.
context.exists?
@note We don’t use count here since Mongo
does not use counted
b-tree indexes, unless a count is already cached then that is used to determine the value.
@return [ true, false ] If the count is more than zero.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 177 def exists? return !documents.empty? if cached? && cache_loaded? return @count > 0 if instance_variable_defined?(:@count) try_cache(:exists) do !!(view.projection(_id: 1).limit(1).first) end end
Run an explain on the criteria.
@example Explain the criteria.
Band.where(name: "Depeche Mode").explain
@return [ Hash ] The explain result.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 194 def explain view.explain end
Return the first result without applying sort
@api private
@since 4.0.2
# File lib/mongoid/contextual/mongo.rb, line 299 def find_first return documents.first if cached? && cache_loaded? if raw_doc = view.first doc = Factory.from_db(klass, raw_doc, criteria) eager_load([doc]).first end end
Execute the find and modify command, used for MongoDB’s $findAndModify. This deletes the found document.
@example Execute the command.
context.find_one_and_delete
@return [ Document
] The result of the command.
@since 5.0.0
# File lib/mongoid/contextual/mongo.rb, line 251 def find_one_and_delete if doc = view.find_one_and_delete Factory.from_db(klass, doc) end end
Execute the find and modify command, used for MongoDB’s $findAndModify.
@example Execute the command.
context.find_one_and_update({ likes: 1 })
@param [ Hash ] replacement The replacement. @param [ Hash ] options The command options.
@option options [ :before, :after ] :return_document Return the updated document
from before or after update.
@option options [ true, false ] :upsert Create the document if it doesn’t exist.
@return [ Document
] The result of the command.
@since 5.0.0
# File lib/mongoid/contextual/mongo.rb, line 236 def find_one_and_replace(replacement, options = {}) if doc = view.find_one_and_replace(replacement, options) Factory.from_db(klass, doc) end end
Execute the find and modify command, used for MongoDB’s $findAndModify.
@example Execute the command.
context.find_one_and_update({ "$inc" => { likes: 1 }})
@param [ Hash ] update The updates. @param [ Hash ] options The command options.
@option options [ :before, :after ] :return_document Return the updated document
from before or after update.
@option options [ true, false ] :upsert Create the document if it doesn’t exist.
@return [ Document
] The result of the command.
@since 5.0.0
# File lib/mongoid/contextual/mongo.rb, line 214 def find_one_and_update(update, options = {}) if doc = view.find_one_and_update(update, options) Factory.from_db(klass, doc) end end
Get the first document in the database for the criteria’s selector.
@example Get the first document.
context.first
@note Automatically adding a sort on _id when no other sort is
defined on the criteria has the potential to cause bad performance issues. If you experience unexpected poor performance when using #first or #last and have no sort defined on the criteria, use the option { id_sort: :none }. Be aware that #first/#last won't guarantee order in this case.
@param [ Hash ] opts The options for the query returning the first document.
@option opts [ :none ] :id_sort Don’t apply a sort on _id if no other sort
is defined on the criteria.
@return [ Document
] The first document.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 276 def first(opts = {}) return documents.first if cached? && cache_loaded? try_cache(:first) do if sort = view.sort || ({ _id: 1 } unless opts[:id_sort] == :none) if raw_doc = view.sort(sort).limit(1).first doc = Factory.from_db(klass, raw_doc, criteria) eager_load([doc]).first end else if raw_doc = view.limit(1).first doc = Factory.from_db(klass, raw_doc, criteria) eager_load([doc]).first end end end end
Execute a $geoNear command against the database.
@example Find documents close to 10, 10.
context.geo_near([ 10, 10 ])
@example Find with spherical distance.
context.geo_near([ 10, 10 ]).spherical
@example Find with a max distance.
context.geo_near([ 10, 10 ]).max_distance(0.5)
@example Provide a distance multiplier.
context.geo_near([ 10, 10 ]).distance_multiplier(1133)
@param [ Array<Float> ] coordinates The coordinates.
@return [ GeoNear
] The GeoNear
command.
@deprecated
@since 3.1.0
# File lib/mongoid/contextual/mongo.rb, line 328 def geo_near(coordinates) GeoNear.new(collection, criteria, coordinates) end
Get the last document in the database for the criteria’s selector.
@example Get the last document.
context.last
@note Automatically adding a sort on _id when no other sort is
defined on the criteria has the potential to cause bad performance issues. If you experience unexpected poor performance when using #first or #last and have no sort defined on the criteria, use the option { id_sort: :none }. Be aware that #first/#last won't guarantee order in this case.
@param [ Hash ] opts The options for the query returning the first document.
@option opts [ :none ] :id_sort Don’t apply a sort on _id if no other sort
is defined on the criteria.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 391 def last(opts = {}) try_cache(:last) do with_inverse_sorting(opts) do if raw_doc = view.limit(1).first doc = Factory.from_db(klass, raw_doc, criteria) eager_load([doc]).first end end end end
Get’s the number of documents matching the query selector.
@example Get the length.
context.length
@return [ Integer ] The number of documents.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 410 def length @length ||= self.count end
Limits the number of documents that are returned from the database.
@example Limit the documents.
context.limit(20)
@param [ Integer ] value The number of documents to return.
@return [ Mongo
] The context.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 425 def limit(value) @view = view.limit(value) and self end
Invoke the block for each element of Contextual
. Create a new array containing the values returned by the block.
If the symbol field name is passed instead of the block, additional optimizations would be used.
@example Map by some field.
context.map(:field1)
@example Map with block.
context.map(&:field1)
@param [ Symbol ] field The field name.
@return [ Array ] The result of mapping.
# File lib/mongoid/contextual/mongo.rb, line 347 def map(field = nil, &block) if block_given? super(&block) else criteria.pluck(field) end end
Initiate a map/reduce operation from the context.
@example Initiate a map/reduce.
context.map_reduce(map, reduce)
@param [ String ] map The map js function. @param [ String ] reduce The reduce js function.
@return [ MapReduce
] The map/reduce lazy wrapper.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 440 def map_reduce(map, reduce) MapReduce.new(collection, criteria, map, reduce) end
Pluck the single field values from the database. Will return duplicates if they exist and only works for top level fields.
@example Pluck a field.
context.pluck(:_id)
@note This method will return the raw db values - it performs no custom
serialization.
@param [ String, Symbol, Array ] fields Fields
to pluck.
@return [ Array<Object, Array> ] The plucked values.
@since 3.1.0
# File lib/mongoid/contextual/mongo.rb, line 458 def pluck(*fields) normalized_select = fields.inject({}) do |hash, f| hash[klass.database_field_name(f)] = 1 hash end view.projection(normalized_select).reduce([]) do |plucked, doc| values = normalized_select.keys.map do |n| n =~ /\./ ? doc[n.partition('.')[0]] : doc[n] end plucked << (values.size == 1 ? values.first : values) end end
Skips the provided number of documents.
@example Skip the documents.
context.skip(20)
@param [ Integer ] value The number of documents to skip.
@return [ Mongo
] The context.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 482 def skip(value) @view = view.skip(value) and self end
Sorts the documents by the provided spec.
@example Sort the documents.
context.sort(name: -1, title: 1)
@param [ Hash ] values The sorting values as field/direction(1/-1)
pairs.
@return [ Mongo
] The context.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 497 def sort(values = nil, &block) if block_given? super(&block) else # update the criteria @criteria = criteria.order_by(values) apply_option(:sort) self end end
Update the first matching document atomically.
@example Update the first matching document.
context.update({ "$set" => { name: "Smiths" }})
@param [ Hash ] attributes The new attributes for the document. @param [ Hash ] opts The update operation options.
@option opts [ Array ] :array_filters A set of filters specifying to which array elements
an update should apply.
@return [ nil, false ] False if no attributes were provided.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 522 def update(attributes = nil, opts = {}) update_documents(attributes, :update_one, opts) end
Update all the matching documents atomically.
@example Update all the matching documents.
context.update_all({ "$set" => { name: "Smiths" }})
@param [ Hash ] attributes The new attributes for each document. @param [ Hash ] opts The update operation options.
@option opts [ Array ] :array_filters A set of filters specifying to which array elements
an update should apply.
@return [ nil, false ] False if no attributes were provided.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 540 def update_all(attributes = nil, opts = {}) update_documents(attributes, :update_many, opts) end
Private Instance Methods
# File lib/mongoid/contextual/mongo.rb, line 735 def _session @criteria.send(:_session) end
# File lib/mongoid/contextual/mongo.rb, line 739 def acknowledged_write? collection.write_concern.nil? || collection.write_concern.acknowledged? end
Apply the field limitations.
@api private
@example Apply the field limitations.
context.apply_fields
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 591 def apply_fields if spec = criteria.options[:fields] @view = view.projection(spec) end end
Apply an option.
@api private
@example Apply the skip option.
context.apply_option(:skip)
@since 3.1.0
# File lib/mongoid/contextual/mongo.rb, line 623 def apply_option(name) if spec = criteria.options[name] @view = view.send(name, spec) end end
Apply the options.
@api private
@example Apply all options.
context.apply_options
@since 3.1.0
# File lib/mongoid/contextual/mongo.rb, line 605 def apply_options apply_fields OPTIONS.each do |name| apply_option(name) end if criteria.options[:timeout] == false @view = view.no_cursor_timeout end 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
# File lib/mongoid/contextual/mongo.rb, line 673 def cache_loaded? !!@cache_loaded 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
# File lib/mongoid/contextual/mongo.rb, line 658 def cacheable? cached? && !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
# File lib/mongoid/contextual/mongo.rb, line 687 def documents @documents ||= [] end
Get the documents the context should iterate. This follows 3 rules:
-
If the query is cached, and we already have documents loaded, use
them.
-
If we are eager loading, then eager load the documents and use
those.
-
Use the query.
@api private
@example Get the documents for iteration.
context.documents_for_iteration
@return [ Array<Document>, Mongo::Collection::View ] The docs to iterate.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 707 def documents_for_iteration return documents if cached? && !documents.empty? return view unless eager_loadable? docs = view.map{ |doc| Factory.from_db(klass, doc, criteria) } eager_load(docs) end
yield the block given or return the cached value
@param [ String, Symbol ] key The instance variable name
@return the result of the block
@since 3.1.4
# File lib/mongoid/contextual/mongo.rb, line 553 def try_cache(key, &block) unless cached? yield else unless ret = instance_variable_get("@#{key}") instance_variable_set("@#{key}", ret = yield) end ret end end
Update the documents for the provided method.
@api private
@example Update the documents.
context.update_documents(attrs)
@param [ Hash ] attributes The updates. @param [ Symbol ] method The method to use.
@return [ true, false ] If the update succeeded.
@since 3.0.4
# File lib/mongoid/contextual/mongo.rb, line 577 def update_documents(attributes, method = :update_one, opts = {}) return false unless attributes attributes = Hash[attributes.map { |k, v| [klass.database_field_name(k.to_s), v] }] view.send(method, attributes.__consolidate__(klass), opts) end
Map the inverse sort symbols to the correct MongoDB values.
@api private
@example Apply the inverse sorting params to the given block
context.with_inverse_sorting
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 637 def with_inverse_sorting(opts = {}) begin if sort = criteria.options[:sort] || ( { _id: 1 } unless opts[:id_sort] == :none ) @view = view.sort(Hash[sort.map{|k, v| [k, -1*v]}]) end yield ensure apply_option(:sort) end end
Yield to the document.
@api private
@example Yield the document.
context.yield_document(doc) do |doc| ... end
@param [ Document
] document The document to yield to.
@since 3.0.0
# File lib/mongoid/contextual/mongo.rb, line 726 def yield_document(document, &block) doc = document.respond_to?(:_id) ? document : Factory.from_db(klass, document, criteria) yield(doc) documents.push(doc) if cacheable? end