dsorm

package module
v0.0.0-...-55b9ed9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 28, 2026 License: MIT Imports: 19 Imported by: 0

README

Run Tests

dsorm

dsorm is a high-performance Go ORM for Google Cloud Datastore with built-in caching support (Memory, Redis, Memcache). It extends the official client with lifecycle hooks, struct tags for keys, field encryption, and a robust caching layer to minimize Datastore costs and latency.

Features

  • Auto-Caching: Transparently caches keys/entities in Memory, Redis, or Memcache.
  • Model Hooks: BeforeSave, AfterSave, OnLoad lifecycle methods.
  • Key Mapping: Use struct tags (e.g., model:"id") to map keys to struct fields.
  • Field Encryption: Built-in encryption for sensitive string fields via marshal:"name,encrypt" tag.
  • JSON Marshaling: Store complex structs/maps as compact JSON strings via marshal tag.
  • API Parity: Wraps standard datastore methods (Put, Get, RunInTransaction) for easy migration.

Installation

go get github.com/altlimit/dsorm

Usage

1. Initialization

Initialize the client with your context. dsorm automatically detects the best cache backend:

  • App Engine: Uses Memcache.
  • Redis (REDIS_ADDR env): Uses Redis.
  • Default: Uses in-memory cache.
ctx := context.Background()

// Basic Init (Auto-detects)
client, err := dsorm.New(ctx)
if err != nil {
    panic(err)
}



// With Options
client, err = dsorm.New(ctx, 
    dsorm.WithProjectID("my-project"),
    dsorm.WithCachePrefix("myapp:"),
    dsorm.WithEncryptionKey([]byte("my-32-byte-secret-key...")),
)
2. Defining Models

Embed dsorm.Base and use tags for keys, properties, and lifecycle management.

type User struct {
    dsorm.Base
    ID        string         `model:"id"`       // Auto-used for Key Name
    Namespace string         `model:"ns"`       // Auto-used for Key Namespace
    Parent    *datastore.Key `model:"parent"`   // Auto-used for Key Parent (can also use *ParentModel)
    Username  string
    Email     string    `datastore:"email"`
    Secret    string    `marshal:"secret,encrypt"`     // Encrypted + NoIndex + stored as "secret"
    Profile   map[string]string `marshal:"profile"` 
    CreatedAt time.Time `model:"created"`  // Auto-set on creation
    UpdatedAt time.Time `model:"modified"` // Auto-set on save
}
// Access key via u.Key()
// ...
3. CRUD Operations

You don't need to manually construct keys. Just set the ID field.

// Create
user := &User{
    ID:       "alice",
    Username: "Alice",
}
// Key is auto-constructed from ID and Namespace tags during Put
err := client.Put(ctx, user) 

// Read
fetched := &User{ID: "alice"}
// Key is auto-reconstructed from ID for the Get lookup
err := client.Get(ctx, fetched)

// Update
fetched.Username = "Alice_Updated"
err := client.Put(ctx, fetched) // UpdatedAt will be auto-updated

// Delete
err := client.Delete(ctx, fetched)
4. Transactions

Pass models to Transact to ensure they are initialized and locked correctly.

user := &User{ID: "bob"}

_, err := client.Transact(ctx, func(tx *dsorm.Transaction) error {
    if err := tx.Get(user); err != nil {
        return err
    }
    user.Username = "Bob (Verified)"
    return tx.Put(user)
})
5. Queries

Query keys-only and auto-fetch entities in batches (of 1000) for performance.

q := datastore.NewQuery("User").FilterField("Username", "=", "Alice")

// Wrapper helper
users, nextCursor, err := dsorm.Query[*User](ctx, client, q, "")

// GetMulti Helper (Get by IDs, Keys, or Structs)
ids := []string{"alice", "bob"}
users, err := dsorm.GetMulti[*User](ctx, client, ids)

Configuration

Set environment variables to configure defaults (fallback):

Variable Description
DATASTORE_PROJECT_ID Google Cloud Project ID.
DATASTORE_ENCRYPTION_KEY 32-byte key for field encryption (Fallback if not provided in Context).
REDIS_ADDR Address for Redis cache (e.g., localhost:6379).

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetMulti

func GetMulti[T Model](ctx context.Context, db *Client, ids any) ([]T, error)

GetMulti loads multiple entities by IDs (int64, string, *datastore.Key) or struct slice.

func Query

func Query[T Model](ctx context.Context, db *Client, q *datastore.Query, cursor string) ([]T, string, error)

Query executes a query and returns a slice of models.

Types

type AfterDelete

type AfterDelete interface {
	AfterDelete(context.Context) error
}

type AfterSave

type AfterSave interface {
	AfterSave(context.Context, Model) error
}

type Base

