class Mongoid::Contextual::Mongo

Constants

OPTIONS

Options constant.

@since 5.0.0

Attributes

view[R]

@attribute [r] view The Mongo collection view.

Public Class Methods

new(criteria) click to toggle source

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

cached?() click to toggle source

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
count(options = {}, &block) click to toggle source

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

Calls superclass method
# 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() click to toggle source

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
Also aliased as: delete_all
delete_all()
Alias for: delete
destroy() click to toggle source

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
Also aliased as: destroy_all
destroy_all()
Alias for: destroy
distinct(field) click to toggle source

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
each(&block) click to toggle source

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
estimated_count(options = {}) click to toggle source

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
exists?() click to toggle source

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
explain() click to toggle source

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
find_first() click to toggle source

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
find_one_and_delete() click to toggle source

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
find_one_and_replace(replacement, options = {}) click to toggle source

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
find_one_and_update(update, options = {}) click to toggle source

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
first(opts = {}) click to toggle source

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
Also aliased as: one
geo_near(coordinates) click to toggle source

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
last(opts = {}) click to toggle source

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
length() click to toggle source

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
Also aliased as: size
limit(value) click to toggle source

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
map(field = nil, &block) click to toggle source

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.

Calls superclass method
# File lib/mongoid/contextual/mongo.rb, line 347
def map(field = nil, &block)
  if block_given?
    super(&block)
  else
    criteria.pluck(field)
  end
end
map_reduce(map, reduce) click to toggle source

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
one(opts = {})
Alias for: first
pluck(*fields) click to toggle source

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
size()
Alias for: length
skip(value) click to toggle source

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
sort(values = nil, &block) click to toggle source

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

Calls superclass method
# 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(attributes = nil, opts = {}) click to toggle source

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(attributes = nil, opts = {}) click to toggle source

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

_session() click to toggle source
# File lib/mongoid/contextual/mongo.rb, line 735
def _session
  @criteria.send(:_session)
end
acknowledged_write?() click to toggle source
# File lib/mongoid/contextual/mongo.rb, line 739
def acknowledged_write?
  collection.write_concern.nil? || collection.write_concern.acknowledged?
end
apply_fields() click to toggle source

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_option(name) click to toggle source

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_options() click to toggle source

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
cache_loaded?() click to toggle source

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
cacheable?() click to toggle source

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
documents() click to toggle source

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
documents_for_iteration() click to toggle source

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.
  1. If we are eager loading, then eager load the documents and use

those.
  1. 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
try_cache(key) { || ... } click to toggle source

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_documents(attributes, method = :update_one, opts = {}) click to toggle source

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
with_inverse_sorting(opts = {}) { || ... } click to toggle source

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_document(document) { |doc| ... } click to toggle source

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