func (ms *metadataService) RecordInteraction(ctx context.Context, email string, interactionType models.EmailCategory, data models.InteractionData) error { newInteraction := data newInteraction.Type = interactionType
_, errInteraction := ms.emailClient.EmailMetadata.Create().
SetLatestInteractions(&newInteraction).
SetEmail(email).
Save(ctx)
if errInteraction != nil {
return fmt.Errorf("could not create interaction record %w", errInteraction)
}
return nil }
enable upsert instead of create
To enable upsert instead of Create() in Ent, you need to use the OnConflict() API. Here’s how you can modify your RecordInteraction function to perform an upsert on email:
Assuming:
email is a unique field in the EmailMetadata schema.SetLatestInteractions is writing to a JSON or struct column.Here’s the modified function:
func (ms *metadataService) RecordInteraction(ctx context.Context, email string, interactionType models.EmailCategory, data models.InteractionData) error {
newInteraction := data
newInteraction.Type = interactionType
_, err := ms.emailClient.EmailMetadata.
Create().
SetEmail(email).
SetLatestInteractions(&newInteraction).
OnConflict(
sql.ConflictColumns("email"),
sql.ResolveWithNewValues(),
sql.ResolveWith(func(update *ent.EmailMetadataUpsert) {
update.SetLatestInteractions(&newInteraction)
}),
).
Save(ctx)
if err != nil {
return fmt.Errorf("could not upsert interaction record: %w", err)
}
return nil
}
OnConflict specifies the unique constraint to match on (email).ResolveWithNewValues() updates all fields with their new values (optional here).ResolveWith(...) gives fine-grained control — we only update LatestInteractions.Let me know if email is not marked Unique() in the Ent schema — that’s required for this to work.
each call to RecordInteraction is supposed to append the interaction to the json field latestinteractions
Perfect — then what you want is append semantics on a JSON field (LatestInteractions). Since Ent’s SetLatestInteractions() replaces the field, you’ll need to:
Update() if the record exists, or Create() if not.Here’s the revised version with that logic:
func (ms *metadataService) RecordInteraction(ctx context.Context, email string, interactionType models.EmailCategory, data models.InteractionData) error {
newInteraction := data
newInteraction.Type = interactionType
// Try fetching existing metadata
existing, err := ms.emailClient.EmailMetadata.
Query().
Where(emailmetadata.EmailEQ(email)).
Only(ctx)
var interactions []models.InteractionData
if err == nil {
// Append to existing slice
interactions = append(existing.LatestInteractions, newInteraction)
// Update
err = ms.emailClient.EmailMetadata.
UpdateOneID(existing.ID).
SetLatestInteractions(interactions).
Exec(ctx)
if err != nil {
return fmt.Errorf("could not update interaction record: %w", err)
}
} else if ent.IsNotFound(err) {
// Create new with the interaction
interactions = []models.InteractionData{newInteraction}
_, err = ms.emailClient.EmailMetadata.
Create().
SetEmail(email).
SetLatestInteractions(interactions).
Save(ctx)
if err != nil {
return fmt.Errorf("could not create interaction record: %w", err)
}
} else {
// Unexpected query error
return fmt.Errorf("failed to query metadata: %w", err)
}
return nil
}
LatestInteractions is a slice-type field (e.g., []InteractionData) serialized as JSON.Email is unique in your schema.Let me know if you need: