rel icon indicating copy to clipboard operation
rel copied to clipboard

Cascade deleting associated to-many records when it's empty

Open skolodyazhnyy opened this issue 3 months ago • 5 comments

I'm having troubles figuring out how to cascade save an empty slice of associated records.

Let's say I have:

type ShoppingCart struct {
	ID                    string             `db:"id,primary"`
	Items                 []ShoppingCartItem `ref:"id" fk:"cart_id" autoload:"true" autosave:"true"`
}

type ShoppingCartItem struct {
	ID        string    `db:"id,primary"`
	ProductID string    `db:"product_id"`
	Quantity  int32     `db:"quantity"`
}

When saving shopping cart, items are automatically cascaded, unless there are no items. If I want to empty shopping cart I want to save it like so:

// user adds item to the shopping cart
cart.Items = []ShoppingCartItem{item1}
rel.Update(ctx, cart) // item1 inserted ✅ 

// user empties cart
cart.Items = nil // or []ShoppingCartItem{}
rel.Update(ctx, cart) // item1 is not deleted ⚠️ 

The issue here is that Update thinks items haven't been loaded, so it does not delete existing items.

Is there way to force make rel to delete item1?

skolodyazhnyy avatar Sep 10 '25 09:09 skolodyazhnyy

deleting association in structset not currently implemented (code) need to do delete it manually for now

if you are interested to submit PR, you can follow this map.go code to use mutation.DeletedIDs

Fs02 avatar Sep 17 '25 11:09 Fs02

I would like to make a PR to address this issue. But I would appreciate if you could suggest a solution here.

I'm thinking we need to preserve existing behaviour: nil and empty slice - no cascading, so we don't break anything for anyone.

From top of my head we could:

  • Add an option rel.ForceCascade("items") - this will tell rel we are sure items were preloaded, so if they are empty delete everything.
  • Create a generic wrapper Items rel.ToMany[X], this wrapper will explicitly mark assoc as loaded and will help track associated elements.

Let me know and I will try to make a PR.

skolodyazhnyy avatar Sep 17 '25 13:09 skolodyazhnyy

rel.ForceCascade sounds good to me

rel.ToMany[X] is a class that need to be embedded?

Fs02 avatar Sep 20 '25 04:09 Fs02

ForceCascade it is :)

For rel.ToMany[X] I thought to introduce a generic which would provide additional information about association. Something like:

type ShoppingCart struct {
	ID    string                       `db:"id,primary"`
	Items rel.ToMany[ShoppingCartItem] `ref:"id" fk:"cart_id" autoload:"true" autosave:"true"`
}

type ShoppingCartItem struct {
	ID        string    `db:"id,primary"`
	ProductID string    `db:"product_id"`
	Quantity  int32     `db:"quantity"`
}

The generic would be something like

type ToMany[A any] struct {
    Elements []A
    loaded bool
}

Iterating becomes a bit cumbersome, but rel gets a place to store additional information about loaded state.

skolodyazhnyy avatar Sep 20 '25 12:09 skolodyazhnyy

I can take a look into it, if you're not getting any time for it @skolodyazhnyy ?

shashank-netapp avatar Sep 29 '25 19:09 shashank-netapp