Skip to content

Commit

Permalink
Improvement/mandatory first condition (#52)
Browse files Browse the repository at this point in the history
* first condition mandatory for and, or, not and xor

* fix lint
  • Loading branch information
FrancoLiberali authored Jan 8, 2024
1 parent c91e24a commit b6d561e
Show file tree
Hide file tree
Showing 13 changed files with 71 additions and 88 deletions.
48 changes: 27 additions & 21 deletions condition/collection.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package condition

import (
"github.com/elliotchance/pie/v2"

"github.com/FrancoLiberali/cql/model"
)

Expand All @@ -18,33 +20,37 @@ func (collection Collection[TObject, TAttribute]) Preload(nestedPreloads ...Join
}

// Any generates a condition that is true if at least one model in the collection fulfills the conditions
func (collection Collection[TObject, TAttribute]) Any(conditions ...WhereCondition[TAttribute]) WhereCondition[TObject] {
return existsCondition[TObject, TAttribute]{
Conditions: conditions,
RelationField: collection.name,
T1Field: collection.t1Field,
T2Field: collection.t2Field,
}
func (collection Collection[TObject, TAttribute]) Any(
firstCondition WhereCondition[TAttribute],
conditions ...WhereCondition[TAttribute],
) WhereCondition[TObject] {
return newExistsCondition[TObject, TAttribute](firstCondition, conditions, collection.name, collection.t1Field, collection.t2Field)
}

// None generates a condition that is true if no model in the collection fulfills the conditions
func (collection Collection[TObject, TAttribute]) None(conditions ...WhereCondition[TAttribute]) WhereCondition[TObject] {
return Not[TObject](existsCondition[TObject, TAttribute]{
Conditions: conditions,
RelationField: collection.name,
T1Field: collection.t1Field,
T2Field: collection.t2Field,
})
func (collection Collection[TObject, TAttribute]) None(
firstCondition WhereCondition[TAttribute],
conditions ...WhereCondition[TAttribute],
) WhereCondition[TObject] {
return Not[TObject](
newExistsCondition[TObject, TAttribute](firstCondition, conditions, collection.name, collection.t1Field, collection.t2Field),
)
}

// All generates a condition that is true if all models in the collection fulfill the conditions (or is empty)
func (collection Collection[TObject, TAttribute]) All(conditions ...WhereCondition[TAttribute]) WhereCondition[TObject] {
return Not[TObject](existsCondition[TObject, TAttribute]{
Conditions: []WhereCondition[TAttribute]{Not[TAttribute](conditions...)},
RelationField: collection.name,
T1Field: collection.t1Field,
T2Field: collection.t2Field,
})
func (collection Collection[TObject, TAttribute]) All(
firstCondition WhereCondition[TAttribute],
conditions ...WhereCondition[TAttribute],
) WhereCondition[TObject] {
return Not[TObject](
newExistsCondition[TObject, TAttribute](
Not[TAttribute](
pie.Unshift(conditions, firstCondition)...,
),
[]WhereCondition[TAttribute]{},
collection.name, collection.t1Field, collection.t2Field,
),
)
}

func NewCollection[TObject model.Model, TAttribute model.Model](name, t1Field, t2Field string) Collection[TObject, TAttribute] {
Expand Down
2 changes: 1 addition & 1 deletion condition/connection_condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (condition connectionCondition[T]) affectsDeletedAt() bool {

// Condition that connects multiple conditions.
// Example: condition1 AND condition2
func NewConnectionCondition[T model.Model](connector sql.Operator, conditions ...WhereCondition[T]) WhereCondition[T] {
func NewConnectionCondition[T model.Model](connector sql.Operator, conditions []WhereCondition[T]) WhereCondition[T] {
return connectionCondition[T]{
Connector: connector,
Conditions: conditions,
Expand Down
2 changes: 1 addition & 1 deletion condition/container_condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (condition containerCondition[T]) affectsDeletedAt() bool {

// Condition that contains a internal condition.
// Example: NOT (internal condition)
func NewContainerCondition[T model.Model](prefix sql.Operator, conditions ...WhereCondition[T]) WhereCondition[T] {
func NewContainerCondition[T model.Model](prefix sql.Operator, conditions []WhereCondition[T]) WhereCondition[T] {
if len(conditions) == 0 {
return newInvalidCondition[T](emptyConditionsError[T](prefix))
}
Expand Down
2 changes: 1 addition & 1 deletion condition/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (deleteS *Delete[T]) Exec() (int64, error) {
}

// Create a Delete to which the conditions are applied inside transaction tx
func NewDelete[T model.Model](tx *gorm.DB, conditions ...Condition[T]) *Delete[T] {
func NewDelete[T model.Model](tx *gorm.DB, conditions []Condition[T]) *Delete[T] {
var err error

if len(conditions) == 0 {
Expand Down
15 changes: 15 additions & 0 deletions condition/exists_condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package condition
import (
"fmt"

"github.com/elliotchance/pie/v2"

"github.com/FrancoLiberali/cql/model"
)

Expand All @@ -14,6 +16,19 @@ type existsCondition[T1 model.Model, T2 model.Model] struct {
T2Field string
}

func newExistsCondition[T1 model.Model, T2 model.Model](
firstCondition WhereCondition[T2],
conditions []WhereCondition[T2],
relationField, t1Field, t2Field string,
) existsCondition[T1, T2] {
return existsCondition[T1, T2]{
Conditions: pie.Unshift(conditions, firstCondition),
RelationField: relationField,
T1Field: t1Field,
T2Field: t2Field,
}
}

//nolint:unused // is used
func (condition existsCondition[T1, T2]) interfaceVerificationMethod(_ T1) {
// This method is necessary to get the compiler to verify
Expand Down
8 changes: 6 additions & 2 deletions condition/logical.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ import (
)

func And[T model.Model](conditions ...WhereCondition[T]) WhereCondition[T] {
return NewConnectionCondition(sql.And, conditions...)
return NewConnectionCondition(sql.And, conditions)
}

func Or[T model.Model](conditions ...WhereCondition[T]) WhereCondition[T] {
return NewConnectionCondition(sql.Or, conditions)
}

func Not[T model.Model](conditions ...WhereCondition[T]) WhereCondition[T] {
return NewContainerCondition(sql.Not, conditions...)
return NewContainerCondition(sql.Not, conditions)
}
2 changes: 1 addition & 1 deletion condition/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (update *Update[T]) Returning(dest *[]T) *Update[T] {
}

// Create a Update to which the conditions are applied inside transaction tx
func NewUpdate[T model.Model](tx *gorm.DB, conditions ...Condition[T]) *Update[T] {
func NewUpdate[T model.Model](tx *gorm.DB, conditions []Condition[T]) *Update[T] {
var err error

if len(conditions) == 0 {
Expand Down
3 changes: 2 additions & 1 deletion delete.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cql

import (
"github.com/elliotchance/pie/v2"
"gorm.io/gorm"

"github.com/FrancoLiberali/cql/condition"
Expand All @@ -16,6 +17,6 @@ import (
func Delete[T model.Model](tx *gorm.DB, firstCondition condition.Condition[T], conditions ...condition.Condition[T]) *condition.Delete[T] {
return condition.NewDelete(
tx,
append(conditions, firstCondition)...,
pie.Unshift(conditions, firstCondition),
)
}
15 changes: 8 additions & 7 deletions logical.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package cql

import (
"github.com/elliotchance/pie/v2"

"github.com/FrancoLiberali/cql/condition"
"github.com/FrancoLiberali/cql/model"
"github.com/FrancoLiberali/cql/sql"
"github.com/FrancoLiberali/cql/unsafe"
)

Expand All @@ -17,17 +18,17 @@ import (
// Example:
//
// cql.And(conditions.City.Name.Is().Eq("Paris"), conditions.City.ZipCode.Is().Eq("75000"))
func And[T model.Model](conditions ...condition.WhereCondition[T]) condition.WhereCondition[T] {
return condition.And(conditions...)
func And[T model.Model](firstCondition condition.WhereCondition[T], conditions ...condition.WhereCondition[T]) condition.WhereCondition[T] {
return condition.And(pie.Unshift(conditions, firstCondition)...)
}

// Or allows the connection of multiple conditions by the OR logical connector.
//
// Example:
//
// cql.Or(conditions.City.Name.Is().Eq("Paris"), conditions.City.Name.Is().Eq("Buenos Aires"))
func Or[T model.Model](conditions ...condition.WhereCondition[T]) condition.WhereCondition[T] {
return condition.NewConnectionCondition(sql.Or, conditions...)
func Or[T model.Model](firstCondition condition.WhereCondition[T], conditions ...condition.WhereCondition[T]) condition.WhereCondition[T] {
return condition.Or(pie.Unshift(conditions, firstCondition)...)
}

// Not allows the negation of the conditions within it. Multiple conditions are connected by an AND by default.
Expand All @@ -39,8 +40,8 @@ func Or[T model.Model](conditions ...condition.WhereCondition[T]) condition.Wher
// translates as
//
// NOT (name = "Paris" AND name = "Buenos Aires")
func Not[T model.Model](conditions ...condition.WhereCondition[T]) condition.WhereCondition[T] {
return condition.Not(conditions...)
func Not[T model.Model](firstCondition condition.WhereCondition[T], conditions ...condition.WhereCondition[T]) condition.WhereCondition[T] {
return condition.Not(pie.Unshift(conditions, firstCondition)...)
}

// True represents a condition that is always true.
Expand Down
9 changes: 7 additions & 2 deletions mysql/logical.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package mysql

import (
"github.com/elliotchance/pie/v2"

"github.com/FrancoLiberali/cql/condition"
"github.com/FrancoLiberali/cql/model"
"github.com/FrancoLiberali/cql/sql"
)

func Xor[T model.Model](conditions ...condition.WhereCondition[T]) condition.WhereCondition[T] {
return condition.NewConnectionCondition(sql.MySQLXor, conditions...)
func Xor[T model.Model](firstCondition condition.WhereCondition[T], conditions ...condition.WhereCondition[T]) condition.WhereCondition[T] {
return condition.NewConnectionCondition(
sql.MySQLXor,
pie.Unshift(conditions, firstCondition),
)
}
29 changes: 0 additions & 29 deletions test/join_conditions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,35 +375,6 @@ func (ts *JoinConditionsIntTestSuite) TestJoinWithUnsafeCondition() {
EqualList(&ts.Suite, []*models.Sale{match}, entities)
}

func (ts *JoinConditionsIntTestSuite) TestJoinWithEmptyConnectionConditionMakesNothing() {
product1 := ts.createProduct("", 1, 0.0, false, nil)
product2 := ts.createProduct("", 2, 0.0, false, nil)

match1 := ts.createSale(0, product1, nil)
match2 := ts.createSale(0, product2, nil)

entities, err := cql.Query[models.Sale](
ts.db,
conditions.Sale.Product(
cql.And[models.Product](),
),
).Find()
ts.Require().NoError(err)

EqualList(&ts.Suite, []*models.Sale{match1, match2}, entities)
}

func (ts *JoinConditionsIntTestSuite) TestJoinWithEmptyContainerConditionReturnsError() {
_, err := cql.Query[models.Sale](
ts.db,
conditions.Sale.Product(
cql.Not[models.Product](),
),
).Find()
ts.ErrorIs(err, cql.ErrEmptyConditions)
ts.ErrorContains(err, "connector: Not; model: models.Product")
}

func (ts *JoinConditionsIntTestSuite) TestDynamicOperatorOver2Tables() {
company1 := ts.createCompany("ditrit")
company2 := ts.createCompany("orness")
Expand Down
21 changes: 0 additions & 21 deletions test/where_conditions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -554,24 +554,3 @@ func (ts *WhereConditionsIntTestSuite) TestUnsafeCondition() {

EqualList(&ts.Suite, []*models.Product{match1, match2}, entities)
}

func (ts *WhereConditionsIntTestSuite) TestEmptyConnectionConditionMakesNothing() {
match1 := ts.createProduct("match", 1, 0.0, true, nil)
match2 := ts.createProduct("match", 1, 0.0, true, nil)

entities, err := cql.Query[models.Product](
ts.db,
cql.And[models.Product](),
).Find()
ts.Require().NoError(err)

EqualList(&ts.Suite, []*models.Product{match1, match2}, entities)
}

func (ts *WhereConditionsIntTestSuite) TestEmptyContainerConditionReturnsError() {
_, err := cql.Query[models.Product](
ts.db,
cql.Not[models.Product](),
).Find()
ts.ErrorIs(err, cql.ErrEmptyConditions)
}
3 changes: 2 additions & 1 deletion update.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cql

import (
"github.com/elliotchance/pie/v2"
"gorm.io/gorm"

"github.com/FrancoLiberali/cql/condition"
Expand All @@ -16,6 +17,6 @@ import (
func Update[T model.Model](tx *gorm.DB, firstCondition condition.Condition[T], conditions ...condition.Condition[T]) *condition.Update[T] {
return condition.NewUpdate(
tx,
append(conditions, firstCondition)...,
pie.Unshift(conditions, firstCondition),
)
}

0 comments on commit b6d561e

Please sign in to comment.