Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions pgcql/pg_field_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ func maskedSplit(cqlTerm string, splitChars string) ([]string, error) {
if backslash {
return terms, fmt.Errorf("a CQL string must not end with a masking backslash")
}
terms = append(terms, string(pgTerm))
if len(pgTerm) > 0 || len(terms) == 0 {
terms = append(terms, string(pgTerm))
}
return terms, nil
}

Expand Down Expand Up @@ -148,13 +150,16 @@ func (f *FieldString) handleEmptyTerm(sc cql.SearchClause) string {
return ""
}

func (f *FieldString) generateFullText(sc cql.SearchClause, queryArgumentIndex int, pgfunc string) (string, []any, error) {
pgTerm, err := maskedExact(sc.Term)
func (f *FieldString) generateTsQuery(sc cql.SearchClause, termOp string, queryArgumentIndex int) (string, []any, error) {
pgTerms, err := maskedSplit(sc.Term, " ")
if err != nil {
return "", nil, err
}
sql := "to_tsvector('" + f.language + "', " + f.column + ") @@ " + pgfunc + "('" + f.language + "', " + fmt.Sprintf("$%d", queryArgumentIndex) + ")"
return sql, []any{pgTerm}, nil
for i, v := range pgTerms {
pgTerms[i] = "'" + strings.ReplaceAll(v, "'", "''") + "'"
}
sql := "to_tsvector('" + f.language + "', " + f.column + ") @@ to_tsquery('" + f.language + "', " + fmt.Sprintf("$%d", queryArgumentIndex) + ")"
return sql, []any{strings.Join(pgTerms, termOp)}, nil
}

func (f *FieldString) generateIn(sc cql.SearchClause, queryArgumentIndex int, not bool) (string, []any, error) {
Expand Down Expand Up @@ -188,9 +193,11 @@ func (f *FieldString) Generate(sc cql.SearchClause, queryArgumentIndex int) (str
if fulltext {
switch sc.Relation {
case cql.ADJ, cql.EQ:
return f.generateFullText(sc, queryArgumentIndex, "phraseto_tsquery")
return f.generateTsQuery(sc, "<->", queryArgumentIndex)
case cql.ALL:
return f.generateFullText(sc, queryArgumentIndex, "plainto_tsquery")
return f.generateTsQuery(sc, "&", queryArgumentIndex)
case cql.ANY:
return f.generateTsQuery(sc, "|", queryArgumentIndex)
}
}
if f.enableSplit {
Expand Down
12 changes: 7 additions & 5 deletions pgcql/pgcql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func TestParsing(t *testing.T) {
{"title exact 2", "Title = $1", []any{"2"}},
{"title<>2", "Title <> $1", []any{"2"}},
{"tag any \"1 23 45\"", "Tag IN($1, $2, $3)", []any{"1", "23", "45"}},
{"tag <> \"1 23 45\"", "Tag NOT IN($1, $2, $3)", []any{"1", "23", "45"}},
{"tag <> \" 1 23 45 \"", "Tag NOT IN($1, $2, $3)", []any{"1", "23", "45"}},
{"tag any \"*\"", "error: masking op * unsupported", nil},
{"a or b and c", "(T = $1 OR T = $2) AND T = $3", []any{"a", "b", "c"}},
{"title = abc", "Title = $1", []any{"abc"}},
Expand Down Expand Up @@ -88,11 +88,13 @@ func TestParsing(t *testing.T) {
{"title=\"a\\^\"", "Title = $1", []any{"a^"}},
{"title=\"a\\", "error: a CQL string must not end with a masking backslash", nil},
{"title=\"a\\x\"", "error: a masking backslash in a CQL string must be followed by *, ?, ^, \" or \\", nil},
{"full = \"abc\"", "to_tsvector('english', full) @@ phraseto_tsquery('english', $1)", []any{"abc"}},
{"full adj \"abc\"", "to_tsvector('english', full) @@ phraseto_tsquery('english', $1)", []any{"abc"}},
{"full all \"abc\"", "to_tsvector('english', full) @@ plainto_tsquery('english', $1)", []any{"abc"}},
{"full = abc", "to_tsvector('english', full) @@ to_tsquery('english', $1)", []any{"'abc'"}},
{"full = \"abc\"", "to_tsvector('english', full) @@ to_tsquery('english', $1)", []any{"'abc'"}},
{"full = \"abc \"", "to_tsvector('english', full) @@ to_tsquery('english', $1)", []any{"'abc'"}},
{"full adj \"a b\"", "to_tsvector('english', full) @@ to_tsquery('english', $1)", []any{"'a'<->'b'"}},
{"full all \"a b\"", "to_tsvector('english', full) @@ to_tsquery('english', $1)", []any{"'a'&'b'"}},
{"full any \"a b\"", "to_tsvector('english', full) @@ to_tsquery('english', $1)", []any{"'a'|'b'"}},
{"full=\"a*\"", "error: masking op * unsupported", nil},
{"full any x", "error: unsupported relation any", nil},
{"full > x", "error: unsupported relation >", nil},
{"price = 10", "price = $1", []any{10.0}},
{"price == 10", "price = $1", []any{10.0}},
Expand Down
3 changes: 3 additions & 0 deletions pgcql/pgx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ func TestPgx(t *testing.T) {
{"author adj \"d e knuth\"", []int{2}},
{"author adj \"e knuth\"", []int{1, 2}},
{"author adj \"e d knuth\"", []int{}},
{"author any \"e f\"", []int{1, 2}},
{"author adj \"e | f\"", []int{}},
{"city = \"Reading\"", []int{1}},
{"city = \"reading\"", []int{1}},
{"address = USA", []int{1, 2}},
Expand All @@ -192,6 +194,7 @@ func TestPgx(t *testing.T) {
{"address = \"unknown country\"", []int{3}},
{"address = \"country unknown\"", []int{}},
{"address adj \"unknown country\"", []int{3}},
{"address any \"unknown reading\"", []int{1, 3}},
} {
runQuery(t, parser, conn, ctx, def, testcase.query, testcase.expectedIds)
}
Expand Down