orm

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Jul 19, 2025 License: MIT Imports: 10 Imported by: 0

README

ORM Package

The orm package provides a Prisma-like experience in Go, using JSON strings for query definitions while supporting both type-safe and non-type-safe usage patterns.

Overview

The orm package is an independent, high-level API that sits on top of the RediORM core. It provides:

  • JSON-based queries - Use familiar JSON syntax instead of method chaining
  • Automatic type conversion - Handles database-specific type conversions (e.g., MySQL string numbers)
  • Dual API support - Both typed (with generics) and untyped (map[string]any) interfaces
  • Complete separation - Independent from the ORM module for clean architecture

Installation

import "github.com/rediwo/redi-orm/orm"

Basic Usage

Creating a Client
// Create client from database
client := orm.NewClient(db)

// Or with custom type converter
client := orm.NewClient(db, orm.WithTypeConverter(customConverter))
CRUD Operations
Create
user, err := client.Model("User").Create(`{
  "data": {
    "name": "John Doe",
    "email": "john@example.com"
  }
}`)
Find Many
users, err := client.Model("User").FindMany(`{
  "where": { 
    "age": { "gte": 18 }
  },
  "orderBy": { "name": "asc" },
  "take": 10,
  "skip": 20
}`)
Update
updated, err := client.Model("User").Update(`{
  "where": { "id": 1 },
  "data": { "name": "Jane Doe" }
}`)
Delete
deleted, err := client.Model("User").Delete(`{
  "where": { "id": 1 }
}`)
Complex Queries
With Conditions
users, err := client.Model("User").FindMany(`{
  "where": { 
    "OR": [
      { "age": { "gte": 18 } },
      { "role": "admin" }
    ],
    "active": true
  }
}`)
With Relations
users, err := client.Model("User").FindMany(`{
  "include": { 
    "posts": {
      "where": { "published": true },
      "orderBy": { "createdAt": "desc" },
      "take": 5
    }
  }
}`)
Aggregations
result, err := client.Model("Order").Aggregate(`{
  "_sum": { "amount": true },
  "_avg": { "amount": true },
  "_count": true,
  "where": { "status": "completed" }
}`)

// result["_sum"]["amount"] will be float64, not string (even for MySQL)
Typed API

For type safety, use the typed variants:

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

var user User
err := client.Model("User").FindUniqueTyped(`{
  "where": { "email": "john@example.com" }
}`, &user)

var users []User
err = client.Model("User").FindManyTyped(`{
  "where": { "active": true }
}`, &users)
Transactions
err := client.Transaction(func(tx *orm.Client) error {
    // All operations use tx instead of client
    user, err := tx.Model("User").Create(`{
      "data": { "name": "Alice", "balance": 1000 }
    }`)
    if err != nil {
        return err // Will rollback
    }
    
    _, err = tx.Model("User").Update(`{
      "where": { "id": ` + fmt.Sprintf("%v", user["id"]) + ` },
      "data": { "balance": 900 }
    }`)
    if err != nil {
        return err // Will rollback
    }
    
    return nil // Will commit
})
Raw Queries
// Execute raw SQL (works with all databases including MongoDB)
raw := client.Model("").Raw("SELECT * FROM users WHERE age > ?", 18)
results, err := raw.Find()

// For single result
result, err := raw.FindOne()

// MongoDB native commands
raw := client.Model("").Raw(`{
    "find": "users",
    "filter": {"age": {"$gt": 18}}
}`)
results, err := raw.Find()

Query Syntax

The orm package uses JSON for all queries. Here are the supported operations:

Operations
  • create - Create a single record
  • createMany - Create multiple records
  • findUnique - Find a single record by unique field
  • findFirst - Find the first matching record
  • findMany - Find multiple records
  • update - Update a single record
  • updateMany - Update multiple records
  • delete - Delete a single record
  • deleteMany - Delete multiple records
  • count - Count matching records
  • aggregate - Perform aggregations
  • upsert - Update or create
