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

Ability to store foreign keys on either or both sides of any association

    • Type: Icon: New Feature New Feature
    • Resolution: Unresolved
    • Priority: Icon: Minor - P4 Minor - P4
    • None
    • Affects Version/s: None
    • Component/s: Associations
    • Labels:

      Currently Mongoid offers a number of association macros, which ultimately derive from what was made available by ActiveRecord for relational databases. These are belongs_to/has_one/has_many/HABTM.

      Associations as implemented by AR for relational databases have two properties:

      1. References (foreign keys) are stored once for a given link. In HABTM this is achieved through an additional table ("join table"), otherwise the foreign key is stored on exactly one side of the association.
      2. The promulgated solution to having (more) complex behavior on the association is to add an explicit join model, and possibly the join table, to associations that didn't already have them.

      MongoDB and Mongoid differ from AR/relational databases in these two areas:

      1. Mongoid supports embedded associations, where data for different models is stored in the same collection. In this arrangement there are no foreign keys at all.
      2. In a HABTM association in Mongoid, foreign keys are stored on both sides of the association.
      3. In general Mongoid schemas do not have join tables/join models, and instead use embedding.

      There are some valid use cases that Mongoid does not presently address via its association macros.

      HABTM with small and large collection

      Suppose there is a HABTM association between collection A with 10 documents and collection B with 1,000,000 documents. The foreign key fields on B would reference only a couple of documents in A, but foreign keys in A may reference tens of thousands of documents in B and would be very unwieldy, both in terms of occupied storage and in terms of CPU overhead for managing them.

      A possible solution is to use `inverse_of: nil` with HABTM, which omits defining the association one one of the sides. However, this also foregoes being able to query the association from one side. In the small and large collection use case being considered, ideal behavior is to have association methods on both sides of the association but only store the data once (in B), with methods on A performing (indexed) queries on B to go from A to B rather than reading the list of Bs in each A.

      has_many with cache

      When defining a has_many/belongs_to association pair (say, C has_many Ds), the foreign keys are stored once on the belongs_to side. However we may want to also store foreign keys on the has_many side for performance reasons, to avoid having to query D to go from C to Ds and instead read Ds directly off of a C. This is essentially opposite to the HABTM use case described above.

      Proposal
      =====

      The proposal is to permit an application using Mongoid to request that foreign key data be stored on one or both ends of an association, for all non-embedded associations. For has_one/has_many this means storing additional data (and keeping it in sync), for HABTM this means not duplicating the data and executing queries on collections instead of reading attributes.

      It does not make sense to not store foreign key data at all, as then the association doesn't work. For all other association types the foreign key data should be storable 1 time or 2 times.

      Attached spreadsheet proposes new macro names that cover all of the cases. The proposed syntax is fully backwards compatible; existing Mongoid apps would work without any behavior change.

      As mentioned above, embedded associations are unaffected by this proposal.

            Assignee:
            Unassigned Unassigned
            Reporter:
            johnnyshields Johnny Shields
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: