go-query

module
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Nov 5, 2025 License: Apache-2.0

README ΒΆ

go-query

A modular, production-ready query library for Golang with a powerful, Google-like search syntax. Features CBOR-encoded cursors, rich operators, and a clean GORM-style API.

πŸ€– AI-Generated Code: This entire library was generated by AI (Claude Sonnet 4.5 and Cursor Auto) as a collaborative programming exercise. While comprehensive testing has been included, please review and test thoroughly before using in production environments.

✨ Key Features

  • πŸ” Google-like Search: Type bare words and they'll be searched automatically
  • 🎯 GORM-style API: Pass a pointer to your result slice, just like GORM
  • πŸ“¦ CBOR Cursors: 50% smaller than JSON, faster encoding
  • 🧩 Modular: MongoDB and GORM executors are separate, optional modules
  • πŸ” SQL Injection Protection: Built-in validation and parameterized queries
  • 🎨 Rich Operators: String matching (LIKE, CONTAINS, REGEX) + array operations (IN, NOT IN)
  • πŸ“Š Smart Parentheses: Full support for complex nested expressions
  • ⚑ Parser Cache: Thread-safe cache for maximum performance

Installation

# Core library
go get github.com/hadi77ir/go-query

# MongoDB executor (optional - separate module)
go get github.com/hadi77ir/go-query/executors/mongodb

# GORM executor for SQL databases (optional - separate module)
go get github.com/hadi77ir/go-query/executors/gorm

# Memory executor for in-memory slices/maps (optional - separate module)
go get github.com/hadi77ir/go-query/executors/memory

Quick Start

Performance Tip: For best performance, use ParserCache to cache parsed queries. This is especially important in production environments where the same queries are parsed repeatedly.

import (
    "github.com/hadi77ir/go-query/executors/mongodb"
    "github.com/hadi77ir/go-query/parser"
    "github.com/hadi77ir/go-query/query"
)

// Create parser cache (recommended for production)
// Cache size: 0 = no caching, >0 = cache last N queries
cache := parser.NewParserCache(100) // Cache up to 100 queries
MongoDB Example
// Connect and create executor
collection := client.Database("mydb").Collection("users")
executor := mongodb.NewExecutor(collection, query.DefaultExecutorOptions())

// Parse query using cache (recommended) - notice the bare "wireless" search term!
q, _ := cache.Parse(`wireless mouse price < 100`)

// Execute - results go directly into your slice
var products []bson.M
result, _ := executor.Execute(ctx, q, "", &products)

fmt.Printf("Found %d products\n", result.TotalItems)
GORM/SQL Example
executor := gorm.NewExecutor(db, &User{}, query.DefaultExecutorOptions())

// Create parser cache for performance
cache := parser.NewParserCache(100)

// Bare search terms automatically search the default field (name)
q, _ := cache.Parse(`john active`)  // Searches name for "john" and "active"

var users []User
result, _ := executor.Execute(ctx, q, "", &users)
Memory/In-Memory Example
// Perfect for testing or filtering in-memory data
products := []Product{
    {Name: "Wireless Mouse", Price: 29.99, Category: "electronics"},
    {Name: "USB Cable", Price: 9.99, Category: "accessories"},
}

executor := memory.NewExecutor(products, query.DefaultExecutorOptions())

// Use parser cache for better performance
cache := parser.NewParserCache(50)
q, _ := cache.Parse(`price < 50 and category = electronics`)

var results []Product
result, _ := executor.Execute(ctx, q, "", &results)
// No database needed!

Quick Query Examples

// Google-style bare search
"wireless mouse"                    // Searches name for "wireless" AND "mouse"
"price < 100 category = electronics" // Mix field queries with bare terms

// Complex expressions
"(status = active and age > 18) or premium = true"
"category IN [electronics, computers] and price < 500"

// Pagination and sorting
"page_size = 20 sort_by = price sort_order = desc"

See Query Syntax Guide for complete syntax documentation.

Supported Operators

Comparison
  • =, !=, >, >=, <, <=
String Matching
  • LIKE, NOT LIKE - SQL-style with % and _ wildcards
  • CONTAINS, ICONTAINS - Substring match (case-sensitive/insensitive)
  • STARTS_WITH, ENDS_WITH - Prefix/suffix match
  • REGEX - Regular expression
Array
  • IN, NOT IN - Value in/not in array
Logical
  • AND, OR - With proper precedence
  • () - Parentheses for grouping (fully nestable)
  • Implicit AND - Bare terms are automatically AND'ed

Documentation

πŸ“š Complete documentation is available in the docs/ folder:

Module Structure

Each executor is a separate Go module:

go-query/                     # Core library
β”œβ”€β”€ parser/                   # Query parser with cache
β”œβ”€β”€ query/                    # Core types  
β”œβ”€β”€ executor/                 # Interface
└── internal/cursor/          # CBOR cursors

executors/mongodb/            # Separate module!
executors/gorm/               # Separate module!
executors/memory/             # Separate module! (zero deps)

Benefits:

  • Only import what you need
  • No unnecessary dependencies
  • Smaller binary sizes
  • Memory executor has ZERO external dependencies

Result Structure

type Result struct {
    NextPageCursor string  // Cursor for next page
    PrevPageCursor string  // Cursor for previous page
    TotalItems     int64   // Total matching items
    ShowingFrom    int     // Start index (1-based)
    ShowingTo      int     // End index (1-based)
    ItemsReturned  int     // Items in this page
    Error          error   // Any error
}

// Data is stored directly in your slice variable!
var users []User
result, _ := executor.Execute(ctx, q, "", &users)
// users now contains the results

// Use cursor for pagination
if result.NextPageCursor != "" {
    var nextPage []User
    executor.Execute(ctx, q, result.NextPageCursor, &nextPage)
}

Count Method

Get the total number of matching items without fetching data:

// Parse query
q, _ := cache.Parse("category = electronics and price > 100")

// Get count (ignores pagination)
count, err := executor.Count(ctx, q)
if err != nil {
    // Handle error
}

fmt.Printf("Found %d matching products\n", count)

// Count matches result.TotalItems from Execute
var products []Product
result, _ := executor.Execute(ctx, q, "", &products)
fmt.Printf("TotalItems: %d (same as count: %d)\n", result.TotalItems, count)

Key Points:

  • Returns total count of all matching items (ignores pagination)
  • Always matches result.TotalItems from Execute with the same query
  • More efficient when you only need the count without data
  • Available on all executors (GORM, MongoDB, Memory, Wrapper)

License

This project is licensed under the Apache License, Version 2.0. See the LICENSE file for details.

Support

Open an issue on GitHub for bugs or feature requests.

Directories ΒΆ

Path Synopsis
executors
gorm module
memory module
wrapper module
internal

Jump to

Keyboard shortcuts

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