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

Duplicate embedded documents

    • Type: Icon: Bug Bug
    • Resolution: Done
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: None
    • Component/s: None
    • Labels:

      I've been observing some weird behavior in Mongoid involving the creation of multiple embedded documents. In this vast majority of cases this does not occur, but it does happen often enough to be a cause for concern. Here are the details of the latest case that I've found.

      The parent document is called Membership:

      class Membership
        include Mongoid::Document
      
        belongs_to :user
        belongs_to :pool
        embeds_many :picks
      
        validates_presence_of :user_id, :pool_id
        validates_uniqueness_of :user_id, scope: :pool_id
      
        ...
      

      The embedded document is called Pick:

      
      class Pick
        include Mongoid::Document
        include Mongoid::BackboneSerialization
      
        field :game_id, type: BSON::ObjectId
        field :team_id, type: BSON::ObjectId
        field :points, type: Integer # Cached value
      
        embedded_in :membership
      
        validates_presence_of :game, :team
        validates_uniqueness_of :game_id
        validate :enforce_immutability
      
        ...
      
        private
          def enforce_immutability
            if locked? and team_id_changed?
              errors.add(:team, 'cannot be changed because the pick is locked')
            end
          end
      end
      

      That Mongoid::BackboneSerialization mixin is simply the following:

      
      module Mongoid
        module BackboneSerialization
          extend ActiveSupport::Concern
      
          module InstanceMethods
            def serializable_hash(options = nil)
              persisted? ? super.merge('id' => _id) : super
            end
          end
        end
      end
      

      So, as for the occurrence of the behavior I'm current observing:

      ruby-1.9.2-p290 :001 > membership = Membership.find('4e68f7607d2c290001000147')
       => #<Membership _id: 4e68f7607d2c290001000147, _type: nil, user_id: BSON::ObjectId('4e68f72a7d2c290001000142'), pool_id: BSON::ObjectId('4e682217749462000100009d')>
      ruby-1.9.2-p290 :002 > picks = membership.picks.where(_id: '4e73e54f9775e50001000003')
       => #<Mongoid::Criteria
        selector: {:_id=>BSON::ObjectId('4e73e54f9775e50001000003')},
        options:  {},
        class:    Pick,
        embedded: true>
      ruby-1.9.2-p290 :003 > picks.length
       => 6
      ruby-1.9.2-p290 :004 > picks.first
       => #<Pick _id: 4e73e54f9775e50001000003, _type: nil, game_id: BSON::ObjectId('4e5d08ae7c0b5f0001000064'), team_id: BSON::ObjectId('4e5d01517c0b5f0001000009'), points: nil>
      ruby-1.9.2-p290 :005 > picks.first.valid?
       => true
      ruby-1.9.2-p290 :006 > membership.picks.create!(game_id: '4e5d08ae7c0b5f0001000064', team_id: '4e5d01517c0b5f0001000009')
      Mongoid::Errors::Validations: Validation failed - Game is already taken.
      

      Also, I confirmed through the mongo shell the there are indeed 6 embedded documents with the same _id

      All interaction in the application with the memberships collection and the embedded picks (and, for that matter, MongoDB in general) is through documented Mongoid APIs (#save, #build, #create, #update_attributes). The application is quite simple, and I'm not doing anything that would be considered even slightly exotic.

      I'm running Ruby 1.9.2-p290, Rails 3.1, Mongo (gem) 1.3.1, Mongoid 2.2.0, MongoDB 1.8.2.

      I've only encountered this in the Heroku production environment (Cedar stack), where I'm using MongoHQ (also on 1.8.2).

      Any help would be greatly appreciated. Thanks!

            Assignee:
            emily.stolfo Emily Stolfo
            Reporter:
            khy khy
            Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: