[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-gorm-advanced-query-helpers-guardrails-en":3,"article-related-gorm-advanced-query-helpers-guardrails-en":30,"series-tools-a8344911-b020-4892-ba6c-621df2dc11f8":75},{"id":4,"slug":5,"title":6,"content":7,"summary":8,"source":9,"source_url":10,"author":11,"image_url":12,"cover_image":12,"category":13,"language":14,"translated_content":11,"related_article_id":15,"keywords":16,"key_takeaways":22,"views":26,"created_at":27,"published_at":28,"topic_cluster_id":29},"a8344911-b020-4892-ba6c-621df2dc11f8","gorm-advanced-query-helpers-guardrails-en","GORM query helpers turn SQL into guardrails","\u003Cp data-speakable=\"summary\">This breaks down GORM advanced queries into a copy-ready pattern for safer reads and writes.\u003C\u002Fp>\u003Cp>I've been using GORM long enough to know when it gets annoying. Basic CRUD is fine. Then the codebase grows, the models get fat, and suddenly every query is either selecting way too much, locking way too little, or doing that lovely little dance where you check for a row, then insert it, then realize two requests raced each other and now you have a mess.\u003C\u002Fp>\u003Cp>That’s the part that always felt off to me. Not GORM itself, exactly. The defaults just stop being enough once you care about performance, concurrency, and not writing the same brittle SQL over and over. The \u003Ca href=\"https:\u002F\u002Fgorm.io\u002Fdocs\u002Fadvanced_query.html\">Advanced Query\u003C\u002Fa> doc on gorm.io is the page I keep coming back to when I need to stop hand-waving and make the query do the actual job. It’s not flashy. It’s just the stuff that saves you from future bug reports.\u003C\u002Fp>\u003Cp>What I like here is that the patterns are small but practical: select fewer columns, lock rows when you mean it, tuck logic into subqueries instead of building string soup, and use FirstOrInit or FirstOrCreate when you want one path for “find it” and “make it.” That’s the real value. Fewer branches in application code, fewer surprise writes, fewer opportunities for me to forget a condition at 2 a.m.\u003C\u002Fp>\u003Ch2>Stop selecting the whole table when you only need three fields\u003C\u002Fh2>\u003Cblockquote>In GORM, you can efficiently select specific fields using the Select method.\u003C\u002Fblockquote>\u003Cp>What this actually means is simple: don’t drag a giant model through your \u003Ca href=\"\u002Ftag\u002Fapi\">API\u003C\u002Fa> when the caller only needs an ID and a name. GORM will happily map query results into a smaller struct, and that’s a nice way to keep payloads and scanning work down.\u003C\u002Fp>\n\u003Cfigure class=\"my-6\">\u003Cimg src=\"https:\u002F\u002Fxxdpdyhzhpamafnrdkyq.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Fcovers\u002Finline-1782606800691-nx1d.png\" alt=\"GORM query helpers turn SQL into guardrails\" class=\"rounded-xl w-full\" loading=\"lazy\" \u002F>\u003C\u002Ffigure>\n\u003Cp>The docs show this with a large \u003Ccode>User\u003C\u002Fcode> model and a tiny \u003Ccode>APIUser\u003C\u002Fcode> struct. When you query into \u003Ccode>APIUser\u003C\u002Fcode>, GORM automatically selects the matching columns. That’s a small thing until you’ve got tables with dozens of fields and a hot endpoint getting hammered.\u003C\u002Fp>\u003Cpre>\u003Ccode>type User struct {\n    ID uint\n    Name string\n    Age int\n    Gender string\n    \u002F\u002F hundreds of fields\n}\n\ntype APIUser struct {\n    ID uint\n    Name string\n}\n\ndb.Model(&User{}).Limit(10).Find(&APIUser{})\n\u002F\u002F SQL: SELECT `id`, `name` FROM `users` LIMIT 10\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>I’ve run into this exact issue in APIs where the model kept growing because product kept asking for one more field. The query still worked, but the response got heavier and the code got sloppier. Once I started querying into DTO-style structs, a lot of accidental data exposure just disappeared.\u003C\u002Fp>\u003Cp>There’s also \u003Ca href=\"https:\u002F\u002Fgorm.io\u002Fdocs\u002Fadvanced_query.html#Smart-Select-Fields\">QueryFields mode\u003C\u002Fa>, which flips the behavior so GORM selects columns by name instead of using \u003Ccode>*\u003C\u002Fcode>. That’s handy when you want explicitness across the board, but I wouldn’t turn it on blindly without checking the generated SQL on your busiest paths.\u003C\u002Fp>\u003Cul>\u003Cli>Use a smaller destination struct when the API doesn’t need the full model.\u003C\u002Fli>\u003Cli>Use \u003Ccode>Select\u003C\u002Fcode> when you want to be explicit about the columns.\u003C\u002Fli>\u003Cli>Check the generated SQL once, then stop guessing.\u003C\u002Fli>\u003C\u002Ful>\u003Cp>How to apply it: define read models for endpoints, reports, and background jobs. Keep your write model separate if the table is wide. If you’re refactoring an old query, start by replacing \u003Ccode>Find(&users)\u003C\u002Fcode> with a smaller destination type and compare the SQL before you touch anything else.\u003C\u002Fp>\u003Ch2>Lock rows when you actually mean to update them\u003C\u002Fh2>\u003Cblockquote>GORM supports different types of locks, for example: db.Clauses(clause.Locking{Strength: \"UPDATE\"}).Find(&users)\u003C\u002Fblockquote>\u003Cp>What this actually means is that you can stop pretending a read is harmless when it’s really a setup for a write. If you’re going to inspect rows and then update them, you often want a row lock so another transaction doesn’t sneak in and change the same data underneath you.\u003C\u002Fp>\u003Cp>The basic example is \u003Ccode>FOR UPDATE\u003C\u002Fcode>. GORM exposes that through \u003Ccode>clause.Locking\u003C\u002Fcode>. There’s also \u003Ccode>SHARE\u003C\u002Fcode>, \u003Ccode>NOWAIT\u003C\u002Fcode>, and \u003Ccode>SKIP LOCKED\u003C\u002Fcode>. Those last two are the ones that matter when concurrency gets ugly.\u003C\u002Fp>\u003Cpre>\u003Ccode>db.Clauses(clause.Locking{Strength: \"UPDATE\"}).Find(&users)\n\u002F\u002F SQL: SELECT * FROM `users` FOR UPDATE\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>The docs are clear that the selected rows stay locked for the duration of the transaction. That matters for workflows like job processing, inventory updates, or anything where two workers might try to claim the same record. If you’ve ever built a queue worker and then watched two processes grab the same job, you already know why this exists.\u003C\u002Fp>\u003Cp>\u003Ca href=\"https:\u002F\u002Fgorm.io\u002Fdocs\u002Fadvanced_query.html#Locking\">NOWAIT\u003C\u002Fa> is the blunt option: fail immediately if the row is locked. \u003Ccode>SKIP LOCKED\u003C\u002Fcode> is the more practical one when you want workers to keep moving and just ignore rows somebody else already owns. I’ve used that pattern in background processors where waiting would have just clogged the pipeline.\u003C\u002Fp>\u003Cul>\u003Cli>Use \u003Ccode>UPDATE\u003C\u002Fcode> locks when a read is the first half of a write.\u003C\u002Fli>\u003Cli>Use \u003Ccode>NOWAIT\u003C\u002Fcode> when waiting would be worse than failing fast.\u003C\u002Fli>\u003Cli>Use \u003Ccode>SKIP LOCKED\u003C\u002Fcode> for concurrent workers that should keep moving.\u003C\u002Fli>\u003C\u002Ful>\u003Cp>How to apply it: wrap the lock in a transaction, keep the locked section short, and don’t do network calls while holding the transaction open. That part sounds obvious until someone stuffs an HTTP request inside the lock and the whole thing turns into self-inflicted pain.\u003C\u002Fp>\u003Ch2>Use subqueries instead of building string soup\u003C\u002Fh2>\u003Cblockquote>Subqueries are a powerful feature in SQL, allowing nested queries.\u003C\u002Fblockquote>\u003Cp>What this actually means is that you can keep complex query logic readable without dropping down to raw SQL too early. GORM accepts a \u003Ccode>*gorm.DB\u003C\u002Fcode> as a subquery parameter, which lets you compose queries instead of concatenating fragments and hoping the parentheses land in the right place.\u003C\u002Fp>\n\u003Cfigure class=\"my-6\">\u003Cimg src=\"https:\u002F\u002Fxxdpdyhzhpamafnrdkyq.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Fcovers\u002Finline-1782606798536-wh78.png\" alt=\"GORM query helpers turn SQL into guardrails\" class=\"rounded-xl w-full\" loading=\"lazy\" \u002F>\u003C\u002Ffigure>\n\u003Cp>The doc’s example compares an amount against the average amount from the same table. That’s the kind of thing I’d rather express as a subquery than as a giant raw string someone has to mentally parse every time they touch it.\u003C\u002Fp>\u003Cpre>\u003Ccode>db.Where(\"amount > (?)\", db.Table(\"orders\").Select(\"AVG(amount)\")).Find(&orders)\n\u002F\u002F SQL: SELECT * FROM \"orders\" WHERE amount > (SELECT AVG(amount) FROM \"orders\");\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>There’s also the nested version, where one subquery feeds another clause. That’s useful when you need grouped logic but still want the query builder to handle quoting and parameter binding.\u003C\u002Fp>\u003Cp>I’ve reached for this when I needed a report query that compared a row against a computed \u003Ca href=\"\u002Ftag\u002Fbenchmark\">benchmark\u003C\u002Fa>. The alternative was a raw statement that nobody wanted to edit later. GORM’s subquery support is not magic, but it keeps the intent visible.\u003C\u002Fp>\u003Cp>You can also use subqueries in the \u003Ccode>FROM\u003C\u002Fcode> clause. That’s the move when you want to treat a filtered or projected dataset like a temp table without actually creating one.\u003C\u002Fp>\u003Cpre>\u003Ccode>db.Table(\"(?) as u\", db.Model(&User{}).Select(\"name\", \"age\")).Where(\"age = ?\", 18).Find(&User{})\n\u002F\u002F SQL: SELECT * FROM (SELECT `name`,`age` FROM `users`) as u WHERE `age` = 18\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>How to apply it: use subqueries when a query starts repeating itself or when you need to compare against a derived value. If the query becomes unreadable, stop and name the subquery variable. I’ve found that one tiny naming step saves a lot of future swearing.\u003C\u002Fp>\u003Ch2>Group conditions keep parentheses from lying to you\u003C\u002Fh2>\u003Cblockquote>Group Conditions in GORM provide a more readable and maintainable way to write complex SQL queries involving multiple conditions.\u003C\u002Fblockquote>\u003Cp>What this actually means is that GORM can help you preserve the logic of a messy WHERE clause without making the parentheses your problem. And parentheses are always the problem when a query gets complicated.\u003C\u002Fp>\u003Cp>The docs show a pizza example with nested \u003Ccode>AND\u003C\u002Fcode> and \u003Ccode>OR\u003C\u002Fcode> groups. It looks verbose in code, but it maps cleanly to SQL and makes the intent much harder to misunderstand later.\u003C\u002Fp>\u003Cpre>\u003Ccode>db.Where(\n    db.Where(\"pizza = ?\", \"pepperoni\").Where(db.Where(\"size = ?\", \"small\").Or(\"size = ?\", \"medium\")),\n).Or(\n    db.Where(\"pizza = ?\", \"hawaiian\").Where(\"size = ?\", \"xlarge\"),\n).Find(&Pizza{})\n\u002F\u002F SQL: SELECT * FROM `pizzas` WHERE (pizza = \"pepperoni\" AND (size = \"small\" OR size = \"medium\")) OR (pizza = \"hawaiian\" AND size = \"xlarge\")\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>I like this because the code mirrors the logic instead of hiding it in a single string where one missing parenthesis changes the entire meaning. I’ve had to debug those queries before, and it’s always embarrassing how small the mistake was.\u003C\u002Fp>\u003Cp>How to apply it: if you catch yourself writing a WHERE clause with multiple AND\u002FOR branches in one string, stop and break it into grouped conditions. Use nested \u003Ccode>db.Where(...)\u003C\u002Fcode> calls to make the intent obvious. Your future self will thank you, and your teammate won’t need to reverse-engineer your brain.\u003C\u002Fp>\u003Cp>One more practical note: group conditions are especially useful when you’re building filter UIs. Each branch can map to a user-selected option without turning the query into a giant switch statement.\u003C\u002Fp>\u003Ch2>Named arguments are boring, and that’s why they’re good\u003C\u002Fh2>\u003Cblockquote>GORM enhances the readability and maintainability of SQL queries by supporting named arguments.\u003C\u002Fblockquote>\u003Cp>What this actually means is that you can stop counting positional placeholders like a machine from 2009. Named arguments make complex conditions easier to read, especially when the same value appears more than once.\u003C\u002Fp>\u003Cp>The docs show both \u003Ccode>sql.Named\u003C\u002Fcode> and a \u003Ccode>map[string]interface{}\u003C\u002Fcode>. Both work. I usually prefer the one that makes the call site easiest to scan in that specific piece of code.\u003C\u002Fp>\u003Cpre>\u003Ccode>db.Where(\"name1 = @name OR name2 = @name\", sql.Named(\"name\", \"jinzhu\")).Find(&user)\n\u002F\u002F SQL: SELECT * FROM `users` WHERE name1 = \"jinzhu\" OR name2 = \"jinzhu\"\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>There’s a reason I keep liking this pattern: it reduces the mental tax of reading a query. If I can tell what a parameter means without tracing its position through four helper functions, that’s already a win.\u003C\u002Fp>\u003Cp>How to apply it: use named arguments in queries with repeated values, optional filters, or helper functions that build conditions incrementally. If the query is tiny, positional placeholders are fine. If it’s not tiny, named args keep it from becoming self-inflicted archaeology.\u003C\u002Fp>\u003Cul>\u003Cli>\u003Ccode>sql.Named\u003C\u002Fcode> is nice when you want the parameter to read like a real argument.\u003C\u002Fli>\u003Cli>\u003Ccode>map[string]interface{}\u003C\u002Fcode> is handy when values are assembled dynamically.\u003C\u002Fli>\u003Cli>Use the style that makes the call site easiest to inspect later.\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>FirstOrInit and FirstOrCreate are the missing “find or make” moves\u003C\u002Fh2>\u003Cblockquote>FirstOrInit fetches the first record that matches given conditions, or initializes a new instance if no matching record is found.\u003C\u002Fblockquote>\u003Cp>What this actually means is that you can collapse a common application pattern into one call: look for the record, and if it isn’t there, prepare a struct with defaults. That’s \u003Ca href=\"https:\u002F\u002Fgorm.io\u002Fdocs\u002Fadvanced_query.html#FirstOrInit\">FirstOrInit\u003C\u002Fa>. If you want the record persisted too, that’s \u003Ca href=\"https:\u002F\u002Fgorm.io\u002Fdocs\u002Fadvanced_query.html#FirstOrCreate\">FirstOrCreate\u003C\u002Fa>.\u003C\u002Fp>\u003Cp>I’ve seen people write this as three separate steps all over the place: query, branch, initialize, maybe save. It works, but it’s noisy and easy to get wrong. GORM gives you a cleaner path, and the difference between \u003Ccode>Attrs\u003C\u002Fcode> and \u003Ccode>Assign\u003C\u002Fcode> is the part you actually need to remember.\u003C\u002Fp>\u003Cp>\u003Ccode>Attrs\u003C\u002Fcode> fills in values only when the record is missing. \u003Ccode>Assign\u003C\u002Fcode> sets values on the struct whether the record exists or not. With \u003Ccode>FirstOrInit\u003C\u002Fcode>, those changes stay in memory. With \u003Ccode>FirstOrCreate\u003C\u002Fcode>, they can be written back to the database.\u003C\u002Fp>\u003Cpre>\u003Ccode>db.Where(User{Name: \"non_existing\"}).Attrs(User{Age: 20}).FirstOrInit(&user)\n\u002F\u002F user -> User{Name: \"non_existing\", Age: 20} if not found\n\ndb.Where(User{Name: \"Jinzhu\"}).Assign(User{Age: 20}).FirstOrInit(&user)\n\u002F\u002F user -> User{ID: 111, Name: \"Jinzhu\", Age: 20} if found\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>That distinction matters more than the name suggests. I’ve used \u003Ccode>Attrs\u003C\u002Fcode> when I wanted a clean default for a new struct, and \u003Ccode>Assign\u003C\u002Fcode> when I wanted the in-memory object to reflect a newer value even if the database row already existed. It’s a subtle difference, but it saves you from writing separate code paths.\u003C\u002Fp>\u003Cp>\u003Ccode>FirstOrCreate\u003C\u002Fcode> pushes the same idea one step further by saving the result. The docs also point out \u003Ccode>RowsAffected\u003C\u002Fcode>, which is the easiest way to tell whether you created something or just loaded it.\u003C\u002Fp>\u003Cpre>\u003Ccode>result := db.FirstOrCreate(&user, User{Name: \"non_existing\"})\n\u002F\u002F result.RowsAffected => 1 when created\n\nresult = db.Where(User{Name: \"jinzhu\"}).FirstOrCreate(&user)\n\u002F\u002F result.RowsAffected => 0 when found\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>How to apply it: use \u003Ccode>FirstOrInit\u003C\u002Fcode> when you need a prepared struct but not a database write yet. Use \u003Ccode>FirstOrCreate\u003C\u002Fcode> when the record should exist after the call. If you’re in a concurrent path, remember that this is still not a magic substitute for proper uniqueness constraints. I’d still back it with a unique index, because I enjoy sleeping.\u003C\u002Fp>\u003Ch2>Find-to-map is the escape hatch for dynamic output\u003C\u002Fh2>\u003Cblockquote>GORM provides flexibility in querying data by allowing results to be scanned into a map[string]interface{} or []map[string]interface{}.\u003C\u002Fblockquote>\u003Cp>What this actually means is that you don’t always need a struct. Sometimes you just need a dynamic shape for admin views, exports, or ad hoc processing. GORM can scan directly into a map or a slice of maps, which is surprisingly handy when the schema is not the point.\u003C\u002Fp>\u003Cp>The doc does have one important warning: include \u003Ccode>Model\u003C\u002Fcode> or \u003Ccode>Table\u003C\u002Fcode> so GORM knows what to query. Forget that, and you’re asking for a confusing failure.\u003C\u002Fp>\u003Cpre>\u003Ccode>result := map[string]interface{}{}\ndb.Model(&User{}).First(&result, \"id = ?\", 1)\n\u002F\u002F SQL: SELECT * FROM `users` WHERE id = 1 LIMIT 1\n\nvar results []map[string]interface{}\ndb.Table(\"users\").Find(&results)\n\u002F\u002F SQL: SELECT * FROM `users`\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>I use this pattern when I’m building something exploratory or when the output shape is driven by configuration instead of a fixed struct. It’s not my first choice for normal application code, but it’s better than inventing a fake model just to hold a temporary result.\u003C\u002Fp>\u003Cp>How to apply it: reach for maps when the result is genuinely dynamic, not just because you’re avoiding typing a struct. If the shape is stable, use a struct. If the shape changes based on user input or report config, maps are fine.\u003C\u002Fp>\u003Ch2>The template you can copy\u003C\u002Fh2>\u003Cpre>\u003Ccode>\u002F\u002F GORM advanced query playbook\n\u002F\u002F Copy this into your project and adapt the model names.\n\npackage data\n\nimport (\n    \"database\u002Fsql\"\n\n    \"gorm.io\u002Fgorm\"\n    \"gorm.io\u002Fgorm\u002Fclause\"\n)\n\ntype User struct {\n    ID     uint\n    Name   string\n    Age    int\n    Status string\n}\n\ntype APIUser struct {\n    ID   uint\n    Name string\n}\n\n\u002F\u002F 1) Select only the fields you need\nfunc ListAPIUsers(db *gorm.DB, limit int) ([]APIUser, error) {\n    var users []APIUser\n    err := db.Model(&User{}).\n        Limit(limit).\n        Find(&users).Error\n    return users, err\n}\n\n\u002F\u002F 2) Lock rows before updating them in a transaction\nfunc ClaimUsers(db *gorm.DB) ([]User, error) {\n    var users []User\n    err := db.Transaction(func(tx *gorm.DB) error {\n        return tx.Clauses(clause.Locking{\n            Strength: \"UPDATE\",\n            Options:   \"SKIP LOCKED\",\n        }).Where(\"status = ?\", \"pending\").Find(&users).Error\n    })\n    return users, err\n}\n\n\u002F\u002F 3) Use subqueries instead of raw string soup\nfunc UsersAboveAverageAge(db *gorm.DB) ([]User, error) {\n    var users []User\n    avgAge := db.Model(&User{}).Select(\"AVG(age)\")\n    err := db.Where(\"age > (?)\", avgAge).Find(&users).Error\n    return users, err\n}\n\n\u002F\u002F 4) Keep complex filters readable with grouped conditions\nfunc FindPizzaCandidates(db *gorm.DB) error {\n    return db.Where(\n        db.Where(\"pizza = ?\", \"pepperoni\").Where(\n            db.Where(\"size = ?\", \"small\").Or(\"size = ?\", \"medium\"),\n        ),\n    ).Or(\n        db.Where(\"pizza = ?\", \"hawaiian\").Where(\"size = ?\", \"xlarge\"),\n    ).Error\n}\n\n\u002F\u002F 5) Use named arguments for repeated values\nfunc FindBySharedName(db *gorm.DB, name string) (*User, error) {\n    var user User\n    err := db.Where(\"name1 = @name OR name2 = @name\", sql.Named(\"name\", name)).First(&user).Error\n    return &user, err\n}\n\n\u002F\u002F 6) Prepare a struct without writing it yet\nfunc InitUser(db *gorm.DB, name string) (*User, error) {\n    var user User\n    err := db.Where(User{Name: name}).Attrs(User{Status: \"new\"}).FirstOrInit(&user).Error\n    return &user, err\n}\n\n\u002F\u002F 7) Find or create, and update the in-memory record when needed\nfunc UpsertUser(db *gorm.DB, name string) (*User, bool, error) {\n    var user User\n    result := db.Where(User{Name: name}).Assign(User{Status: \"active\"}).FirstOrCreate(&user)\n    created := result.RowsAffected == 1\n    return &user, created, result.Error\n}\n\n\u002F\u002F 8) Scan dynamic results into maps when the shape is not stable\nfunc LoadDynamicUsers(db *gorm.DB) ([]map[string]interface{}, error) {\n    var rows []map[string]interface{}\n    err := db.Table(\"users\").Find(&rows).Error\n    return rows, err\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>This is the version I’d actually keep around in a project: one file, one set of examples, no ceremony. The point isn’t to copy every line blindly. The point is to have a sane starting point for the queries you keep writing anyway.\u003C\u002Fp>\u003Cp>Original source: \u003Ca href=\"https:\u002F\u002Fgorm.io\u002Fdocs\u002Fadvanced_query.html\">https:\u002F\u002Fgorm.io\u002Fdocs\u002Fadvanced_query.html\u003C\u002Fa>. Everything above is my breakdown and refactor of that documentation into a more usable pattern for day-to-day development, with the code examples adapted into a copy-ready template.\u003C\u002Fp>","I break down GORM advanced queries and give you a copy-ready pattern for selective reads, locks, subqueries, and upserts.","gorm.io","https:\u002F\u002Fgorm.io\u002Fdocs\u002Fadvanced_query.html",null,"https:\u002F\u002Fxxdpdyhzhpamafnrdkyq.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Fcovers\u002Finline-1782606800691-nx1d.png","tools","en","199c5c27-7d55-46b4-a59f-fa2a9d4d6340",[17,18,19,20,21],"gorm","go","sql","query-builder","transactions",[23,24,25],"Select fewer columns when you only need part of a model.","Use locks, subqueries, and grouped conditions to make query intent explicit.","FirstOrInit and FirstOrCreate collapse common find-or-create flows into one call.",0,"2026-06-28T00:32:59.159701+00:00","2026-06-28T00:32:59.151+00:00","a7343b93-37cc-4634-a2bc-707f6275bdb6",{"tags":31,"relatedLang":34,"relatedPosts":38},[32],{"name":33,"slug":18},"Go",{"id":15,"slug":35,"title":36,"language":37},"gorm-advanced-query-helpers-guardrails-zh","GORM 查詢助手把 SQL 變護欄","zh",[39,45,51,57,63,69],{"id":40,"slug":41,"title":42,"cover_image":43,"image_url":43,"created_at":44,"category":13},"76829ec6-953d-4ae8-8cbd-7d4ebf92ed5f","golangci-lint-faq-ci-policy-en","Golangci-lint’s FAQ turns CI noise into a policy","https:\u002F\u002Fxxdpdyhzhpamafnrdkyq.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Fcovers\u002Finline-1782607698288-yn0f.png","2026-06-28T00:47:54.495313+00:00",{"id":46,"slug":47,"title":48,"cover_image":49,"image_url":49,"created_at":50,"category":13},"af69202e-8810-49fc-ba85-dfd18ae1217e","golangci-lint-v2-5-0-revive-checks-en","Golangci-lint v2.5.0 adds 8 revive checks","https:\u002F\u002Fxxdpdyhzhpamafnrdkyq.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Fcovers\u002Finline-1782605879327-gt0d.png","2026-06-28T00:17:31.879198+00:00",{"id":52,"slug":53,"title":54,"cover_image":55,"image_url":55,"created_at":56,"category":13},"5521addb-874b-44fe-a38d-32f4299010d2","open-source-ai-projects-developers-2026-en","7 open-source AI projects developers need in 2026","https:\u002F\u002Fxxdpdyhzhpamafnrdkyq.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Fcovers\u002Finline-1782593283378-u4f3.png","2026-06-27T20:47:36.97629+00:00",{"id":58,"slug":59,"title":60,"cover_image":61,"image_url":61,"created_at":62,"category":13},"0a5f2eb9-7c4b-4382-b0e0-501f1bc3e70f","midjourney-review-2026-v8-worth-it-en","Midjourney Review 2026: Is V8 Still Worth It?","https:\u002F\u002Fxxdpdyhzhpamafnrdkyq.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Fcovers\u002Finline-1782576174182-vc3v.png","2026-06-27T16:02:30.622473+00:00",{"id":64,"slug":65,"title":66,"cover_image":67,"image_url":67,"created_at":68,"category":13},"3a9390c3-0160-4d2a-bb42-25f6437aecb4","midjourney-v81-faster-renders-pricing-video-en","Midjourney V8.1 lands with 4-5x faster renders","https:\u002F\u002Fxxdpdyhzhpamafnrdkyq.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Fcovers\u002Finline-1782573467328-ybtn.png","2026-06-27T15:17:23.634784+00:00",{"id":70,"slug":71,"title":72,"cover_image":73,"image_url":73,"created_at":74,"category":13},"9e1a94d8-5e53-4bca-b32d-83c73cef1639","mlops-roadmap-2026-turns-learning-into-delivery-en","MLOps Roadmap 2026 Turns Learning Into Delivery","https:\u002F\u002Fxxdpdyhzhpamafnrdkyq.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Fcovers\u002Finline-1782567216077-5x25.png","2026-06-27T13:33:07.454419+00:00",[76,81,86,91,96,101,106,111,116,121],{"id":77,"slug":78,"title":79,"created_at":80},"8008f1a9-7a00-4bad-88c9-3eedc9c6b4b1","surepath-ai-mcp-policy-controls-en","SurePath AI's New MCP Policy Controls Enhance AI Security","2026-03-26T01:26:52.222015+00:00",{"id":82,"slug":83,"title":84,"created_at":85},"27e39a8f-b65d-4f7b-a875-859e2b210156","mcp-standard-ai-tools-2026-en","MCP Standard in 2026: Integrating AI Tools","2026-03-26T01:27:43.127519+00:00",{"id":87,"slug":88,"title":89,"created_at":90},"165f9a19-c92d-46ba-b3f0-7125f662921d","rag-2026-transforming-enterprise-ai-en","How RAG in 2026 is Transforming Enterprise AI","2026-03-26T01:28:11.485236+00:00",{"id":92,"slug":93,"title":94,"created_at":95},"6a2a8e6e-b956-49d8-be12-cc47bdc132b2","mastering-ai-prompts-2026-guide-en","Mastering AI Prompts: A 2026 Guide for Developers","2026-03-26T01:29:07.835148+00:00",{"id":97,"slug":98,"title":99,"created_at":100},"3ab2c67e-4664-4c67-a013-687a2f605814","garry-tan-open-sources-claude-code-toolkit-en","Garry Tan Open-Sources a Claude Code Toolkit","2026-03-26T08:26:20.245934+00:00",{"id":102,"slug":103,"title":104,"created_at":105},"66a7cbf8-7e76-41d4-9bbf-eaca9761bf69","github-ai-projects-to-watch-in-2026-en","20 GitHub AI Projects to Watch in 2026","2026-03-26T08:28:09.752027+00:00",{"id":107,"slug":108,"title":109,"created_at":110},"9f332fda-eace-448a-a292-2283951eee71","practical-github-guide-learning-ml-2026-en","A Practical GitHub Guide to Learning ML in 2026","2026-03-27T01:16:50.125678+00:00",{"id":112,"slug":113,"title":114,"created_at":115},"1b1f637d-0f4d-42bd-974b-07b53829144d","aiml-2026-student-ai-ml-lab-repo-review-en","AIML-2026 Is a Bare-Bones Student Lab Repo","2026-03-27T01:21:51.661231+00:00",{"id":117,"slug":118,"title":119,"created_at":120},"6d1bf3f6-e191-4d30-b55b-8a0722fa6afe","ai-trending-github-repos-and-research-feeds-en","AI Trending Tracks Repos and Research Feeds","2026-03-27T01:31:35.709532+00:00",{"id":122,"slug":123,"title":124,"created_at":125},"010539a1-4c3a-4bd3-937a-26616422ee0d","awesome-ai-for-science-research-tools-map-en","Awesome AI for Science Is Becoming a Real Research Map","2026-03-27T01:46:50.89513+00:00"]