Uploaded image for project: 'Mongoid'
  1. Mongoid
  2. MONGOID-4784

Use filtered positional update operator to update nested arrays of embedded documents

    XMLWordPrintable

Details

    Description

      In MongoDB 3.6, filtered positional update operator was added as described here: https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#update-nested-arrays-in-conjunction-with. This operator permits find-and-modify operations on nested arrays of embedded documents - specifically, it is free of the restriction of the positional update operator (https://docs.mongodb.com/manual/reference/operator/update/positional/) which only works with one level of nested arrays.

      Mongoid should use the filtered positional update operator to solve the following reported problem:


      This seems to have been fixed before but it doesn't use the positional operator when an embedded document has embedded documents.

      require 'mongoid'
      Mongoid.connect_to 'embedded_positional_operators_test'
      class Team
       include Mongoid::Document
      embeds_many :players
      field :name, type: String, default: ''
      end
      class Player
       include Mongoid::Document
       embeds_many :previous_teams
       embedded_in :team, inverse_of: :players
      field :name, type: String, default: ''
      end
      class PreviousTeam
       include Mongoid::Document
      embedded_in :player, inverse_of: :previous_teams
      field :name, type: String, default: ''
       field :year, type: String, default: ''
      end
      Team.delete_all
      Team.create!(name: 'Falcons')
      falcons_instance_1 = Team.first
      falcons_instance_2 = Team.first
      falcons_instance_1.players.create!(name: 'Vidur', previous_teams: [{name: 'Eagles', year: '2018'}])
      falcons_instance_2.players.create!(name: 'Jonathon', previous_teams: [{name: 'Paaaaaanthers', year: '2018'}])
      me = falcons_instance_2.players.find_by(name: 'Jonathon')
      # me.previous_teams.find_or_initialize_by(year: '2018').update(name: 'Panthers')
      me.previous_teams.find_or_initialize_by(year: '2018').assign_attributes(name: 'Panthers')
      me.save
      Team.first.players.each do |player|
       previous_teams = player.previous_teams.each.map(&:name).join(', ')
       puts "Player Name: #{player.name}, Previous Teams: #{previous_teams}"
      end
      

       
      So the expected print would be:
      Player Name: Vidur, Previous Teams: Eagles
      Player Name: Jonathon, Previous Teams: Panthers
       
      but it returns:
      Player Name: Vidur, Previous Teams: Panthers
      Player Name: Jonathon, Previous Teams: Paaaaaanthers

      it looks like the query being created is:

      { q: { _id: ObjectId('5db0a55728f9d506e57a336d'), players._id: ObjectId('5db0a55728f9d506e57a3370') }, u: { $set: { players.0.previous_teams.0.name: "Panthers" } }
      

      And that first 0 should be the positional operator $.

      I looked through the code and found it was being done here and in there I see it uses positional operator only when nesting 1 level deep. Looking at the commit that added that im guessing its needed when the embedded document with the same name?

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              jonathongardner Jonathon Gardner
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated: