Documentation
¶
Overview ¶
Package enginemongo is the MongoDB engine for r3: a full r3.CRUD backed by the official Go driver v2, using reflection-based struct metadata and BSON query building. The drivers/mongo package wraps it as a thin constructor.
Key types:
- BaseCRUD: full r3.CRUD backed by *mongo.Collection
- BaseRaw: escape hatch for arbitrary MongoDB operations
- StructMeta: reflection-based struct metadata (fields, PK, relations, soft-delete)
- PreparedListQuery: pre-computed BSON filter/sort/pagination from an r3.Query
Field mapping reads `bson` or `r3` struct tags; the primary key defaults to `_id`.
Index ¶
- Constants
- func BSONFilter(kv ...any) bson.D
- func NewBaseQuerier[T any, ID comparable](coll *mongo.Collection, opts ...r3.Option) r3.Querier[T, ID]
- func NewBaseQuerierFromDB[T any, ID comparable](db *mongo.Database, opts ...r3.Option) r3.Querier[T, ID]
- func RunPreloads(ctx context.Context, db *mongo.Database, meta *StructMeta, entitiesPtr any, ...) error
- type BaseCRUD
- func (r *BaseCRUD[T, ID]) Aggregate(ctx context.Context, qarg ...r3.Query) ([]r3.AggregateRow, error)
- func (r *BaseCRUD[T, ID]) AggregateThroughRelation(ctx context.Context, relation string, qarg ...r3.Query) ([]r3.AggregateRow, error)
- func (r *BaseCRUD[T, ID]) Count(ctx context.Context, qarg ...r3.Query) (int64, error)
- func (r *BaseCRUD[T, ID]) Create(ctx context.Context, entity T) (T, error)
- func (r *BaseCRUD[T, ID]) Delete(ctx context.Context, id ID) error
- func (r *BaseCRUD[T, ID]) Get(ctx context.Context, id ID, qarg ...r3.Query) (T, error)
- func (r *BaseCRUD[T, ID]) HardDelete(ctx context.Context, id ID) error
- func (r *BaseCRUD[T, ID]) List(ctx context.Context, qarg ...r3.Query) ([]T, int64, error)
- func (r *BaseCRUD[T, ID]) Patch(ctx context.Context, entity T, fields r3.Fields) (T, error)
- func (r *BaseCRUD[T, ID]) PatchWhere(ctx context.Context, filters r3.Filters, entity T, fields r3.Fields) (int64, error)
- func (r *BaseCRUD[T, ID]) Restore(ctx context.Context, id ID) error
- func (r *BaseCRUD[T, ID]) Update(ctx context.Context, entity T) (T, error)
- func (r *BaseCRUD[T, ID]) Upsert(ctx context.Context, entity T, opts ...r3.UpsertOption) (T, error)
- type BaseRaw
- func (r *BaseRaw[T, ID]) Aggregate(ctx context.Context, pipeline any) ([]T, error)
- func (r *BaseRaw[T, ID]) BulkWrite(ctx context.Context, models []mongo.WriteModel) (*mongo.BulkWriteResult, error)
- func (r *BaseRaw[T, ID]) Col() *mongo.Collection
- func (r *BaseRaw[T, ID]) CountDocuments(ctx context.Context, filter any) (int64, error)
- func (r *BaseRaw[T, ID]) DeleteMany(ctx context.Context, filter any) (*mongo.DeleteResult, error)
- func (r *BaseRaw[T, ID]) DeleteOne(ctx context.Context, filter any) (*mongo.DeleteResult, error)
- func (r *BaseRaw[T, ID]) Distinct(ctx context.Context, fieldName string, filter any) *mongo.DistinctResult
- func (r *BaseRaw[T, ID]) Find(ctx context.Context, filter any, opts ...options.Lister[options.FindOptions]) ([]T, error)
- func (r *BaseRaw[T, ID]) FindOne(ctx context.Context, filter any, ...) (T, error)
- func (r *BaseRaw[T, ID]) FindOneAndDelete(ctx context.Context, filter any, ...) (T, error)
- func (r *BaseRaw[T, ID]) FindOneAndUpdate(ctx context.Context, filter any, update any, ...) (T, error)
- func (r *BaseRaw[T, ID]) InsertOne(ctx context.Context, doc any) (*mongo.InsertOneResult, error)
- func (r *BaseRaw[T, ID]) UpdateMany(ctx context.Context, filter any, update any) (*mongo.UpdateResult, error)
- func (r *BaseRaw[T, ID]) UpdateOne(ctx context.Context, filter any, update any) (*mongo.UpdateResult, error)
- type PreparedListQuery
- type RelationKind
- type RelationMeta
- type StructMeta
- func (m *StructMeta) FieldValues(entity any) []any
- func (m *StructMeta) FieldValuesForFields(entity any, fields []string) []any
- func (m *StructMeta) HasCodecs() bool
- func (m *StructMeta) IDValue(entity any) any
- func (m *StructMeta) NonIDFieldValues(entity any) []any
- func (m *StructMeta) NonIDFields() []string
- func (m *StructMeta) SetFieldsFromMap(entityPtr any, data map[string]any)
- func (m *StructMeta) SetIDValue(entityPtr any, val any)
- func (m *StructMeta) ToBSONDoc(entity any, includeID bool) map[string]any
- func (m *StructMeta) ValidatePatchFields(fields []string) ([]string, error)
Constants ¶
const ( // RelHasMany represents a one-to-many relationship. RelHasMany = r3tag.RelHasMany // RelBelongsTo represents a many-to-one relationship. RelBelongsTo = r3tag.RelBelongsTo // RelManyToMany represents a many-to-many relationship via a join collection. RelManyToMany = r3tag.RelManyToMany )
Variables ¶
This section is empty.
Functions ¶
func BSONFilter ¶
BSONFilter creates a simple bson.D filter from key-value pairs. Usage: BSONFilter("status", "active", "age", bson.D{{"$gt", 18}}).
func NewBaseQuerier ¶
func NewBaseQuerier[T any, ID comparable](coll *mongo.Collection, opts ...r3.Option) r3.Querier[T, ID]
NewBaseQuerier returns a BaseCRUD as a read-only r3.Querier, so callers get a compile-time guarantee they cannot write.
func NewBaseQuerierFromDB ¶
func NewBaseQuerierFromDB[T any, ID comparable](db *mongo.Database, opts ...r3.Option) r3.Querier[T, ID]
NewBaseQuerierFromDB is NewBaseQuerier, deriving the collection name from T.
func RunPreloads ¶
func RunPreloads( ctx context.Context, db *mongo.Database, meta *StructMeta, entitiesPtr any, preloads r3.Preloads, ) error
RunPreloads loads the requested relations for entities (a pointer to a slice, e.g. *[]City) via key-set $in queries, one per relation:
- has-many: query children where fk $in (parent IDs), group by FK, assign to the parent's slice field.
- belongs-to: query targets where _id $in (parent FKs), assign to the parent's pointer field.
Types ¶
type BaseCRUD ¶
type BaseCRUD[T any, ID comparable] struct { Collection *mongo.Collection r3.DefaultsManager Meta StructMeta Config r3.Config Raw *BaseRaw[T, ID] // CodecSchema is the minimal schema (codec'd fields, keyed by bson name) that // drives value-codec encoding of filter and cursor arguments. Zero when the // type declares no codecs. CodecSchema r3.Schema }
BaseCRUD is a full r3.CRUD[T, ID] backed by a *mongo.Collection. ID must match the _id field type (e.g. primitive.ObjectID or string).
func NewBaseCRUD ¶
func NewBaseCRUD[T any, ID comparable](coll *mongo.Collection, opts ...r3.Option) *BaseCRUD[T, ID]
NewBaseCRUD builds a BaseCRUD over the given collection. Accepts optional r3.Option values.
func NewBaseCRUDFromDB ¶
func NewBaseCRUDFromDB[T any, ID comparable](db *mongo.Database, opts ...r3.Option) *BaseCRUD[T, ID]
NewBaseCRUDFromDB builds a BaseCRUD, deriving the collection name from T. Accepts optional r3.Option values.
func (*BaseCRUD[T, ID]) Aggregate ¶ added in v0.0.3
func (r *BaseCRUD[T, ID]) Aggregate(ctx context.Context, qarg ...r3.Query) ([]r3.AggregateRow, error)
Aggregate computes grouped aggregates via the pipeline: $match (filters + soft-delete) → $group → $project (flatten group keys) → $match (having) → $sort → $skip/$limit. See r3.Aggregator for the query semantics.
Degradations vs SQL: dotted (nested-path) group fields are unsupported, and COUNT(DISTINCT field) uses $addToSet, materializing the value set per group - fine for the moderate cardinalities aggregates target.
func (*BaseCRUD[T, ID]) AggregateThroughRelation ¶ added in v0.0.9
func (r *BaseCRUD[T, ID]) AggregateThroughRelation( ctx context.Context, relation string, qarg ...r3.Query, ) ([]r3.AggregateRow, error)
AggregateThroughRelation aggregates the entity's related rows through a declared relation, grouped and folded over the related base collection (the child collection for has-many, the join collection for many-to-many). It mirrors drivers/gorm/relaggregate.go; see r3.RelationAggregator for the query semantics.
Mongo has no server-side join, so owner restriction and target soft-delete exclusion are done as pre-query key sets (like the relationship filters), then the shared $group pipeline runs over the base collection.
func (*BaseCRUD[T, ID]) Create ¶
Create inserts a document and returns it with its _id. A pre-assigned non-zero _id (e.g. an app-generated UUID string PK, as used by features/history) is stored as-is; a zero _id is excluded from the insert so MongoDB generates one, which is then written back onto the entity.
func (*BaseCRUD[T, ID]) Delete ¶
Delete removes a document by its _id, or soft-deletes it (setting the soft-delete field to now) when the model has one.
func (*BaseCRUD[T, ID]) Get ¶
Get retrieves a document by its _id, normalizing not-found to r3.ErrNotFound.
func (*BaseCRUD[T, ID]) HardDelete ¶
HardDelete permanently removes a document, bypassing soft-delete.
func (*BaseCRUD[T, ID]) Patch ¶
Patch updates only the given fields on the document identified by entity's _id, then re-fetches and returns it.
func (*BaseCRUD[T, ID]) PatchWhere ¶ added in v0.0.9
func (r *BaseCRUD[T, ID]) PatchWhere( ctx context.Context, filters r3.Filters, entity T, fields r3.Fields, ) (int64, error)
PatchWhere sets fields (taken from entity's values) on every document matching filters and returns the modified-document count - the multi-document Patch. It shares Patch's field validation (ValidatePatchFields: no unknown, ID, or soft-delete field), maps to a single UpdateMany with a $set, and excludes soft-deleted documents by default. Relationship ("has") filters are not supported and return an error. Mirrors engine/sql/bulkpatch.go.
func (*BaseCRUD[T, ID]) Restore ¶
Restore un-deletes a soft-deleted document by unsetting its soft-delete field; errors if the model has none.
func (*BaseCRUD[T, ID]) Upsert ¶ added in v0.0.9
Upsert inserts entity, or updates the colliding document on a conflict against the conflict target (the _id primary key by default), via UpdateOne with upsert:true.
The conflict target is UpsertSpec.ConflictColumns (a unique-index field set), defaulting to the PK. UpsertSpec.UpdateFields, when set, restricts the fields overwritten on conflict (Patch-validated); empty means a full replace of every non-conflict field. Fields not written by the update branch are still applied on insert via $setOnInsert, so a new document is stored complete. The stored document is re-fetched and returned. Mirrors the SQL semantics in engine/sql/upsert.go.
type BaseRaw ¶
type BaseRaw[T any, ID any] struct { Collection *mongo.Collection Meta StructMeta }
BaseRaw is the escape hatch for arbitrary operations against the underlying collection.
func NewBaseRaw ¶
func NewBaseRaw[T any, ID comparable](coll *mongo.Collection, meta StructMeta) *BaseRaw[T, ID]
NewBaseRaw builds a BaseRaw over coll.
func (*BaseRaw[T, ID]) Aggregate ¶
Aggregate executes an aggregation pipeline and decodes results into T.
func (*BaseRaw[T, ID]) BulkWrite ¶
func (r *BaseRaw[T, ID]) BulkWrite(ctx context.Context, models []mongo.WriteModel) (*mongo.BulkWriteResult, error)
BulkWrite executes a bulk write operation.
func (*BaseRaw[T, ID]) Col ¶
func (r *BaseRaw[T, ID]) Col() *mongo.Collection
Col returns the underlying *mongo.Collection for fully custom usage.
func (*BaseRaw[T, ID]) CountDocuments ¶
CountDocuments returns the count of documents matching the filter.
func (*BaseRaw[T, ID]) DeleteMany ¶
DeleteMany deletes all documents matching the filter.
func (*BaseRaw[T, ID]) Distinct ¶
func (r *BaseRaw[T, ID]) Distinct(ctx context.Context, fieldName string, filter any) *mongo.DistinctResult
Distinct returns the distinct values for a given field.
func (*BaseRaw[T, ID]) Find ¶
func (r *BaseRaw[T, ID]) Find( ctx context.Context, filter any, opts ...options.Lister[options.FindOptions], ) ([]T, error)
Find executes a find query with the given filter and returns matching documents.
func (*BaseRaw[T, ID]) FindOne ¶
func (r *BaseRaw[T, ID]) FindOne( ctx context.Context, filter any, opts ...options.Lister[options.FindOneOptions], ) (T, error)
FindOne executes a find query expected to return a single document.
func (*BaseRaw[T, ID]) FindOneAndDelete ¶
func (r *BaseRaw[T, ID]) FindOneAndDelete( ctx context.Context, filter any, opts ...options.Lister[options.FindOneAndDeleteOptions], ) (T, error)
FindOneAndDelete atomically finds a document and deletes it, returning the deleted document.
func (*BaseRaw[T, ID]) FindOneAndUpdate ¶
func (r *BaseRaw[T, ID]) FindOneAndUpdate( ctx context.Context, filter any, update any, opts ...options.Lister[options.FindOneAndUpdateOptions], ) (T, error)
FindOneAndUpdate atomically finds a document and updates it, returning the result.
func (*BaseRaw[T, ID]) UpdateMany ¶
func (r *BaseRaw[T, ID]) UpdateMany(ctx context.Context, filter any, update any) (*mongo.UpdateResult, error)
UpdateMany updates all documents matching the filter.
type PreparedListQuery ¶
type PreparedListQuery struct {
Filter bson.D // combined BSON filter
Sort bson.D
Projection bson.D // field selection
// IsPaginated marks offset pagination; Limit and Offset (skip) are then set.
IsPaginated bool
Limit int64
Offset int64
// IsCursorPaginated marks cursor/keyset pagination: use CursorFilter and
// CursorLimit, with no skip and no count query.
IsCursorPaginated bool
CursorFilter bson.D // keyset filter; empty for the first page
CursorLimit int64
// Query is the merged r3.Query, for Preloads, IncludeTrashed, etc.
Query r3.Query
}
PreparedListQuery holds the BSON-ready pieces (filter, sort, projection, pagination) derived from an r3.Query.
func PrepareListQuery ¶
func PrepareListQuery(dm *r3.DefaultsManager, schema r3.Schema, qarg ...r3.Query) (PreparedListQuery, error)
PrepareListQuery merges defaults with user args, then converts filters, sorts, and pagination into BSON via PrepareMergedListQuery.
func PrepareMergedListQuery ¶ added in v0.0.9
PrepareMergedListQuery converts an already-merged query's filters, sorts, and pagination into BSON. Cursor pagination takes precedence over offset. It is the half of PrepareListQuery after the merge, exposed so a driver can transform the query first (e.g. lower relationship "has" filters into key-set In filters).
schema drives value-codec encoding of filter and cursor arguments to stored form (e.g. a time.Time bound against an int column); a zero schema (no codecs) leaves the arguments untouched.
type RelationKind ¶
type RelationKind = r3tag.RelationKind
RelationKind describes the type of relationship between two entities.
type RelationMeta ¶
type RelationMeta struct {
FieldName string // relation name (Go field name, or spec name)
FieldIndex int // struct field index for preload assignment; -1 for a declared spec
Kind RelationKind // has-many, belongs-to, or many-to-many
FKField string // foreign key field: child-side (has-many), owner-side (belongs-to), owner-side in join (m2m)
RefField string // related-side FK field in the join collection (many-to-many only)
JoinTable string // join collection name (many-to-many only)
TargetMeta StructMeta // metadata for the related entity (collection, IDField, SoftDeleteField)
TargetType reflect.Type // reflect.Type of the target entity (element type); nil for a declared spec
}
RelationMeta holds metadata about a relation - either reflected from a struct field (preloadable) or declared explicitly via r3.WithRelations (FieldIndex -1, TargetType nil).
func RelationMetaFromSpec ¶ added in v0.0.9
func RelationMetaFromSpec(spec r3.RelationSpec) RelationMeta
RelationMetaFromSpec converts a core r3.RelationSpec (a relation declared by collection and field names) into a RelationMeta. The target Go type is unknown - the point of an explicit spec - so TargetType is nil and FieldIndex is -1: the relation is filterable and aggregatable but has no struct field to preload into.
type StructMeta ¶
type StructMeta struct {
CollectionName string // e.g. "cities"
Fields []string // BSON field names in order, e.g. ["_id", "name", ...]
FieldIndices []int // struct field index per BSON field
IDField string // primary key BSON field name (defaults to "_id")
IDFieldIdx int // index into Fields/FieldIndices for the ID entry
// SoftDeleteField is the soft-delete BSON field name; empty disables soft-delete.
SoftDeleteField string
// SoftDeleteZero is the zero value of a non-pointer soft-delete field, which a
// live record persists (a time.Time stores the zero BSON Date, not null), so
// the "not deleted" filter must match it as well as null. Nil for a pointer
// field (live records store null) or when soft-delete is disabled.
SoftDeleteZero any
// Codecs maps a BSON field name to the value codec declared on it
// (r3:"...,codec:name"). Keyed by the physical bson name - not the schema
// name - because reads/writes and filter args all reference the stored field.
// Empty when the type declares no codecs.
Codecs map[string]r3.Codec
Relations []RelationMeta
}
StructMeta is reflection-based metadata about a struct type T, the MongoDB equivalent of enginesql.StructMeta.
func GetStructMeta ¶
func GetStructMeta[T any]() StructMeta
GetStructMeta derives collection name and field info from T. Field-name priority: `r3` tag, then `bson`, then `db`, then snake_case; a "-" in any tag skips the field.
func WithDeclaredRelations ¶ added in v0.0.9
func WithDeclaredRelations(meta StructMeta, specs []r3.RelationSpec) StructMeta
WithDeclaredRelations returns a copy of meta with specs added to its Relations. A declared relation whose name matches a tag-reflected one replaces it (explicit spec wins). The input meta is never mutated (shared and cached); nil/empty specs return meta unchanged.
func (*StructMeta) FieldValues ¶
func (m *StructMeta) FieldValues(entity any) []any
FieldValues extracts the BSON field values from a struct in the same order as Fields.
func (*StructMeta) FieldValuesForFields ¶
func (m *StructMeta) FieldValuesForFields(entity any, fields []string) []any
FieldValuesForFields extracts field values from an entity for the given BSON field names.
func (*StructMeta) HasCodecs ¶ added in v0.0.9
func (m *StructMeta) HasCodecs() bool
HasCodecs reports whether the type declares any value codec.
func (*StructMeta) IDValue ¶
func (m *StructMeta) IDValue(entity any) any
IDValue extracts the ID field value from an entity.
func (*StructMeta) NonIDFieldValues ¶
func (m *StructMeta) NonIDFieldValues(entity any) []any
NonIDFieldValues extracts field values excluding the ID, for inserts.
func (*StructMeta) NonIDFields ¶
func (m *StructMeta) NonIDFields() []string
NonIDFields returns all BSON field names except the ID field.
func (*StructMeta) SetFieldsFromMap ¶
func (m *StructMeta) SetFieldsFromMap(entityPtr any, data map[string]any)
SetFieldsFromMap sets struct fields from a map of BSON field name -> value.
func (*StructMeta) SetIDValue ¶
func (m *StructMeta) SetIDValue(entityPtr any, val any)
SetIDValue sets the ID field value on an entity (via pointer).
func (*StructMeta) ToBSONDoc ¶
func (m *StructMeta) ToBSONDoc(entity any, includeID bool) map[string]any
ToBSONDoc converts an entity to a BSON document for insert/update, optionally omitting the ID.
func (*StructMeta) ValidatePatchFields ¶
func (m *StructMeta) ValidatePatchFields(fields []string) ([]string, error)
ValidatePatchFields checks that the given field names are valid for a Patch operation.