Query Options
  • where - Filter conditions
  • data - Data for create/update
  • orderBy - Sort results
  • take - Limit results
  • skip - Skip results
  • select - Select specific fields
  • include - Include relations
  • distinct - Distinct results
Where Operators
  • equals - Exact match (default)
  • not - Not equal
  • in - In array
  • notIn - Not in array
  • lt - Less than
  • lte - Less than or equal
  • gt - Greater than
  • gte - Greater than or equal
  • contains - Contains substring
  • startsWith - Starts with
  • endsWith - Ends with
Logical Operators
  • AND - All conditions must match
  • OR - Any condition must match
  • NOT - Negate condition

Type Conversion

The orm package automatically handles database-specific type conversions:

  • MySQL: Converts string numbers to proper numeric types
  • PostgreSQL: Native numeric types preserved
  • SQLite: Integer/float types preserved
  • MongoDB: BSON types converted to Go types, ObjectId to string

This is especially important for aggregations where MySQL returns strings:

// MySQL returns "123.45" as string, orm converts to float64
result, err := client.Model("Order").Aggregate(`{
  "_sum": { "amount": true }
}`)
sum := result["_sum"].(map[string]any)["amount"].(float64) // Always float64

Testing

The orm package includes comprehensive conformance tests:

suite := &orm.OrmConformanceTests{
    DriverName:  "SQLite",
    DatabaseURI: uri,
    NewDatabase: func(uri string) (types.Database, error) {
        // Create database instance
    },
    // ... other configuration
}

suite.RunAll(t)

Differences from ORM Module

Feature ORM Module ORM Package
API Style Method chaining JSON strings
Type Safety Go types only Dual (typed/untyped)
Complexity Lower-level Higher-level
Use Case Direct control Rapid development

Best Practices

  1. Use typed API when possible - Provides compile-time safety
  2. Batch operations - Use createMany, updateMany for bulk operations
  3. Limit includes - Deep nesting can impact performance
  4. Use transactions - For operations that must succeed together
  5. Handle errors - Always check for errors, especially in typed API

Examples

See the orm_conformance_tests_*.go files for comprehensive examples of all features.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildCondition

func BuildCondition(where any) types.Condition

BuildCondition builds a condition from where object

Types

type Client

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

Client is the main entry point for the ORM API

func NewClient

func NewClient(db types.Database, opts ...ClientOption) *Client

NewClient creates a new ORM client

func (*Client) GetDB

func (c *Client) GetDB() types.Database

GetDB returns the underlying database

func (*Client) Model

func (c *Client) Model(modelName string) *Model

Model returns a model query builder for the specified model

func (*Client) Transaction

func (c *Client) Transaction(fn func(tx *Client) error) error

Transaction executes a function within a database transaction

type ClientOption

type ClientOption func(*Client)

ClientOption is a functional option for configuring the client

func WithTypeConverter

func WithTypeConverter(tc *TypeConverter) ClientOption

WithTypeConverter sets a custom type converter

type Model

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

Model represents a database model with ORM query capabilities

func (*Model) Aggregate

func (m *Model) Aggregate(jsonQuery string) (map[string]any, error)

Aggregate performs aggregation queries

func (*Model) Count

func (m *Model) Count(jsonQuery string) (int64, error)

Count counts records

func (*Model) Create

func (m *Model) Create(jsonData string) (map[string]any, error)

Create creates a new record

func (*Model) CreateTyped

func (m *Model) CreateTyped(jsonData string, dest any) error

CreateTyped creates a new record and scans it into dest

func (*Model) Delete

func (m *Model) Delete(jsonQuery string) (map[string]any, error)

Delete deletes a record

func (*Model) DeleteMany

func (m *Model) DeleteMany(jsonQuery string) (map[string]any, error)

DeleteMany deletes multiple records

func (*Model) FindFirst

func (m *Model) FindFirst(jsonQuery string) (map[string]any, error)

FindFirst finds the first matching record

func (*Model) FindFirstTyped