type Base struct {
	// contains filtered or unexported fields
}

Base provides default implementation for Model interface and common fields. Embed this in your struct to use dsorm.

func (*Base) IsNew

func (b *Base) IsNew() bool

func (*Base) Key

func (b *Base) Key() *datastore.Key

func (*Base) Load

func (b *Base) Load(ps []datastore.Property) error

Load implements datastore.PropertyLoadSaver.

func (*Base) LoadKey

func (b *Base) LoadKey(k *datastore.Key) error

LoadKey implements datastore.KeyLoader.

func (*Base) Save

func (b *Base) Save() ([]datastore.Property, error)

Save implements datastore.PropertyLoadSaver.

type BeforeDelete

type BeforeDelete interface {
	BeforeDelete(context.Context) error
}

type BeforeSave

type BeforeSave interface {
	BeforeSave(context.Context, Model) error
}

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client wraps datastore and cache operations.

func New

func New(ctx context.Context, opts ...Option) (*Client, error)

New creates a new Client with options value. It auto-detects caching backend: App Engine (Memcache), Redis (env REDIS_ADDR), or Memory.

func (*Client) Client

func (db *Client) Client() *ds.Client

func (*Client) Delete

func (db *Client) Delete(ctx context.Context, val interface{}) error

func (*Client) DeleteMulti

func (db *Client) DeleteMulti(ctx context.Context, vals interface{}) error

func (*Client) Get

func (db *Client) Get(ctx context.Context, val interface{}) error

Get loads the entity for the given key/struct into the struct.

func (*Client) GetMulti

func (db *Client) GetMulti(ctx context.Context, vals interface{}) error

GetMulti loads multiple entities.

func (*Client) Key

func (db *Client) Key(val interface{}) *datastore.Key

Key returns the datastore key for the given entity.

func (*Client) Keys

func (db *Client) Keys(val interface{}) []*datastore.Key

Keys returns a slice of keys for the given slice of entities.

func (*Client) Put

func (db *Client) Put(ctx context.Context, val interface{}) error

Put saves an entity.

func (*Client) PutMulti

func (db *Client) PutMulti(ctx context.Context, vals interface{}) error

PutMulti saves multiple entities.

func (*Client) Query

func (db *Client) Query(ctx context.Context, q *datastore.Query, cursor string, vals interface{}) ([]*datastore.Key, string, error)

func (*Client) RawClient

func (db *Client) RawClient() *datastore.Client

func (*Client) Transact

func (db *Client) Transact(ctx context.Context, f func(tx *Transaction) error) (*datastore.Commit, error)

Transact runs a function in a transaction.

type Model

type Model interface {
	IsNew() bool
	Key() *datastore.Key
}

Model defines the basic methods required for a struct to be managed by dsorm.

type OnLoad

type OnLoad interface {
	OnLoad(context.Context) error
}

type Option

type Option func(*options)

func WithCache

func WithCache(c ds.Cache) Option

func WithDatastoreClient

func WithDatastoreClient(c *datastore.Client) Option

func WithEncryptionKey

func WithEncryptionKey(key []byte) Option

func WithProjectID

func WithProjectID(id string) Option

type Transaction

type Transaction struct {
	// contains filtered or unexported fields
}

Transaction wraps ds.Transaction to provide dsorm functionality (ID mapping, lifecycle hooks).

func (*Transaction) Delete

func (t *Transaction) Delete(val interface{}) error

Delete deletes an entity within the transaction.

func (*Transaction) DeleteMulti

func (t *Transaction) DeleteMulti(vals interface{}) error

DeleteMulti deletes multiple entities within the transaction.

func (*Transaction) Get

func (t *Transaction) Get(val interface{}) error

Get loads entity to val within the transaction.

func (*Transaction) GetMulti

func (t *Transaction) GetMulti(vals interface{}) error

GetMulti loads multiple entities within the transaction.

func (*Transaction) Put

func (t *Transaction) Put(val interface{}) error

Put saves an entity within the transaction.

func (*Transaction) PutMulti

func (t *Transaction) PutMulti(vals interface{}) error

PutMulti saves multiple entities within the transaction.

Directories

Path Synopsis
cache
memory
Package memory IS NOT MEANT TO BE USED - THIS IS FOR PROOF OF CONCEPT AND TESTING ONLY, IT IS A LOCAL MEMORY STORE AND WILL RESULT IN INCONSISTENT CACHING FOR DISTRIBUTED SYSTEMS!
Package memory IS NOT MEANT TO BE USED - THIS IS FOR PROOF OF CONCEPT AND TESTING ONLY, IT IS A LOCAL MEMORY STORE AND WILL RESULT IN INCONSISTENT CACHING FOR DISTRIBUTED SYSTEMS!
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL