The library parses search query (e.g. title:"hello world" limit:42
) to struct with plain text fallback 😎.
package main
import (
"time"
"github.com/teacat/qparser"
)
type SearchFilter struct {
Keyword string `query:"keyword,default"` // Falls back for plain text
Title string `query:"title"`
Limit int `query:"limit"`
Tags []string `query:"tags"`
Before time.Time `query:"before,format=2006-01-02"`
}
func main() {
q := `hello world title:foobar limit:42 tags:admin,editor,guest before:2024-12-31`
var filter SearchFilter
if err := qparser.Unmarshal(q, &filter); err != nil {
panic(err)
}
// Keyword: hello world
// Title : foobar
// Limit : 42
// Tags : [admin editor guest]
// Before : 2024-12-31 00:00:00 +0000 UTC
}
$ go get -u github.com/teacat/qparser
Function | Description |
---|---|
IsQuery(input string) bool |
Check if input string contains search query syntax |
Unmarshal(input string, target any) error |
Parse input into target struct |
UnmarshalStrict(input string, target any) error |
Same as above but returns error if invalid type or unknown field |
The parser supports the following Go types.
- Pointer types remain
nil
when not specified in the query - Non-pointer types are set to zero value when not specified in the query
Basic Types | Pointer Types | Slice Types |
---|---|---|
string |
*string |
[]string |
bool |
*bool |
- |
int |
*int |
[]int |
int8 |
*int8 |
[]int8 |
int16 |
*int16 |
[]int16 |
int32 |
*int32 |
[]int32 |
int64 |
*int64 |
[]int64 |
uint |
*uint |
[]uint |
uint8 |
*uint8 |
[]uint8 |
uint16 |
*uint16 |
[]uint16 |
uint32 |
*uint32 |
[]uint32 |
uint64 |
*uint64 |
[]uint64 |
float32 |
*float32 |
[]float32 |
float64 |
*float64 |
[]float64 |
time.Time |
*time.Time |
- |
Use the query
tag to configure field behavior:
Tag | Description |
---|---|
query:"fieldname" |
Maps search query to struct field |
query:"fieldname,default" |
Marks field as default for plain text queries |
query:"fieldname,format=2006-01-02" |
Custom time format for time.Time fields |
Search queries use the format field:value
or field:"quoted value"
for values with spaces.
type Filter struct {
Title string `query:"title"`
Author string `query:"author"`
Category string `query:"category"`
}
q := `title:hello author:world category:"science fiction"`
// Title : hello
// Author : world
// Category: science fiction
When input doesn't contain search query syntax, it falls back to the field marked with default
.
type Filter struct {
Query string `query:"query,default"`
Type string `query:"type"`
}
q := `search for golang tutorials`
// Query: search for golang tutorials
// Type :
Query fields take precedence, and remaining text or unknown field goes to default field.
type Filter struct {
Keyword string `query:"keyword,default"`
Category string `query:"category"`
}
q := `hello world category:books foo bar emotion:happy`
// Keyword : hello world foo bar emotion:happy
// Category: books
The parser supports all Go numeric types.
type Filter struct {
Price float64 `query:"price"`
Discount float32 `query:"discount"`
Quantity uint32 `query:"qty"`
MinAge int8 `query:"min_age"`
MaxSize uint64 `query:"max_size"`
}
q := `price:10.10 discount:0.10 qty:100 min_age:10 max_size:100000`
// Price : 10.10
// Discount: 0.10
// Quantity: 100
// MinAge : 10
// MaxSize : 100000
The parser supports slices of all numeric types and strings with comma-separated (no space between) values.
type Filter struct {
Tags []string `query:"tags"`
Scores []int `query:"scores"`
Weights []float64 `query:"weights"`
}
q := `tags:foo,bar scores:10,20,30 weights:0.1,0.2,0.3`
// Tags : { "foo", "bar" }
// Scores : { 10, 20, 30 }
// Weights: { 0.1, 0.2, 0.3 }
Note: For values containing spaces or commas, wrap the value in quotes:
q := `tags:"hello world,golang,web dev"`
// Tags: { "hello world", "golang", "web dev" }
The parser supports custom/multiple formats for a single field. The parser will try each format until one succeeds.
Default formats are used when no custom format is specified.
type Filter struct {
StartedAt time.Time `query:"started_at"` // Default formats are 2006, 2006-01, 2006-01-02
EndedAt time.Time `query:"ended_at,format=01/02/2006"` // Custom US format
PublishDate time.Time `query:"publish_date,format=2006 2006-01 2006-01-02"` // Multiple formats (space-separated)
}
q := `started_at:2025-12-30 ended_at:30/12/2025 publish_date:2025`
// StartedAt : time.Date(2025, 12, 30, 0, 0, 0, 0, time.UTC)
// EndedAt : time.Date(2025, 12, 30, 0, 0, 0, 0, time.UTC)
// PublishDate: time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
The parser supports both regular bool
and optional *bool
(pointer) types.
type Filter struct {
IsActive bool `query:"is_active"`
IsRequired *bool `query:"is_required"` // nil if not specified
}
q := `is_active:true`
// IsActive : true
// IsRequired: nil
The parser accepts the following values for boolean fields (case-insensitive):
Parsed As | Value | Examples |
---|---|---|
true |
true , 1 , yes , y , on |
active:true , enabled:1 |
false |
false , 0 , no , n , off |
active:false , hidden:0 |
Note: Any unrecognized boolean value will result in false
and may trigger an error in strict mode.