uptrace / bun

SQL-first Golang ORM
https://bun.uptrace.dev
BSD 2-Clause "Simplified" License
3.46k stars 206 forks source link

m2m relations with composite keys #992

Open ygabuev opened 1 month ago

ygabuev commented 1 month ago

I am trying to have a many-to-many relation on models with composite primary keys. Here is a concrete example:

CREATE TABLE orders (id int, shop_id int);
CREATE TABLE items (id int, shop_id int);
CREATE TABLE orders_to_items (shop_id int, order_id int, item_id int);

INSERT INTO orders VALUES (1, 22);
INSERT INTO items VALUES (11, 22);
INSERT INTO orders_to_items VALUES (22, 1, 11);
package main

import (
    "context"
    "database/sql"
    "os"

    "github.com/uptrace/bun"
    "github.com/uptrace/bun/dialect/pgdialect"
    "github.com/uptrace/bun/driver/pgdriver"
    "github.com/uptrace/bun/extra/bundebug"
)

type Order struct {
    ID     int    `bun:",pk"`
    ShopID int    `bun:",pk"`
    Items  []Item `bun:"m2m:orders_to_items,join:Order=Item"`
}

type Item struct {
    ID     int `bun:",pk"`
    ShopID int `bun:",pk"`
}

type OrderToItem struct {
    bun.BaseModel `bun:"table:orders_to_items"`

    ShopID int `bun:""`

    OrderID int    `bun:""`
    Order   *Order `bun:"rel:belongs-to,join:order_id=id,join:shop_id=shop_id"`
    ItemID  int    `bun:""`
    Item    *Item  `bun:"rel:belongs-to,join:item_id=id,join:shop_id=shop_id"`
}

func main() {
    sqldb := sql.OpenDB(pgdriver.NewConnector(
        pgdriver.WithDSN(os.Getenv("DB_URL")),
        pgdriver.WithPassword(os.Getenv("DB_PASSWORD")),
    ))

    db := bun.NewDB(sqldb, pgdialect.New())
    db.AddQueryHook(bundebug.NewQueryHook(bundebug.FromEnv("BUNDEBUG")))
    db.RegisterModel((*OrderToItem)(nil))

    var orders []Order

    err := db.NewSelect().
        Model(&orders).
        Relation("Items").
        Scan(context.Background())
    if err != nil {
        panic(err)
    }
}

This is almost the same as the official example, but I also additionally have the shop_id columns that 1) are part of the primary key for the items and orders tables, and 2) should be used for JOINs. When I run this code, I get the following error:

[bun]  17:55:27.357   SELECT                1.395ms  SELECT "order_to_item"."order_id", "order_to_item"."shop_id", "order_to_item"."item_id", "order_to_item"."shop_id", "item"."id", "item"."shop_id" FROM "items" AS "item" JOIN "orders_to_items" AS "order_to_item" ON ("order_to_item"."order_id", "order_to_item"."shop_id") IN ((1, 22)) WHERE ("item"."id" = "order_to_item"."item_id") AND ("item"."shop_id" = "order_to_item"."shop_id")   *errors.errorString: bun: m2m relation=Items does not have base model=Order with key=['\x01' '\x16' '\x16' '\x16'] (check join conditions)
panic: bun: m2m relation=Items does not have base model=Order with key=['\x01' '\x16' '\x16' '\x16'] (check join conditions)

goroutine 1 [running]:
main.main()
    /home/ygabuev/code/sw__antigravity/go/lol.go:53 +0x3b9
exit status 2

Am I doing something wrong or is this unsupported / a bug?