Skip to content

Many-to-many associations work incorrectly when there is similar foreign key #434

Description

@hieuk09

Describe the bug

I have table books with many-to-many relationship with authors via book_authors. However, I also have a column author_id on books to denote the main author.

When querying associations using through:, rom-sql uses the column author_id to combine the associations when query, results in incorrect output.

To Reproduce

Run the below script

require 'rom'

rom = ROM.container(:sql, 'sqlite::memory') do |config|
  config.default.create_table(:books) do
    primary_key :id
    foreign_key :author_id, :authors
  end

  config.default.create_table(:authors) do
    primary_key :id
  end

  config.default.create_table(:book_authors) do
    primary_key :id
    foreign_key :book_id, :books
    foreign_key :author_id, :authors
  end

  config.relation(:books) do
    schema(infer: true) do
      associations do
        has_many :book_authors
      end
    end

    auto_struct true
  end

  config.relation(:authors) do
    schema(infer: true) do
      associations do
        has_many :books, through: :book_authors
      end
    end

    auto_struct true
  end

  config.relation(:book_authors) do
    schema(infer: true) do
      associations do
        belongs_to :book
        belongs_to :author
      end
    end

    auto_struct true
  end
end

class AuthorRepo < ROM::Repository[:authors]
  def all_with_books
    root.combine(books: :book_authors).to_a
  end
end

gateway = rom.gateways[:default]
author_repo = AuthorRepo.new(container: rom)

gateway[:authors].insert(id: 1)
gateway[:authors].insert(id: 2)
gateway[:books].insert(id: 1, author_id: 2)
gateway[:books].insert(id: 2, author_id: 2)
gateway[:book_authors].insert(id: 1, book_id: 1, author_id: 1)
gateway[:book_authors].insert(id: 2, book_id: 2, author_id: 2)
gateway[:book_authors].insert(id: 3, book_id: 1, author_id: 2)

puts author_repo.all_with_books.inspect

# output: 
# [#<ROM::Struct::Author id=1 books=[]>, #<ROM::Struct::Author id=2 books=[#<ROM::Struct::Book id=1 author_id=2 book_authors=[#<ROM::Struct::BookAuthor id=1 book_id=1 author_id=1>, #<ROM::Struct::BookAuthor id=3 book_id=1 author_id=2>]>, #<ROM::Struct::Book id=1 author_id=2 book_authors=[#<ROM::Struct::BookAuthor id=1 book_id=1 author_id=1>, #<ROM::Struct::BookAuthor id=3 book_id=1 author_id=2>]>, #<ROM::Struct::Book id=2 author_id=2 book_authors=[#<ROM::Struct::BookAuthor id=2 book_id=2 author_id=2>]>]>]

Expected behavior

Author 1 should also have one book from the book_authors record 1

My environment

  • Affects my production application: YES
  • Ruby version: 3.3
  • OS: MacOS

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions