Skip to content

Commit

Permalink
Adds crude filters
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewpeterkort committed Dec 31, 2024
1 parent f13814d commit 0b4615f
Show file tree
Hide file tree
Showing 8 changed files with 2,039 additions and 1,858 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/graphql-go/handler v0.2.4
github.com/mongodb/mongo-tools v0.0.0-20240715143021-aa6a140d3f17
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/vektah/gqlparser/v2 v2.5.20
go.mongodb.org/mongo-driver v1.11.9
Expand Down Expand Up @@ -72,7 +73,6 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sosodev/duration v1.3.1 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
github.com/urfave/cli/v2 v2.27.5 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
Expand Down
49 changes: 49 additions & 0 deletions gql-gen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# GQL Gen server:

A graphql query server plugin for querying GRIP

## Setup

```
go build --buildmode=plugin ./gql-gen
grip server -w graphql=gql-gen.so
```

Open http://localhost:8201/graphql/

## Example FHIR query:

```
query($filter: JSON){
specimen(filter: $filter first:10){
id
subject{
... on PatientType{
identifier{
system
value
}
}
}
processing{
method{
coding{
code
display
system
}
}
}
}
}
{
"filter": {
"=": {
"id":
"example-uuid"
}
}
}
```
3,642 changes: 1,821 additions & 1,821 deletions gql-gen/generated/exec.go

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions gql-gen/gqlgen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ resolver:
layout: single-file

omit_slice_element_pointers: true

models:
JSON:
model: github.com/99designs/gqlgen/graphql.Map
12 changes: 10 additions & 2 deletions gql-gen/graph/collectFields.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ func queryBuild(query **gripql.Query, selSet ast.SelectionSet, curElement string

func (r *queryResolver) GetSelectedFieldsAst(ctx context.Context, sourceType string) ([]any, error) {
resctx := graphql.GetFieldContext(ctx)
pesctx := graphql.GetOperationContext(ctx)
fmt.Println("VARIABLES: ", pesctx.Variables)

rt := &renderTree{
rFieldPaths: map[string][]string{"f0": []string{}},
rTree: map[string]any{},
Expand All @@ -100,8 +103,13 @@ func (r *queryResolver) GetSelectedFieldsAst(ctx context.Context, sourceType str
}
}

// Traverse back to f0 since only filters on the root node are applied currently
q = q.Select("f0")
fmt.Printf("ARGS: %#v\n", resctx.Args)
applyFilters(&q, resctx.Args)

//fmt.Printf("RENDER: %#v\n", render)
q = q.Limit(10).Render(render)
q = q.Render(render)
fmt.Println("QUERY AFTER: ", q)

result, err := r.GripDb.Traversal(context.Background(), &gripql.GraphQuery{Graph: "CALIPER", Query: q.Statements})
Expand All @@ -114,7 +122,7 @@ func (r *queryResolver) GetSelectedFieldsAst(ctx context.Context, sourceType str
values := r.GetRender().GetStructValue().AsMap()
//fmt.Printf("VALUES: %#v\n", values)
data := buildOutputTree(rt.rTree, values)
fmt.Printf("DATA: %#v\n", data)
//fmt.Printf("DATA: %#v\n", data)
out = append(out, data)
}
return out, nil
Expand Down
110 changes: 110 additions & 0 deletions gql-gen/graph/filters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package graph

import (
"fmt"
"strings"

"github.com/bmeg/grip/gripql"
)

func applyFilters(query **gripql.Query, args map[string]any) error {
//Todo: support "accessiblity", "format", "sort":
if filter, ok := args["filter"]; ok {
chainedFilter, err := applyJsonFilter(filter.(map[string]any))
if err != nil {
return err
}
fmt.Printf("CHAINED FILTER: %s\n", chainedFilter.String())
*query = (*query).Has(chainedFilter)
}
if first, ok := args["first"]; ok {
if first.(*int) == nil {
*query = (*query).Limit(uint32(10))
} else {
*query = (*query).Limit(uint32(*first.(*int)))
}
}
if offset, ok := args["offset"]; ok {
if offset.(*int) != nil {
*query = (*query).Skip(uint32(*offset.(*int)))
}
}
return nil
}

func applyJsonFilter(filter map[string]any) (*gripql.HasExpression, error) {
topLevelOp := ""
for key := range filter {
topLevelOp = key
break
}
topLevelOpLowerCase := strings.ToLower(topLevelOp)

switch topLevelOpLowerCase {
case "and", "or":
var expressions []*gripql.HasExpression
for _, item := range filter[topLevelOp].([]any) {
itemObj, ok := item.(map[string]any)
if !ok {
return nil, fmt.Errorf("invalid nested filter structure")
}
subExpr, err := applyJsonFilter(itemObj)
if err != nil {
return nil, err
}
expressions = append(expressions, subExpr)
}

if len(expressions) == 1 {
return expressions[0], nil
} else if len(expressions) > 1 {
if topLevelOpLowerCase == "and" {
return gripql.And(expressions...), nil
} else {
return gripql.Or(expressions...), nil
}
} else {
return nil, fmt.Errorf("no valid expressions for logical operator: %s", topLevelOp)
}

default:
field := ""
topFilter, ok := filter[topLevelOp].(map[string]any)
if !ok {
return nil, fmt.Errorf("Top level filter %s not of type map[string]any", filter[topLevelOp])
}

for key := range topFilter {
field = key
break
}

hasExpr, err := mapGraphQLOperatorToGrip(field, topFilter[field], topLevelOp)
if err != nil {
return nil, err
}

return hasExpr, nil
}
}

func mapGraphQLOperatorToGrip(field string, value any, op string) (*gripql.HasExpression, error) {
switch strings.ToLower(op) {
case "eq", "=":
return gripql.Eq(field, value), nil
case "neq", "!=":
return gripql.Neq(field, value), nil
case "lt", "<":
return gripql.Lt(field, value), nil
case "gt", ">":
return gripql.Gt(field, value), nil
case "gte", ">=":
return gripql.Gte(field, value), nil
case "lte", "<=":
return gripql.Lte(field, value), nil
case "in":
return gripql.Within(field, value), nil
default:
return nil, fmt.Errorf("Operator %s does not match any of the known operators\n", op)
}
}
20 changes: 20 additions & 0 deletions gql-gen/graph/jsonfilter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package graph

import (
"encoding/json"
"fmt"
)

// Custom JSON type handler for json filter query arguments
type JSON map[string]interface{}

func (j *JSON) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, j); err != nil {
return fmt.Errorf("failed to unmarshal JSON: %w", err)
}
return nil
}

func (j JSON) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}(j))
}
Loading

0 comments on commit 0b4615f

Please sign in to comment.