Skip to content

Commit 774dea1

Browse files
committed
first commit
0 parents  commit 774dea1

File tree

5 files changed

+1190
-0
lines changed

5 files changed

+1190
-0
lines changed

README.md

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# qparser [![GoDoc](https://godoc.org/github.com/teacat/qparser?status.svg)](https://godoc.org/github.com/teacat/qparser) [![Coverage Status](https://coveralls.io/repos/github/teacat/qparser/badge.svg?branch=master)](https://coveralls.io/github/teacat/qparser?branch=master) [![Build Status](https://travis-ci.org/teacat/qparser.svg?branch=master)](https://travis-ci.org/teacat/qparser) [![Go Report Card](https://goreportcard.com/badge/github.com/teacat/qparser)](https://goreportcard.com/report/github.com/teacat/qparser)
2+
3+
The library parses search query (e.g. `title:"hello world" limit:42`) to struct with plain text fallback 😎.
4+
5+
## Table of Contents
6+
7+
- [Installation](#installation)
8+
- [Functions](#functions)
9+
- [Field Types](#field-types)
10+
- [Struct Tags](#struct-tags)
11+
- [Examples](#examples)
12+
- [Basic Parsing](#basic-parsing)
13+
- [Fallback Parsing](#fallback-parsing)
14+
- [Enhanced Fallback Parsing](#enhanced-fallback-parsing)
15+
- [Numeric Parsing](#numeric-parsing)
16+
- [Slice Parsing](#slice-parsing)
17+
- [Time Parsing](#time-parsing)
18+
- [Boolean Parsing](#boolean-parsing)
19+
20+
```go
21+
package main
22+
23+
import (
24+
"time"
25+
26+
"github.com/teacat/qparser"
27+
)
28+
29+
type SearchFilter struct {
30+
Keyword string `query:"keyword,default"` // Falls back for plain text
31+
Title string `query:"title"`
32+
Limit int `query:"limit"`
33+
Tags []string `query:"tags"`
34+
Before time.Time `query:"before,format=2006-01-02"`
35+
}
36+
37+
func main() {
38+
q := `hello world title:foobar limit:42 tags:admin,editor,guest before:2024-12-31`
39+
40+
var filter SearchFilter
41+
if err := qparser.Unmarshal(q, &filter); err != nil {
42+
panic(err)
43+
}
44+
45+
// Keyword: hello world
46+
// Title : foobar
47+
// Limit : 42
48+
// Tags : [admin editor guest]
49+
// Before : 2024-12-31 00:00:00 +0000 UTC
50+
}
51+
```
52+
53+
## Installation
54+
55+
```bash
56+
$ go get -u github.com/teacat/qparser
57+
```
58+
59+
## Functions
60+
61+
| Function | Description |
62+
| ------------------------------------------------- | ---------------------------------------------------------------- |
63+
| `IsQuery(input string) bool` | Check if input string contains search query syntax |
64+
| `Unmarshal(input string, target any) error` | Parse input into target struct |
65+
| `UnmarshalStrict(input string, target any) error` | Same as above but returns error if invalid type or unknown field |
66+
67+
## Field Types
68+
69+
The parser supports the following Go types.
70+
71+
- Pointer types remain `nil` when **not specified** in the query
72+
- Non-pointer types are set to zero value when **not specified** in the query
73+
74+
| Basic Types | Pointer Types | Slice Types |
75+
| ----------- | ------------- | ----------- |
76+
| `string` | `*string` | `[]string` |
77+
| `bool` | `*bool` | - |
78+
| `int` | `*int` | `[]int` |
79+
| `int8` | `*int8` | `[]int8` |
80+
| `int16` | `*int16` | `[]int16` |
81+
| `int32` | `*int32` | `[]int32` |
82+
| `int64` | `*int64` | `[]int64` |
83+
| `uint` | `*uint` | `[]uint` |
84+
| `uint8` | `*uint8` | `[]uint8` |
85+
| `uint16` | `*uint16` | `[]uint16` |
86+
| `uint32` | `*uint32` | `[]uint32` |
87+
| `uint64` | `*uint64` | `[]uint64` |
88+
| `float32` | `*float32` | `[]float32` |
89+
| `float64` | `*float64` | `[]float64` |
90+
| `time.Time` | `*time.Time` | - |
91+
92+
## Struct Tags
93+
94+
Use the `query` tag to configure field behavior:
95+
96+
| Tag | Description |
97+
| ------------------------------------- | --------------------------------------------- |
98+
| `query:"fieldname"` | Maps search query to struct field |
99+
| `query:"fieldname,default"` | Marks field as default for plain text queries |
100+
| `query:"fieldname,format=2006-01-02"` | Custom time format for time.Time fields |
101+
102+
## Examples
103+
104+
### Basic Parsing
105+
106+
Search queries use the format `field:value` or `field:"quoted value"` for values with spaces.
107+
108+
```go
109+
type Filter struct {
110+
Title string `query:"title"`
111+
Author string `query:"author"`
112+
Category string `query:"category"`
113+
}
114+
115+
q := `title:hello author:world category:"science fiction"`
116+
117+
// Title : hello
118+
// Author : world
119+
// Category: science fiction
120+
```
121+
122+
### Fallback Parsing
123+
124+
When input doesn't contain search query syntax, it falls back to the field marked with `default`.
125+
126+
```go
127+
type Filter struct {
128+
Query string `query:"query,default"`
129+
Type string `query:"type"`
130+
}
131+
132+
q := `search for golang tutorials`
133+
134+
// Query: search for golang tutorials
135+
// Type :
136+
```
137+
138+
### Enhanced Fallback Parsing
139+
140+
Query fields take precedence, and remaining text or unknown field goes to default field.
141+
142+
```go
143+
type Filter struct {
144+
Keyword string `query:"keyword,default"`
145+
Category string `query:"category"`
146+
}
147+
148+
q := `hello world category:books foo bar emotion:happy`
149+
150+
// Keyword : hello world foo bar emotion:happy
151+
// Category: books
152+
```
153+
154+
### Numeric Parsing
155+
156+
The parser supports all Go numeric types.
157+
158+
```go
159+
type Filter struct {
160+
Price float64 `query:"price"`
161+
Discount float32 `query:"discount"`
162+
Quantity uint32 `query:"qty"`
163+
MinAge int8 `query:"min_age"`
164+
MaxSize uint64 `query:"max_size"`
165+
}
166+
167+
q := `price:10.10 discount:0.10 qty:100 min_age:10 max_size:100000`
168+
169+
// Price : 10.10
170+
// Discount: 0.10
171+
// Quantity: 100
172+
// MinAge : 10
173+
// MaxSize : 100000
174+
```
175+
176+
### Slice Parsing
177+
178+
The parser supports slices of all numeric types and strings with comma-separated (no space between) values.
179+
180+
```go
181+
type Filter struct {
182+
Tags []string `query:"tags"`
183+
Scores []int `query:"scores"`
184+
Weights []float64 `query:"weights"`
185+
}
186+
187+
q := `tags:foo,bar scores:10,20,30 weights:0.1,0.2,0.3`
188+
189+
// Tags : { "foo", "bar" }
190+
// Scores : { 10, 20, 30 }
191+
// Weights: { 0.1, 0.2, 0.3 }
192+
```
193+
194+
**Note:** For values containing spaces or commas, wrap the value in quotes:
195+
196+
```go
197+
q := `tags:"hello world,golang,web dev"`
198+
// Tags: { "hello world", "golang", "web dev" }
199+
```
200+
201+
### Time Parsing
202+
203+
The parser supports custom/multiple formats for a single field. The parser will try each format until one succeeds.
204+
205+
Default formats are used when no custom format is specified.
206+
207+
```go
208+
type Filter struct {
209+
StartedAt time.Time `query:"started_at"` // Default formats are 2006, 2006-01, 2006-01-02
210+
EndedAt time.Time `query:"ended_at,format=01/02/2006"` // Custom US format
211+
PublishDate time.Time `query:"publish_date,format=2006 2006-01 2006-01-02"` // Multiple formats (space-separated)
212+
}
213+
214+
q := `started_at:2025-12-30 ended_at:30/12/2025 publish_date:2025`
215+
216+
// StartedAt : time.Date(2025, 12, 30, 0, 0, 0, 0, time.UTC)
217+
// EndedAt : time.Date(2025, 12, 30, 0, 0, 0, 0, time.UTC)
218+
// PublishDate: time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
219+
```
220+
221+
### Boolean Parsing
222+
223+
The parser supports both regular `bool` and optional `*bool` (pointer) types.
224+
225+
```go
226+
type Filter struct {
227+
IsActive bool `query:"is_active"`
228+
IsRequired *bool `query:"is_required"` // nil if not specified
229+
}
230+
231+
q := `is_active:true`
232+
233+
// IsActive : true
234+
// IsRequired: nil
235+
```
236+
237+
The parser accepts the following values for boolean fields (case-insensitive):
238+
239+
| Parsed As | Value | Examples |
240+
| --------- | ------------------------------ | -------------------------- |
241+
| `true` | `true`, `1`, `yes`, `y`, `on` | `active:true`, `enabled:1` |
242+
| `false` | `false`, `0`, `no`, `n`, `off` | `active:false`, `hidden:0` |
243+
244+
**Note:** Any unrecognized boolean value will result in `false` and may trigger an error in strict mode.

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/teacat/qparser
2+
3+
go 1.23

0 commit comments

Comments
 (0)