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: