Replace SQLite with BadgerDB

This commit is contained in:
David Stotijn
2022-01-21 11:45:54 +01:00
parent 8a3b3cbf02
commit d84d2d0905
49 changed files with 2496 additions and 2677 deletions

View File

@ -1,6 +1,10 @@
package search
import "strings"
import (
"encoding/gob"
"regexp"
"strings"
)
type Expression interface {
String() string
@ -11,7 +15,7 @@ type PrefixExpression struct {
Right Expression
}
func (pe *PrefixExpression) String() string {
func (pe PrefixExpression) String() string {
b := strings.Builder{}
b.WriteString("(")
b.WriteString(pe.Operator.String())
@ -28,7 +32,7 @@ type InfixExpression struct {
Right Expression
}
func (ie *InfixExpression) String() string {
func (ie InfixExpression) String() string {
b := strings.Builder{}
b.WriteString("(")
b.WriteString(ie.Left.String())
@ -45,6 +49,32 @@ type StringLiteral struct {
Value string
}
func (sl *StringLiteral) String() string {
func (sl StringLiteral) String() string {
return sl.Value
}
type RegexpLiteral struct {
*regexp.Regexp
}
func (rl RegexpLiteral) MarshalBinary() ([]byte, error) {
return []byte(rl.Regexp.String()), nil
}
func (rl *RegexpLiteral) UnmarshalBinary(data []byte) error {
re, err := regexp.Compile(string(data))
if err != nil {
return err
}
*rl = RegexpLiteral{re}
return nil
}
func init() {
gob.Register(PrefixExpression{})
gob.Register(InfixExpression{})
gob.Register(StringLiteral{})
gob.Register(RegexpLiteral{})
}

View File

@ -2,6 +2,7 @@ package search
import (
"fmt"
"regexp"
)
type precedence int
@ -99,7 +100,7 @@ func ParseQuery(input string) (expr Expression, err error) {
case expr == nil:
expr = right
default:
expr = &InfixExpression{
expr = InfixExpression{
Operator: TokOpAnd,
Left: expr,
Right: right,
@ -170,7 +171,7 @@ func (p *Parser) parseExpression(prec precedence) (Expression, error) {
}
func parsePrefixExpression(p *Parser) (Expression, error) {
expr := &PrefixExpression{
expr := PrefixExpression{
Operator: p.cur.Type,
}
@ -187,7 +188,7 @@ func parsePrefixExpression(p *Parser) (Expression, error) {
}
func parseInfixExpression(p *Parser, left Expression) (Expression, error) {
expr := &InfixExpression{
expr := InfixExpression{
Operator: p.cur.Type,
Left: left,
}
@ -200,13 +201,24 @@ func parseInfixExpression(p *Parser, left Expression) (Expression, error) {
return nil, fmt.Errorf("could not parse expression for right operand: %w", err)
}
if expr.Operator == TokOpRe || expr.Operator == TokOpNotRe {
if rightStr, ok := right.(StringLiteral); ok {
re, err := regexp.Compile(rightStr.Value)
if err != nil {
return nil, fmt.Errorf("could not compile regular expression %q: %w", rightStr.Value, err)
}
right = re
}
}
expr.Right = right
return expr, nil
}
func parseStringLiteral(p *Parser) (Expression, error) {
return &StringLiteral{Value: p.cur.Literal}, nil
return StringLiteral{Value: p.cur.Literal}, nil
}
func parseGroupedExpression(p *Parser) (Expression, error) {
@ -227,7 +239,7 @@ func parseGroupedExpression(p *Parser) (Expression, error) {
return nil, fmt.Errorf("could not parse expression: %w", err)
}
expr = &InfixExpression{
expr = InfixExpression{
Operator: TokOpAnd,
Left: expr,
Right: right,

View File

@ -3,6 +3,7 @@ package search
import (
"errors"
"reflect"
"regexp"
"testing"
)
@ -24,101 +25,101 @@ func TestParseQuery(t *testing.T) {
{
name: "string literal expression",
input: "foobar",
expectedExpression: &StringLiteral{Value: "foobar"},
expectedExpression: StringLiteral{Value: "foobar"},
expectedError: nil,
},
{
name: "boolean expression with equal operator",
input: "foo = bar",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpEq,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "boolean expression with not equal operator",
input: "foo != bar",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpNotEq,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "boolean expression with greater than operator",
input: "foo > bar",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpGt,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "boolean expression with less than operator",
input: "foo < bar",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpLt,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "boolean expression with greater than or equal operator",
input: "foo >= bar",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpGtEq,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "boolean expression with less than or equal operator",
input: "foo <= bar",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpLtEq,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "boolean expression with regular expression operator",
input: "foo =~ bar",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpRe,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: regexp.MustCompile("bar"),
},
expectedError: nil,
},
{
name: "boolean expression with not regular expression operator",
input: "foo !~ bar",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpNotRe,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: regexp.MustCompile("bar"),
},
expectedError: nil,
},
{
name: "boolean expression with AND, OR and NOT operators",
input: "foo AND bar OR NOT baz",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpAnd,
Left: &StringLiteral{Value: "foo"},
Right: &InfixExpression{
Left: StringLiteral{Value: "foo"},
Right: InfixExpression{
Operator: TokOpOr,
Left: &StringLiteral{Value: "bar"},
Right: &PrefixExpression{
Left: StringLiteral{Value: "bar"},
Right: PrefixExpression{
Operator: TokOpNot,
Right: &StringLiteral{Value: "baz"},
Right: StringLiteral{Value: "baz"},
},
},
},
@ -127,16 +128,16 @@ func TestParseQuery(t *testing.T) {
{
name: "boolean expression with nested group",
input: "(foo AND bar) OR NOT baz",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpOr,
Left: &InfixExpression{
Left: InfixExpression{
Operator: TokOpAnd,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
Right: &PrefixExpression{
Right: PrefixExpression{
Operator: TokOpNot,
Right: &StringLiteral{Value: "baz"},
Right: StringLiteral{Value: "baz"},
},
},
expectedError: nil,
@ -144,59 +145,59 @@ func TestParseQuery(t *testing.T) {
{
name: "implicit boolean expression with string literal operands",
input: "foo bar baz",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpAnd,
Left: &InfixExpression{
Left: InfixExpression{
Operator: TokOpAnd,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
Right: &StringLiteral{Value: "baz"},
Right: StringLiteral{Value: "baz"},
},
expectedError: nil,
},
{
name: "implicit boolean expression nested in group",
input: "(foo bar)",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpAnd,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "implicit and explicit boolean expression with string literal operands",
input: "foo bar OR baz yolo",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpAnd,
Left: &InfixExpression{
Left: InfixExpression{
Operator: TokOpAnd,
Left: &StringLiteral{Value: "foo"},
Right: &InfixExpression{
Left: StringLiteral{Value: "foo"},
Right: InfixExpression{
Operator: TokOpOr,
Left: &StringLiteral{Value: "bar"},
Right: &StringLiteral{Value: "baz"},
Left: StringLiteral{Value: "bar"},
Right: StringLiteral{Value: "baz"},
},
},
Right: &StringLiteral{Value: "yolo"},
Right: StringLiteral{Value: "yolo"},
},
expectedError: nil,
},
{
name: "implicit boolean expression with comparison operands",
input: "foo=bar baz=~yolo",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpAnd,
Left: &InfixExpression{
Left: InfixExpression{
Operator: TokOpEq,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
Right: &InfixExpression{
Right: InfixExpression{
Operator: TokOpRe,
Left: &StringLiteral{Value: "baz"},
Right: &StringLiteral{Value: "yolo"},
Left: StringLiteral{Value: "baz"},
Right: regexp.MustCompile("yolo"),
},
},
expectedError: nil,
@ -204,17 +205,17 @@ func TestParseQuery(t *testing.T) {
{
name: "eq operator takes precedence over boolean ops",
input: "foo=bar OR baz=yolo",
expectedExpression: &InfixExpression{
expectedExpression: InfixExpression{
Operator: TokOpOr,
Left: &InfixExpression{
Left: InfixExpression{
Operator: TokOpEq,
Left: &StringLiteral{Value: "foo"},
Right: &StringLiteral{Value: "bar"},
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
Right: &InfixExpression{
Right: InfixExpression{
Operator: TokOpEq,
Left: &StringLiteral{Value: "baz"},
Right: &StringLiteral{Value: "yolo"},
Left: StringLiteral{Value: "baz"},
Right: StringLiteral{Value: "yolo"},
},
},
expectedError: nil,