Skip to content

MONGOID-6001 Handle empty has_and_belongs_to_many foreign key arrays - #6171

Open
ujihisa wants to merge 3 commits into
mongodb:masterfrom
ujihisa:habtm-fix
Open

ujihisa wants to merge 3 commits into
mongodb:masterfrom
ujihisa:habtm-fix

Conversation

@ujihisa

@ujihisa ujihisa commented Sep 3, 2026

Copy link
Copy Markdown

Description

Mongoid 9.1.0 raises NoMethodError when a has_and_belongs_to_many association stores an empty foreign-key array and the association is reset before it is enumerated. Mongoid 9.0.9 does not reproduce the issue.

Mongoid itself can create this data. For example, Team.create!(user_ids: []) stores an explicit empty array.

Root cause

The array-of-hashes $lookup branch in HasAndBelongsToMany::Buildable#build was added by MONGOID-5731 in pull request 6081. Because [].all? { |o| o.is_a?(Hash) } returns true, an empty persisted foreign-key array is treated as $lookup output and returned as a plain Array. After the association proxy is reset, HasMany::Enumerable has no unloaded criteria and can call methods on a missing criteria object.

Fix

The $lookup branch now accepts only non-empty arrays of hashes. For an empty foreign-key array, the code passes nil to query_criteria, which returns crit.none. This keeps the association safe to reset and lets its target empty?, size, and enumeration methods return an empty result without querying the database, even when the general empty-$in short-circuit option is disabled. Proxy count and exists? are outside this guarantee because they build criteria from the stored IDs.

Scope

Some Array-backed targets from $lookup can still fail after reset, including non-empty has_and_belongs_to_many results and empty has_many results. That issue predates this regression and is outside this change. A follow-up should harden HasMany::Enumerable for Array targets.

Tests

The tests cover:

  • Building a has_and_belongs_to_many association from an empty foreign-key array returns an empty Mongoid::Criteria.
  • Team.create!(user_ids: []) persists the empty array, and the same association proxy can be reset and used with empty?, size, and enumeration without a query.
  • Existing includes/preload tests cover empty eager-loaded results without an extra query.
  • Existing tests continue to cover non-empty ID arrays and arrays of $lookup documents.

Verification

  • The focused association and integration specs pass: 1499 examples, 0 failures.
  • RuboCop is clean.

Related Mongoid tickets

MONGOID-5030 covers short-circuiting queries whose $in value is an empty array. It provides related context, but it is not the same regression.

@ujihisa
ujihisa requested a review from a team as a code owner September 3, 2026 23:27
@ujihisa ujihisa changed the title MONGOID-6000 Handle empty has_and_belongs_to_many foreign key arrays MONGOID-6001 Handle empty has_and_belongs_to_many foreign key arrays Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant