Skip to content

Commit 454b20a

Browse files
committed
refactor: simplify Lucene parser API and architecture
- NewParser(model) replaces NewParserFromType(model) - Implicit search restricted to string fields only - Removed complex tag configuration - Split driver.go into postgres_driver.go and dynamodb_driver.go - NewPostgresDriver() and NewDynamoDBDriver() constructors - Checks for JSONB/JSON in type name, maps, and structs - Parse-time validation (HTTP 400) not runtime (HTTP 500)
1 parent 13b5547 commit 454b20a

8 files changed

Lines changed: 414 additions & 546 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ require (
1212
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.53.5
1313
github.com/aws/aws-sdk-go-v2/service/sns v1.39.10
1414
github.com/gocql/gocql v1.7.0
15+
github.com/grindlemire/go-lucene v0.0.26
1516
github.com/scylladb/go-reflectx v1.0.1
1617
github.com/scylladb/gocqlx/v3 v3.0.4
1718
gopkg.in/yaml.v3 v3.0.1
@@ -41,7 +42,6 @@ require (
4142
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
4243
github.com/go-sql-driver/mysql v1.8.1 // indirect
4344
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
44-
github.com/grindlemire/go-lucene v0.0.26 // indirect
4545
github.com/jackc/pgpassfile v1.0.0 // indirect
4646
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
4747
github.com/jackc/pgx/v5 v5.6.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
125125
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
126126
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
127127
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
128+
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
129+
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
128130
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
129131
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
130132
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=

storage/dynamodb.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ func (s *DynamoDBAdapter) Search(dest any, sortKey string, query string, limit i
239239
// Parse Lucene query
240240
destType := reflect.TypeOf(dest).Elem().Elem()
241241
model := reflect.New(destType).Elem().Interface()
242-
parser, _ := lucene.NewParserFromType(model)
242+
parser, _ := lucene.NewParser(model)
243243
whereClause, params, _ := parser.ParseToDynamoDBPartiQL(query)
244244

245245
// Build query
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package lucene
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
8+
"github.com/grindlemire/go-lucene/pkg/driver"
9+
"github.com/grindlemire/go-lucene/pkg/lucene/expr"
10+
)
11+
12+
// DynamoDBPartiQLDriver converts Lucene queries to DynamoDB PartiQL.
13+
type DynamoDBPartiQLDriver struct {
14+
driver.Base
15+
fields map[string]FieldInfo
16+
}
17+
18+
func NewDynamoDBDriver(fields []FieldInfo) *DynamoDBPartiQLDriver {
19+
fieldMap := make(map[string]FieldInfo)
20+
for _, f := range fields {
21+
fieldMap[f.Name] = f
22+
}
23+
24+
fns := map[expr.Operator]driver.RenderFN{
25+
expr.Literal: driver.Shared[expr.Literal],
26+
expr.And: driver.Shared[expr.And],
27+
expr.Or: driver.Shared[expr.Or],
28+
expr.Not: driver.Shared[expr.Not],
29+
expr.Equals: driver.Shared[expr.Equals],
30+
expr.Range: driver.Shared[expr.Range],
31+
expr.Must: driver.Shared[expr.Must],
32+
expr.MustNot: driver.Shared[expr.MustNot],
33+
expr.Wild: driver.Shared[expr.Wild],
34+
expr.Regexp: driver.Shared[expr.Regexp],
35+
expr.Like: dynamoDBLike, // Custom LIKE for DynamoDB functions
36+
expr.Greater: driver.Shared[expr.Greater],
37+
expr.GreaterEq: driver.Shared[expr.GreaterEq],
38+
expr.Less: driver.Shared[expr.Less],
39+
expr.LessEq: driver.Shared[expr.LessEq],
40+
expr.In: driver.Shared[expr.In],
41+
expr.List: driver.Shared[expr.List],
42+
}
43+
44+
return &DynamoDBPartiQLDriver{
45+
Base: driver.Base{
46+
RenderFNs: fns,
47+
},
48+
fields: fieldMap,
49+
}
50+
}
51+
52+
// RenderPartiQL renders the expression to DynamoDB PartiQL with AttributeValue parameters.
53+
func (d *DynamoDBPartiQLDriver) RenderPartiQL(e *expr.Expression) (string, []types.AttributeValue, error) {
54+
// Use base rendering with ? placeholders
55+
str, params, err := d.RenderParam(e)
56+
if err != nil {
57+
return "", nil, err
58+
}
59+
60+
// Convert params to DynamoDB AttributeValues
61+
attrValues := make([]types.AttributeValue, len(params))
62+
for i, param := range params {
63+
attrValues[i] = &types.AttributeValueMemberS{Value: fmt.Sprintf("%v", param)}
64+
}
65+
66+
return str, attrValues, nil
67+
}
68+
69+
// dynamoDBLike implements LIKE using DynamoDB's begins_with and contains functions.
70+
func dynamoDBLike(left, right string) (string, error) {
71+
// Remove quotes from right side to analyze pattern
72+
pattern := strings.Trim(right, "'")
73+
74+
// Replace wildcards for analysis
75+
hasPrefix := strings.HasPrefix(pattern, "%")
76+
hasSuffix := strings.HasSuffix(pattern, "%")
77+
78+
if hasPrefix && hasSuffix {
79+
// %value% -> contains(field, value)
80+
value := strings.Trim(pattern, "%")
81+
return fmt.Sprintf("contains(%s, '%s')", left, value), nil
82+
} else if !hasPrefix && hasSuffix {
83+
// value% -> begins_with(field, value)
84+
value := strings.TrimSuffix(pattern, "%")
85+
return fmt.Sprintf("begins_with(%s, '%s')", left, value), nil
86+
} else if hasPrefix && !hasSuffix {
87+
// %value -> contains(field, value) (DynamoDB doesn't have ends_with)
88+
value := strings.TrimPrefix(pattern, "%")
89+
return fmt.Sprintf("contains(%s, '%s')", left, value), nil
90+
}
91+
92+
// Exact match
93+
return fmt.Sprintf("%s = %s", left, right), nil
94+
}

0 commit comments

Comments
 (0)