# encoding: utf-8 module Mongoid module Atomic # This module is responsible for taking update selectors and switching out # the indexes for the $ positional operator where appropriate. # # @since 3.1.0 module Positionable # Takes the provided selector and atomic operations and replaces the # indexes of the embedded documents with the positional operator when # needed. # # @note The only time we can accurately know when to use the positional # operator is at the exact time we are going to persist something. So # we can tell by the selector that we are sending if it is actually # possible to use the positional operator at all. For example, if the # selector is: { "_id" => 1 }, then we could not use the positional # operator for updating embedded documents since there would never be a # match - we base whether we can based on the number of levels deep the # selector goes, and if the id values are not nil. # # @example Process the operations. # positionally( # { "_id" => 1, "addresses._id" => 2 }, # { "$set" => { "addresses.0.street" => "hobrecht" }} # ) # # @param [ Hash ] selector The selector. # @param [ Hash ] operations The update operations. # @param [ Hash ] processed The processed update operations. # # @return [ Hash ] The new operations. # # @since 3.1.0 def positionally(selector, operations, processed = {}) if selector.size == 1 || selector.values.any? { |val| val.nil? } return operations end keys = selector.keys.map{ |m| m.sub('._id','') } - ['_id'] keys = keys.sort_by { |s| s.length*-1 } process_operations(keys, operations, processed) end private def process_operations(keys, operations, processed) operations.each_pair do |operation, update| processed[operation] = process_updates(keys, update) end processed end def process_updates(keys, update, updates = {}) update.each_pair do |position, value| updates[replace_index(keys, position)] = value end updates end def replace_index(keys, position) # replace to $ only if that key is on the selector keys.each do |kk| if position =~ /^#{kk}\.\d+\.(.*)/ return "#{kk}.$.#{$1}" end end position end end end end