Skip to content

Commit

Permalink
Merge pull request #80 from ulule/feat/values
Browse files Browse the repository at this point in the history
feat: handle bidirectional operator
  • Loading branch information
thoas authored Jun 5, 2019
2 parents be8dac3 + dc0a656 commit 7ed4e94
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 0 deletions.
87 changes: 87 additions & 0 deletions builder/select_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,93 @@ import (
"github.com/ulule/loukoum/v3/stmt"
)

func TestSelect_Value(t *testing.T) {
RunBuilderTests(t, []BuilderTest{
{
Name: "ValueEqual",
Builder: loukoum.Select("id").
From("video").
Where(
loukoum.Value("fr").
Equal(loukoum.Raw("ANY(string_to_array(languages, ','))")),
),
String: "SELECT id FROM video WHERE ('fr' = ANY(string_to_array(languages, ',')))",
Query: "SELECT id FROM video WHERE ($1 = ANY(string_to_array(languages, ',')))",
NamedQuery: "SELECT id FROM video WHERE (:arg_1 = ANY(string_to_array(languages, ',')))",
Args: []interface{}{"fr"},
},
{
Name: "ValueNotEqual",
Builder: loukoum.Select("id").
From("video").
Where(
loukoum.Value("fr").
NotEqual(loukoum.Raw("ANY(string_to_array(languages, ','))")),
),
String: "SELECT id FROM video WHERE ('fr' != ANY(string_to_array(languages, ',')))",
Query: "SELECT id FROM video WHERE ($1 != ANY(string_to_array(languages, ',')))",
NamedQuery: "SELECT id FROM video WHERE (:arg_1 != ANY(string_to_array(languages, ',')))",
Args: []interface{}{"fr"},
},
{
Name: "ValueOverlap",
Builder: loukoum.Select("id").
From("video").
Where(
loukoum.Value("{fr}").
Overlap(loukoum.Raw("languages")),
),
String: "SELECT id FROM video WHERE ('{fr}' && languages)",
Query: "SELECT id FROM video WHERE ($1 && languages)",
NamedQuery: "SELECT id FROM video WHERE (:arg_1 && languages)",
Args: []interface{}{"{fr}"},
},
{
Name: "ValueIsContainedBy",
Builder: loukoum.Select("id").
From("video").
Where(
loukoum.Value("{fr}").
IsContainedBy(loukoum.Raw("languages")),
),
String: "SELECT id FROM video WHERE ('{fr}' <@ languages)",
Query: "SELECT id FROM video WHERE ($1 <@ languages)",
NamedQuery: "SELECT id FROM video WHERE (:arg_1 <@ languages)",
Args: []interface{}{"{fr}"},
},
{
Name: "IdentifierContains",
Builder: loukoum.Select("id").
From("video").
Where(loukoum.Condition("languages").Contains("{fr}")),
String: "SELECT id FROM video WHERE (languages @> '{fr}')",
Query: "SELECT id FROM video WHERE (languages @> $1)",
NamedQuery: "SELECT id FROM video WHERE (languages @> :arg_1)",
Args: []interface{}{"{fr}"},
},
{
Name: "IdentifierIsContainsBy",
Builder: loukoum.Select("id").
From("video").
Where(loukoum.Condition("languages").IsContainedBy("{fr}")),
String: "SELECT id FROM video WHERE (languages <@ '{fr}')",
Query: "SELECT id FROM video WHERE (languages <@ $1)",
NamedQuery: "SELECT id FROM video WHERE (languages <@ :arg_1)",
Args: []interface{}{"{fr}"},
},
{
Name: "IdentifierOverlap",
Builder: loukoum.Select("id").
From("video").
Where(loukoum.Condition("languages").Overlap("{fr}")),
String: "SELECT id FROM video WHERE (languages && '{fr}')",
Query: "SELECT id FROM video WHERE (languages && $1)",
NamedQuery: "SELECT id FROM video WHERE (languages && :arg_1)",
Args: []interface{}{"{fr}"},
},
})
}

func TestSelect_Columns(t *testing.T) {
RunBuilderTests(t, []BuilderTest{
{
Expand Down
5 changes: 5 additions & 0 deletions loukoum.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ func Pair(key, value interface{}) types.Pair {
return types.Pair{Key: key, Value: value}
}

// Value is a wrapper to create a new Value expression.
func Value(value interface{}) stmt.Value {
return stmt.NewValue(value)
}

// Select starts a SelectBuilder using the given columns.
func Select(columns ...interface{}) builder.Select {
return builder.NewSelect().Columns(columns...)
Expand Down
48 changes: 48 additions & 0 deletions stmt/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,24 @@ func (identifier Identifier) IsEmpty() bool {
return identifier.Identifier == ""
}

// Contains performs a "contains" comparison.
func (identifier Identifier) Contains(value interface{}) InfixExpression {
operator := NewComparisonOperator(types.Contains)
return NewInfixExpression(identifier, operator, NewWrapper(NewExpression(value)))
}

// IsContainedBy performs a "is contained by" comparison.
func (identifier Identifier) IsContainedBy(value interface{}) InfixExpression {
operator := NewComparisonOperator(types.IsContainedBy)
return NewInfixExpression(identifier, operator, NewWrapper(NewExpression(value)))
}

// Overlap performs an "overlap" comparison.
func (identifier Identifier) Overlap(value interface{}) InfixExpression {
operator := NewComparisonOperator(types.Overlap)
return NewInfixExpression(identifier, operator, NewWrapper(NewExpression(value)))
}

// Equal performs an "equal" comparison.
func (identifier Identifier) Equal(value interface{}) InfixExpression {
operator := NewComparisonOperator(types.Equal)
Expand Down Expand Up @@ -209,6 +227,36 @@ func (value Value) Write(ctx types.Context) {
}
}

// Overlap performs an "overlap" comparison.
func (value Value) Overlap(what interface{}) InfixExpression {
operator := NewComparisonOperator(types.Overlap)
return NewInfixExpression(value, operator, NewWrapper(NewExpression(what)))
}

// Equal performs an "equal" comparison.
func (value Value) Equal(what interface{}) InfixExpression {
operator := NewComparisonOperator(types.Equal)
return NewInfixExpression(value, operator, NewWrapper(NewExpression(what)))
}

// NotEqual performs a "not equal" comparison.
func (value Value) NotEqual(what interface{}) InfixExpression {
operator := NewComparisonOperator(types.NotEqual)
return NewInfixExpression(value, operator, NewWrapper(NewExpression(what)))
}

// Contains performs a "contains" comparison.
func (value Value) Contains(what interface{}) InfixExpression {
operator := NewComparisonOperator(types.Contains)
return NewInfixExpression(value, operator, NewWrapper(NewExpression(what)))
}

// IsContainedBy performs a "is contained by" comparison.
func (value Value) IsContainedBy(what interface{}) InfixExpression {
operator := NewComparisonOperator(types.IsContainedBy)
return NewInfixExpression(value, operator, NewWrapper(NewExpression(what)))
}

// IsEmpty returns true if statement is undefined.
func (value Value) IsEmpty() bool {
return false
Expand Down
3 changes: 3 additions & 0 deletions types/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,7 @@ const (
NotILike = ComparisonOperator("NOT ILIKE")
Between = ComparisonOperator("BETWEEN")
NotBetween = ComparisonOperator("NOT BETWEEN")
Contains = ComparisonOperator("@>")
IsContainedBy = ComparisonOperator("<@")
Overlap = ComparisonOperator("&&")
)

0 comments on commit 7ed4e94

Please sign in to comment.