Skip to content

Commit 2a4706d

Browse files
committed
feat: implement filters on collection level via query params
1 parent 7c0eeba commit 2a4706d

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed

docs/ContentModel.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,55 @@ Returns:
141141
}
142142
```
143143

144+
### Filtering Collections based on Metadata
145+
Since metadata gets exposed on collection level, you can use http query params to filter the collection results.
146+
147+
#### Example
148+
149+
**first.md**
150+
151+
```yaml
144152

153+
author: leo
154+
title: This is the first blog post
155+
state: released
156+
157+
# This is my stuff.
158+
```
159+
160+
**second.md**
161+
162+
```yaml
163+
164+
author: maik
165+
title: My deep-dive into monitors
166+
state: not-released
167+
168+
# This is my stuff.
169+
```
170+
171+
If you go to `/api/blogs?author=maik`, you shall receive:
172+
```json
173+
{
174+
"collection": "blogs",
175+
"items": [
176+
{
177+
"slug": "blogs/second",
178+
"meta": {
179+
"author": "maik",
180+
"contentType": "application/json",
181+
"kind": "markdown",
182+
"size": 108,
183+
"state": "not-released",
184+
"title": "My deep-dive into monitors"
185+
}
186+
}
187+
]
188+
}
189+
```
190+
191+
> [!NOTE]
192+
> If no matching entries are found, the items array will just be empty.
145193
146194
### What Are Collections Used For?
147195

internal/handlers/content.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"strings"
66

77
"github.com/cheetahbyte/centra/internal/cache"
8+
"github.com/cheetahbyte/centra/internal/helper"
89
"github.com/go-chi/chi/v5"
910
)
1011

@@ -31,12 +32,20 @@ func HandleContent(w http.ResponseWriter, r *http.Request) {
3132
if !node.IsLeaf() {
3233
items := node.GetChildren()
3334
collectionItems := make([]CollectionItem, 0, len(items))
35+
q := helper.ParseQueryParams(r)
3436
for p, child := range items {
37+
meta := child.GetMetadata()
38+
// this is probably not ideal but it works for now.
39+
if !helper.MatchesQuery(meta, q) {
40+
continue
41+
}
42+
3543
collectionItems = append(collectionItems, CollectionItem{
3644
Slug: path + "/" + p,
3745
Meta: child.GetMetadata(),
3846
})
3947
}
48+
4049
writeJSON(w, http.StatusOK, map[string]any{
4150
"collection": path,
4251
"items": collectionItems,

internal/helper/query.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package helper
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"slices"
7+
"strings"
8+
)
9+
10+
func ParseQueryParams(r *http.Request) map[string][]string {
11+
q := r.URL.Query()
12+
result := make(map[string][]string)
13+
14+
for key, values := range q {
15+
for _, v := range values {
16+
split := strings.Split(v, ",")
17+
for _, s := range split {
18+
result[key] = append(result[key], strings.TrimSpace(s))
19+
}
20+
}
21+
}
22+
23+
return result
24+
}
25+
26+
func matchesValue(metaValue any, allowed []string) bool {
27+
switch v := metaValue.(type) {
28+
case string:
29+
ms := strings.ToLower(v)
30+
for _, a := range allowed {
31+
if strings.Contains(ms, strings.ToLower(a)) {
32+
return true
33+
}
34+
}
35+
return false
36+
37+
case []string:
38+
return slices.ContainsFunc(v, func(tag string) bool {
39+
ts := strings.ToLower(tag)
40+
for _, a := range allowed {
41+
if strings.Contains(ts, strings.ToLower(a)) {
42+
return true
43+
}
44+
}
45+
return false
46+
})
47+
48+
case bool, int, int64, float64:
49+
s := fmt.Sprintf("%v", v)
50+
ss := strings.ToLower(s)
51+
for _, a := range allowed {
52+
if strings.Contains(ss, strings.ToLower(a)) {
53+
return true
54+
}
55+
}
56+
return false
57+
58+
default:
59+
s := fmt.Sprint(v)
60+
ss := strings.ToLower(s)
61+
for _, a := range allowed {
62+
if strings.Contains(ss, strings.ToLower(a)) {
63+
return true
64+
}
65+
}
66+
return false
67+
}
68+
}
69+
70+
func MatchesQuery(meta map[string]any, query map[string][]string) bool {
71+
for key, allowed := range query {
72+
val, ok := meta[key]
73+
if !ok || !matchesValue(val, allowed) {
74+
return false
75+
}
76+
}
77+
return true
78+
}

0 commit comments

Comments
 (0)