func (m *Model) FindFirstTyped(jsonQuery string, dest any) error

FindFirstTyped finds the first matching record and scans it into dest

func (*Model) FindMany

func (m *Model) FindMany(jsonQuery string) ([]map[string]any, error)

FindMany finds multiple records

func (*Model) FindManyTyped

func (m *Model) FindManyTyped(jsonQuery string, dest any) error

FindManyTyped finds multiple records and scans them into dest

func (*Model) FindUnique

func (m *Model) FindUnique(jsonQuery string) (map[string]any, error)

FindUnique finds a single unique record

func (*Model) FindUniqueTyped

func (m *Model) FindUniqueTyped(jsonQuery string, dest any) error

FindUniqueTyped finds a single unique record and scans it into dest

func (*Model) GroupBy

func (m *Model) GroupBy(jsonQuery string) ([]map[string]any, error)

GroupBy performs group by queries

func (*Model) Query

func (m *Model) Query(jsonQuery string) (any, error)

Query executes a query with the given JSON string

func (*Model) QueryTyped

func (m *Model) QueryTyped(jsonQuery string, dest any) error

QueryTyped executes a query and scans the result into the provided destination

func (*Model) Raw

func (m *Model) Raw(sql string, args ...any) *RawQuery

Raw executes a raw SQL query

func (*Model) Update

func (m *Model) Update(jsonQuery string) (map[string]any, error)

Update updates records

func (*Model) UpdateMany

func (m *Model) UpdateMany(jsonQuery string) (map[string]any, error)

UpdateMany updates multiple records

type OrmConformanceTests

type OrmConformanceTests struct {
	DriverName      string
	DatabaseURI     string
	SkipTests       map[string]bool
	Characteristics OrmDriverCharacteristics
	NewDatabase     func(uri string) (types.Database, error) // Function to create database instance
	CleanupTables   func(t *testing.T, db types.Database)    // Driver-specific table cleanup
}

OrmConformanceTests provides a comprehensive test suite for the ORM API

func (*OrmConformanceTests) RunAll

func (act *OrmConformanceTests) RunAll(t *testing.T)

RunAll runs all orm conformance tests

type OrmDriverCharacteristics

type OrmDriverCharacteristics struct {
	// SupportsReturning indicates if the driver supports RETURNING clause
	SupportsReturning bool

	// MaxConnectionPoolSize is the maximum number of connections in the pool
	MaxConnectionPoolSize int

	// SupportsNestedTransactions indicates if the driver supports savepoints
	SupportsNestedTransactions bool

	// ReturnsStringForNumbers indicates if the driver returns strings for numeric values (MySQL)
	ReturnsStringForNumbers bool
}

OrmDriverCharacteristics defines driver-specific behaviors for ORM tests

type RawQuery

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

RawQuery represents a raw SQL query

func (*RawQuery) Exec

func (r *RawQuery) Exec() (types.Result, error)

Exec executes the raw query

func (*RawQuery) Find

func (r *RawQuery) Find() ([]map[string]any, error)

Find executes the query and returns multiple results

func (*RawQuery) FindOne

func (r *RawQuery) FindOne() (map[string]any, error)

FindOne executes the query and returns a single result

type TypeConverter

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

TypeConverter handles database-specific type conversions

func NewTypeConverter

func NewTypeConverter(capabilities types.DriverCapabilities) *TypeConverter

NewTypeConverter creates a new type converter for the specified driver

func (*TypeConverter) ConvertAggregateResult

func (tc *TypeConverter) ConvertAggregateResult(result map[string]any) map[string]any

ConvertAggregateResult converts aggregation results

func (*TypeConverter) ConvertFieldValue

func (tc *TypeConverter) ConvertFieldValue(fieldName string, value any) any

ConvertFieldValue converts a field value for database operations

func (*TypeConverter) ConvertResult

func (tc *TypeConverter) ConvertResult(modelName string, result map[string]any) map[string]any

ConvertResult converts a single result map based on the driver capabilities

Jump to

Keyboard shortcuts

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