{"id":21658333,"url":"https://github.com/si3nloong/goloquent","last_synced_at":"2025-07-17T21:30:47.611Z","repository":{"id":33822645,"uuid":"134233705","full_name":"si3nloong/goloquent","owner":"si3nloong","description":"Golang sequel eloquent","archived":true,"fork":false,"pushed_at":"2021-12-30T10:17:45.000Z","size":400,"stargazers_count":15,"open_issues_count":3,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-15T08:20:55.303Z","etag":null,"topics":["datastore","datastore-mysql","eloquent","golang","mysql","postgres","sequel"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/si3nloong.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-05-21T07:25:15.000Z","updated_at":"2024-05-03T04:27:37.000Z","dependencies_parsed_at":"2022-08-25T23:31:24.582Z","dependency_job_id":null,"html_url":"https://github.com/si3nloong/goloquent","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/si3nloong%2Fgoloquent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/si3nloong%2Fgoloquent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/si3nloong%2Fgoloquent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/si3nloong%2Fgoloquent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/si3nloong","download_url":"https://codeload.github.com/si3nloong/goloquent/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226304587,"owners_count":17603626,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["datastore","datastore-mysql","eloquent","golang","mysql","postgres","sequel"],"created_at":"2024-11-25T09:29:06.643Z","updated_at":"2024-11-25T09:29:12.576Z","avatar_url":"https://github.com/si3nloong.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Goloquent\n\n\u003e The only sequel ORM that respect google datastore.\n\n\n## Why Goloquent?\n\nFew years back, we required to migrate our database to local due to compliance issue, and there have no way to map back the data to SQL database. For sake, we introduce goloquent for whoever want to migrate their google datastore to SQL database.\n\n## Database Support\n\n- [x] MySQL (version 5.7 and above)\n- [x] Postgres (version 9.4 and above)\n\n\nThis package is not compactible with native package `database/sql`, if you want the support of it, you may go for [sqlike](https://github.com/si3nloong/sqlike)\n\n# Sequel Datastore ORM\n\nInspired by Laravel Eloquent and Google Cloud Datastore\n\nThis repo still under development. We accept any pull request. ^\\_^\n\n\n\n## Installation\n\n```bash\n  // dependency\n  $ go get -u github.com/go-sql-driver/mysql // Mysql\n  $ go get -u github.com/lib/pq // Postgres\n  $ go get -u cloud.google.com/go/datastore\n  $ go get -u github.com/si3nloong/goloquent\n```\n\n- **Import the library**\n\n```go\n  import \"github.com/si3nloong/goloquent\"\n```\n\n## Quick Start\n\n### Connect to database\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n\n    conn, err := db.Open(\"mysql\", db.Config{\n        Username: \"root\",\n        Password: \"\",\n        Host: \"localhost\",\n        Port: \"3306\",\n        Database: \"test\",\n        Logger: func(stmt *goloquent.Stmt) {\n            log.Println(stmt.TimeElapse()) // elapse time in time.Duration\n            log.Println(stmt.String()) // Sql string without any ?\n            log.Println(stmt.Raw()) // Sql prepare statement\n            log.Println(stmt.Arguments()) // Sql prepare statement's arguments\n            log.Println(fmt.Sprintf(\"[%.3fms] %s\", stmt.TimeElapse().Seconds()*1000, stmt.String()))\n        },\n    })\n    defer conn.Close()\n    if err != nil {\n        panic(\"Connection error: \", err)\n    }\n```\n\n#### User Table\n\n```go\n// Address :\ntype Address struct {\n\tLine1    string\n\tLine2    string\n\tCountry  string\n\tPostCode uint\n\tRegion   struct {\n\t\tTimeZone    time.Time\n\t\tKeys        []*datastore.Key   `goloquent:\"keys\"`\n\t\tCountryCode string             `goloquent:\"regionCode\"`\n\t\tGeolocation datastore.GeoPoint `goloquent:\"geo\"`\n\t} `goloquent:\"region\"`\n}\n\n// User : User kind parent is Merchant\ntype User struct {\n    Key             *datastore.Key `goloquent:\"__key__\"` // load table key\n    Name            string\n    Nicknames       []string\n    CountryCode     string\n    PhoneNumber     string\n    Email           string\n    BirthDate       goloquent.Date\n    Address         Address\n    Age             uint8\n    ExtraInfo       json.RawMessage\n    CreatedDateTime time.Time\n    UpdatedDateTime time.Time\n}\n\n// Load : load func will execute after load\nfunc (x *User) Load() error {\n\treturn nil\n}\n\n// Save : save func will execute before save\nfunc (x *User) Save() (error) {\n\treturn nil\n}\n```\n\n### Helper\n\n```go\n    // StringPrimaryKey is to get key value in string form\n    key := datastore.NameKey(\"Merchant\", \"mjfFgYnxBS\", nil)\n    fmt.Println(goloquent.StringKey(key)) // \"mjfFgYnxBS\"\n\n    key = datastore.IDKey(\"User\", int64(2305297334603281546), nil)\n    fmt.Println(goloquent.StringKey(key)) // \"2305297334603281546\"\n\n    key = datastore.NameKey(\"User\", int64(2305297334603281546), datastore.NameKey(\"Merchant\", \"mjfFgYnxBS\", nil))\n    fmt.Println(goloquent.StringifyKey(key)) // Merchant,'mjfFgYnxBS'/User,2305297334603281546\n```\n\n### Table\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n\n    // Check table exists\n    db.Table(\"User\").Exists() // true\n\n    // Drop table if exists\n    if err := db.Table(\"User\").DropIfExists(); err != nil {\n        log.Fatal(err)\n    }\n\n    // Add index\n    if err := db.Table(\"User\").AddIndex(\"Name\", \"Email\"); err != nil {\n        log.Fatal(err)\n    }\n\n    // Add unique index\n    if err := db.Table(\"User\").AddUniqueIndex(\"Email\"); err != nil {\n        log.Fatal(err)\n    }\n```\n\n### Create Record\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n\n    // Example\n    user := new(User)\n    user.Name = \"Hello World\"\n    user.Age = 18\n\n    // OR\n    var user *User\n    user.Name = \"Hello World\"\n    user.Age = 18\n\n    // Create without parent key\n    if err := db.Create(user, nil); err != nil {\n        log.Println(err) // fail to create record\n    }\n\n    // Create with parent key\n    parentKey := datastore.NameKey(\"Parent\", \"value\", nil)\n    if err := db.Create(user, parentKey); err != nil {\n        log.Println(err) // fail to create record\n    }\n\n    // Create without key, goloquent will auto generate primary key\n    if err := db.Create(user); err != nil {\n        log.Println(err) // fail to create record\n    }\n\n    // Create with self generate key\n    user.Key = datastore.NameKey(\"User\", \"uniqueID\", nil)\n    if err := db.Create(user); err != nil {\n        log.Println(err) // fail to create record\n    }\n```\n\n### Upsert Record\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n\n    // Example\n    user := new(User)\n    user.Name = \"Hello World\"\n    user.Age = 18\n\n    // Update if key exists, else create the user record\n    parentKey := datastore.NameKey(\"Parent\", \"value\", nil)\n    if err := db.Upsert(user, parentKey); err != nil {\n        log.Println(err) // fail\n    }\n\n    // Upsert with self generate key\n    user := new(User)\n    user.Key = datastore.NameKey(\"User\", \"uniqueID\", nil)\n    user.Name = \"Hello World\"\n    user.Age = 18\n    if err := db.Upsert(user); err != nil {\n        log.Println(err) // fail\n    }\n```\n\n### Retrieve Record\n\n- **Get Single Record using Primary Key**\n\n```go\n    // Example\n    primaryKey := datastore.IDKey(\"User\", int64(2305297334603281546), nil)\n    user := new(User)\n    if err := db.Find(primaryKey, user); err != nil {\n        log.Println(err) // error while retrieving record\n    }\n\n    if err := db.Where(\"Status\", \"=\", \"ACTIVE\").\n        Find(primaryKey, user); err != goloquent.ErrNoSuchEntity {\n        // if no record found using primary key, error `ErrNoSuchEntity` will throw instead\n        log.Println(err) // error while retrieving record\n    }\n```\n\n- **Get Single Record**\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n    // Example 1\n    user := new(User)\n    if err := db.First(user); err != nil {\n        log.Println(err) // error while retrieving record\n    }\n\n    if user.Key != nil { // if have record\n        fmt.Println(\"Have record\")\n    } else { // no record\n        fmt.Println(\"Doesnt't have record\")\n    }\n\n    // Example 2\n    user := new(User)\n    if err := db.Where(\"Email\", \"=\", \"admin@hotmail.com\").\n        First(user); err != nil {\n        log.Println(err) // error while retrieving record\n    }\n\n    // Example 3\n    age := 22\n    parentKey := datastore.IDKey(\"Parent\", 1093, nil)\n    user := new(User)\n    if err := db.Ancestor(parentKey).\n        WhereEqual(\"Age\", \u0026age).\n        OrderBy(\"-CreatedDateTime\").\n        First(user); err != nil {\n        log.Println(err) // error while retrieving record\n    }\n```\n\n- **Get Multiple Record**\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n    // Example 1\n    users := new([]User)\n    if err := db.Limit(10).Get(users); err != nil {\n        log.Println(err) // error while retrieving record\n    }\n\n    // Example 2\n    users := new([]*User)\n    if err := db.WhereEqual(\"Name\", \"Hello World\").\n        Get(users); err != nil {\n        log.Println(err) // error while retrieving record\n    }\n\n    // Example 3\n    users := new([]User)\n    if err := db.Ancestor(parentKey).\n        WhereEqual(\"Name\", \"myz\").\n        WhereEqual(\"Age\", 22).\n        Get(users); err != nil {\n        log.Println(err) // error while retrieving record\n    }\n```\n\n- **Get Record with OrderBying**\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n    // Ascending OrderBy\n    users := new([]*User)\n    if err := db.OrderBy(\"CreatedDateTime\").\n        Get(users); err != nil {\n        log.Println(err) // error while retrieving record\n    }\n\n    // Descending OrderBy\n    if err := db.Table(\"User\").\n        OrderBy(\"-CreatedDateTime\").\n        Get(users); err != nil {\n        log.Println(err) // error while retrieving record\n    }\n```\n\n- **Pagination Record**\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n\n    p := goloquent.Pagination{\n        Limit:  10,\n        Cursor: \"\", // pass the cursor that generate by the query so that it will display the next record\n    }\n\n    // Example\n    users := new([]*User)\n    if err := db.Ancestor(parentKey).\n        OrderBy(\"-CreatedDateTime\").\n        Paginate(\u0026p, users); err != nil {\n        log.Println(err) // error while retrieving record\n    }\n\n    // ***************** OR ********************\n    p := \u0026goloquent.Pagination{\n        Limit:  10, // number of records in each page\n        Cursor: \"EhQKCE1lcmNoYW50EK3bueKni5eNIxIWCgxMb3lhbHR5UG9pbnQaBkZrUUc4eA\", // pass the cursor to get next record set\n    }\n\n    users := new([]*User)\n    if err := db.Ancestor(parentKey).\n        OrderBy(\"-CreatedDateTime\").\n        Paginate(p, users); err != nil {\n        log.Println(err) // error while retrieving record\n    }\n\n    log.Println(p.NextCursor()) // next page cursor\n    log.Println(p.Count()) // record count\n```\n\n### Save Record\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n    // Example\n    if err := db.Save(user); err != nil {\n        log.Println(err) // fail to delete record\n    }\n```\n\n### Delete Record\n\n- **Delete using Primary Key**\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n    // Example\n    if err := db.Delete(user); err != nil {\n        log.Println(err) // fail to delete record\n    }\n```\n\n- **Delete using Where statement**\n\n```go\n    // Delete user table record which account type not equal to \"PREMIUM\" or \"MONTLY\"\n    if err := db.Table(\"User\").\n        WhereNotIn(\"AccountType\", []string{\n            \"PREMIUM\", \"MONTLY\",\n        }).Flush(); err != nil {\n        log.Println(err) // fail to delete record\n    }\n```\n\n### Transaction\n\n```go\n    // Example\n    if err := db.RunInTransaction(func(txn *goloquent.DB) error {\n        user := new(User)\n        if err := txn.Create(user, nil); err != nil {\n            return err // return any err to rollback the transaction\n        }\n        return nil // return nil to commit the transaction\n    }); err != nil {\n        log.Println(err)\n    }\n```\n\n- **Table Locking (only effective inside RunInTransaction)**\n\n```go\n    // Example\n    merchantKey := datastore.IDKey(\"Merchant\", \"mjfFgYnxBS\", nil)\n    userKey := datastore.IDKey(\"User\", int64(4645436182170916864), nil)\n    if err := db.RunInTransaction(func(txn *goloquent.DB) error {\n        user := new(User)\n\n        if err := txn.NewQuery().\n            WLock(). // Lock record for update\n            Find(userKey, user); err != nil {\n            return err\n        }\n\n        merchant := new(Merchant)\n        if err := txn.NewQuery().\n            RLock(). // Lock record for read\n            Find(merchantKey, merchant); err != nil {\n            return err\n        }\n\n        user.Age = 30\n        if err := txn.Save(user); err != nil {\n            return err // return any err to rollback the transaction\n        }\n\n        return nil // return nil to commit the transaction\n    }); err != nil {\n        log.Println(err)\n    }\n```\n\n- **Database Migration**\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n    // Example\n    user := new(User)\n    if err := db.Migrate(\n        new(user),\n        Merchant{},\n        \u0026Log{},\n    ); err != nil {\n        log.Println(err)\n    }\n```\n\n- **Filter Query**\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n    // Update single record\n    user := new(User)\n    if err := db.NewQuery().\n        WhereIn(\"Status\", []interface{}{\"active\", \"pending\"}).\n        First(user); err != nil {\n        log.Println(err) // error while retrieving record or record not found\n    }\n\n    // Get record with like\n    if err := db.NewQuery().\n        WhereLike(\"Name\", \"%name%\").\n        First(user); err != nil {\n        log.Println(err) // error while retrieving record or record not found\n    }\n```\n\n- **Update Query**\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n    // Update multiple record\n    if err := db.Table(\"User\").\n        Where(\"Age\", \"\u003e\", 10).\n        Update(map[string]interface{}{\n            \"Name\": \"New Name\",\n            \"Email\": \"email@gmail.com\",\n            \"UpdatedDateTime\": time.Now().UTC(),\n        }); err != nil {\n        log.Println(err) // error while retrieving record or record not found\n    }\n\n    if err := db.Table(\"User\").\n        Omit(\"Name\").\n        Where(\"Age\", \"\u003e\", 10).\n        Update(User{\n            Name: \"New Name\",\n            Email: \"test@gmail.com\",\n            UpdatedDateTime: time.Now().UTC(),\n        }); err != nil {\n        log.Println(err) // error while retrieving record or record not found\n    }\n```\n\n- **JSON Filter**\n\n```go\n    import \"github.com/si3nloong/goloquent/db\"\n\n    // JSON equal\n    users := new([]User)\n    postCode := uint32(63000)\n\tif err := db.NewQuery().\n\t\tWhereJSONEqual(\"Address\u003ePostCode\", \u0026postCode).\n\t\tGet(users); err != nil {\n\t\tlog.Println(err)\n    }\n\n    // JSON not equal\n    var timeZone *time.Time\n\tif err := db.NewQuery().\n\t\tWhereJSONNotEqual(\"Address\u003eregion.TimeZone\", timeZone).\n\t\tGet(users); err != nil {\n\t\tlog.Println(err)\n    }\n\n    // JSON contains any\n    if err := db.NewQuery().\n\t\tWhereJSONContainAny(\"Nicknames\", []string{\n            \"Joe\", \"John\", \"Robert\",\n        }).Get(users); err != nil {\n\t\tlog.Println(err)\n    }\n\n    // JSON check type\n    if err := db.NewQuery().\n\t\tWhereJSONType(\"Address\u003eregion\", \"Object\").\n        Get(users); err != nil {\n\t\tlog.Println(err)\n    }\n\n    // JSON check is object type\n    if err := db.NewQuery().\n\t\tWhereJSONIsObject(\"Address\u003eregion\").\n        Get(users); err != nil {\n\t\tlog.Println(err)\n    }\n\n    // JSON check is array type\n    if err := db.NewQuery().\n\t\tWhereJSONIsArray(\"Address\u003eregion.keys\").\n        Get(users); err != nil {\n\t\tlog.Println(err)\n    }\n```\n\n- **Data Type Support for Where Filtering**\n\nThe supported data type are :\n\n```go\n- string\n- bool\n- int, int8, int16, int32 and int64 (signed integers)\n- uint, uint8, uint16, uint32 and uint64\n- float32 and float64\n- []byte\n- datastore.GeoPoint\n- goloquent.Date\n- json.RawMessage\n- time.Time\n- pointers to any one of the above\n- *datastore.Key\n- slices of any of the above\n```\n\n- **Extra Schema Option**\n\nAvailable shorthand:\n\n- longtext (only applicable for `string` data type)\n- index\n- unsigned (only applicable for `float32` and `float64` data type)\n- flatten (only applicable for struct or []struct)\n\n```go\ntype model struct {\n    CreatedDateTime time.Time // `CreatedDateTime`\n    UpdatedDateTime time.Time // `UpdatedDateTime`\n}\n\n// Fields may have a `goloquent:\"name,options\"` tag.\ntype User struct {\n    Key         *datastore.Key `goloquent:\"__key__\"` // Primary Key\n    Name        string `goloquent:\",longtext\"` // Using `TEXT` datatype instead of `VARCHAR(255)` by default\n    CreditLimit    float64    `goloquent:\",unsigned\"` // Unsigned option only applicable for float32 \u0026 float64 data type\n    PhoneNumber string `goloquent:\",charset=utf8,collate=utf8_bin,datatype=char(20)\"`\n    Email       string\n    Skip        string `goloquent:\"-\"` // Skip this field to store in db\n    DefaultAddress struct {\n        AddressLine1 string // `DefaultAddress.AddressLine1`\n        AddressLine2 string // `DefaultAddress.AddressLine2`\n        PostCode     int    // `DefaultAddress.PostCode`\n        City         string // `DefaultAddress.City`\n        State        string // `DefaultAddress.State`\n        Country      string\n    } `goloquent:\",flatten\"` // Flatten the struct field\n    Birthdate *goloquent.Date\n    ExtraInfo json.RawMessage\n    model                    // Embedded struct\n    Deleted goloquent.SoftDelete\n}\n```\n\nThe supported data type are :\n\n```go\n- string\n- int, int8, int16, int32 and int64 (signed integers)\n- uint, uint8, uint16, uint32 and uint64\n- bool\n- float32 and float64\n- []byte\n- any type whose underlying type is one of the above predeclared types\n- datastore.GeoPoint\n- goloquent.Date\n- goloquent.SoftDelete\n- time.Time\n- json.RawMessage\n- structs whose fields are all valid value types\n- pointers to any one of the above\n- *datastore.Key\n- slices of any of the above\n```\n\n| Data Type          | Mysql               | Postgres            | Default Value       | CharSet |\n| :----------------- | :------------------ | ------------------- | :------------------ | :------ |\n| \\*datastore.Key    | varchar(512)        | varchar(512)        |                     | latin1  |\n| datastore.GeoPoint | varchar(50)         | varchar(50)         | {Lat: 0, Lng: 0}    |         |\n| string             | varchar(191)        | varchar(191)        | \"\"                  | utf8mb4 |\n| []byte             | mediumblob          | bytea               |                     |         |\n| bool               | boolean             | bool                | false               |         |\n| float32            | double              | real                | 0                   |         |\n| float64            | double              | real                | 0                   |         |\n| int                | int                 | integer             | 0                   |         |\n| int8               | tinyint             | smallint            | 0                   |         |\n| int16              | smallint            | smallint            | 0                   |         |\n| int32              | mediumint           | integer             | 0                   |         |\n| int64              | bigint              | bigint              | 0                   |         |\n| uint               | int (unsigned)      | integer (unsigned)  | 0                   |         |\n| uint8              | smallint (unsigned) | smallint (unsigned) | 0                   |         |\n| uint16             | smallint (unsigned) | smallint (unsigned) | 0                   |         |\n| uint32             | smallint (unsigned) | integer (unsigned)  | 0                   |         |\n| uint64             | bigint (unsigned)   | bigint (unsigned)   | 0                   |         |\n| slice or array     | json                | jsonb               |                     |         |\n| struct             | json                | jsonb               |                     |         |\n| json.RawMessage    | json                | jsonb               |                     |         |\n| Date               | date                | date                | 0001-01-01          |         |\n| time.Time          | datetime            | timestamp           | 0001-01-01 00:00:00 |         |\n| SoftDelete         | datetime (nullable) | timestamp           | NULL                |         |\n\n**$Key**, **$Deleted** are reserved words, please avoid to use these words as your column name\n\n[MIT License](https://github.com/si3nloong/goloquent/blob/master/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsi3nloong%2Fgoloquent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsi3nloong%2Fgoloquent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsi3nloong%2Fgoloquent/lists"}