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

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

      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:

      Unable to find source-code formatter for language: mongodb. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
      { 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?

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

              Created:
              Updated: