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

Counter cache not decremented when nullifying relation/association

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Minor - P4 Minor - P4
    • 8.0.1
    • Affects Version/s: 5.2.1
    • Component/s: Associations, Persistence
    • Labels:
      None
    • Environment:
      ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin16]
      Rails 4.2.10
    • Minor Change

      Counter cache is not correctly updated (i.e. decremented) when the relation is nullified using update_attribute

      After looking in the code, it seems that CounterCache update callback is broken in this case, because the outermost 'if' block prevents the execution when the updated attribute is null (whereas the callback is executed after the update)

      Issue found in v5, but by code reading (no test) I would say that the bug is also present in v6 and v7 branches

      Here is some steps to reproduce, everything goes well (association, re-association) until final attribute update (de-association)

      [1] pry(main)> class B
      [1] pry(main)*   include Mongoid::Document
      [1] pry(main)* end
      => B
      [2] pry(main)> class A
      [2] pry(main)*   include Mongoid::Document
      [2] pry(main)*   belongs_to :b, counter_cache: true
      [2] pry(main)* end
      => {:relation=>Mongoid::Relations::Referenced::In,
       :extend=>nil,
       :inverse_class_name=>"A",
       :name=>:b,
       :counter_cache=>true,
       :validate=>false}
      [3] pry(main)> class B
      [3] pry(main)*   include Mongoid::Document
      [3] pry(main)*   has_many :as
      [3] pry(main)*   field :as_count, type: Integer, default: 0
      [3] pry(main)* end
      => #<Mongoid::Fields::Standard:0x007f9d1ff71368
       @default_val=0,
       @label=nil,
       @name="as_count",
       @options={:type=>Integer, :default=>0, :klass=>B},
       @pre_processed=true>
      [4] pry(main)> a = A.create
      => #<A _id: 5a7b0efdce735f25ce2ae942, b_id: nil>
      [5] pry(main)> b1 = B.create
      => #<B _id: 5a7b0f04ce735f25ce2ae943, as_count: 0>
      [6] pry(main)> a.update_attribute(:b, b1)
      => true
      [7] pry(main)> b1.reload.as
      => [#<A _id: 5a7b0efdce735f25ce2ae942, b_id: BSON::ObjectId('5a7b0f04ce735f25ce2ae943')>]
      [8] pry(main)> b1.as_count
      => 1
      [9] pry(main)> b2 = B.create
      => #<B _id: 5a7b0f37ce735f25ce2ae944, as_count: 0>
      [10] pry(main)> a.update_attribute(:b, b2)
      => true
      [11] pry(main)> b1.reload.as
      => []
      [12] pry(main)> b1.as_count
      => 0
      [13] pry(main)> b2.reload.as
      => [#<A _id: 5a7b0efdce735f25ce2ae942, b_id: BSON::ObjectId('5a7b0f37ce735f25ce2ae944')>]
      [14] pry(main)> b2.as_count
      => 1
      [15] pry(main)> a.update_attribute(:b, nil)
      => true
      [16] pry(main)> b2.reload.as
      => []
      [17] pry(main)> b2.as_count
      => 1
      [18] pry(main)> b2.reset_counters(:as)
      => [:as]
      [19] pry(main)> b2.as_count
      => 0
      

            Assignee:
            neil.shweky@mongodb.com Neil Shweky (Inactive)
            Reporter:
            laurent_dazzl Laurent Cogné
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: