Repository: ovn-org/libovsdb
Pull request #117 (2021-05-18 15:27:02)

 Add WhereAll() conditions #115 
WhereAll uses the condition list to generate a single operation that matches all of the conditions where as Where is left with the old behavior, i.e: generate N operations where each operation match a single condition.

The logical semantics for conditions is therefore:
Where() -> OR
WhereAll() -> AND

Examples:
```
// Delete a single a port whose name is "foo" AND is enabled
lsp := &LogicalSwitchPort{}
client.WhereAll(&lsp,
    Condition {
        Field: &lsp.Name,
        Function: ovsdb.ConditionEqual,
        Value: "foo",
    }, Condition {
        Field: &lsp.Enabled:
        Function: ovsdb.ConditionEqual,
        Value: []bool{true},
    }).Delete()

// Delete all ports that are enabled OR are named "foo"
client.Where(&lsp,
    Condition {
        Field: &lsp.Name,
        Function: ovsdb.ConditionEqual,
        Value: "foo",
    }, Condition {
        Field: &lsp.Enabled:
        Function: ovsdb.ConditionEqual,
        Value: []bool{true},
    }).Delete()
```

Based on #114 and #113


Patch:
diff --git a/client/api.go b/client/api.go
index 8634c09..fcfac08 100644
--- a/client/api.go
+++ b/client/api.go
@@ -29,8 +29,15 @@ type API interface {
 	WhereCache(predicate interface{}) ConditionalAPI
 
 	// Create a ConditionalAPI from a Model's index data or a list of Conditions
+	// where operations apply to elements that match any of the conditions
+	// If no condition is given, it will match the values provided in Model according
+	// to the database index.
 	Where(Model, ...Condition) ConditionalAPI
 
+	// Create a ConditionalAPI from a Model's index data or a list of Conditions
+	// where operations apply to elements that match all the conditions
+	WhereAll(Model, ...Condition) ConditionalAPI
+
 	// Get retrieves a model from the cache
 	// The way the object will be fetch depends on the data contained in the
 	// provided model and the indexes defined in the associated schema
@@ -176,7 +183,12 @@ func (a api) List(result interface{}) error {
 
 // Where returns a conditionalAPI based on a Condition list
 func (a api) Where(model Model, cond ...Condition) ConditionalAPI {
-	return newConditionalAPI(a.cache, a.conditionFromModel(model, cond...))
+	return newConditionalAPI(a.cache, a.conditionFromModel(false, model, cond...))
+}
+
+// Where returns a conditionalAPI based on a Condition list
+func (a api) WhereAll(model Model, cond ...Condition) ConditionalAPI {
+	return newConditionalAPI(a.cache, a.conditionFromModel(true, model, cond...))
 }
 
 // Where returns a conditionalAPI based a Predicate
@@ -200,7 +212,7 @@ func (a api) conditionFromFunc(predicate interface{}) Conditional {
 }
 
 // FromModel returns a Condition from a model and a list of fields
-func (a api) conditionFromModel(model Model, cond ...Condition) Conditional {
+func (a api) conditionFromModel(any bool, model Model, cond ...Condition) Conditional {
 	var conditional Conditional
 	var err error
 
@@ -210,13 +222,13 @@ func (a api) conditionFromModel(model Model, cond ...Condition) Conditional {
 	}
 
 	if len(cond) == 0 {
-		conditional, err = newEqualityConditional(a.cache.orm, tableName, model)
+		conditional, err = newEqualityConditional(a.cache.orm, tableName, any, model)
 		if err != nil {
 			conditional = newErrorConditional(err)
 		}
 
 	} else {
-		conditional, err = newExplicitConditional(a.cache.orm, tableName, model, cond...)
+		conditional, err = newExplicitConditional(a.cache.orm, tableName, any, model, cond...)
 		if err != nil {
 			conditional = newErrorConditional(err)
 		}
@@ -351,7 +363,7 @@ func (a api) Mutate(model Model, mutationObjs []Mutation) ([]ovsdb.Operation, er
 				Op:        opMutate,
 				Table:     tableName,
 				Mutations: mutations,
-				Where:     []ovsdb.Condition{condition},
+				Where:     condition,
 			},
 		)
 	}
@@ -384,7 +396,7 @@ func (a api) Update(model Model, fields ...interface{}) ([]ovsdb.Operation, erro
 				Op:    opUpdate,
 				Table: table,
 				Row:   row,
-				Where: []ovsdb.Condition{condition},
+				Where: condition,
 			},
 		)
 	}
@@ -404,7 +416,7 @@ func (a api) Delete() ([]ovsdb.Operation, error) {
 			ovsdb.Operation{
 				Op:    opDelete,
 				Table: a.cond.Table(),
-				Where: []ovsdb.Condition{condition},
+				Where: condition,
 			},
 		)
 	}
diff --git a/client/client.go b/client/client.go
index 43cce94..153a749 100644
--- a/client/client.go
+++ b/client/client.go
@@ -375,6 +375,11 @@ func (ovs OvsdbClient) Where(m Model, conditions ...Condition) ConditionalAPI {
 	return ovs.api.Where(m, conditions...)
 }
 
+//WhereAll implements the API interface's WhereAll function
+func (ovs OvsdbClient) WhereAll(m Model, conditions ...Condition) ConditionalAPI {
+	return ovs.api.WhereAll(m, conditions...)
+}
+
 //WhereCache implements the API interface's WhereCache function
 func (ovs OvsdbClient) WhereCache(predicate interface{}) ConditionalAPI {
 	return ovs.api.WhereCache(predicate)
diff --git a/client/condition.go b/client/condition.go
index 452f501..c2fa097 100644
--- a/client/condition.go
+++ b/client/condition.go
@@ -10,8 +10,9 @@ import (
 // Conditional is the interface used by the ConditionalAPI to match on cache objects
 // and generate ovsdb conditions
 type Conditional interface {
-	// Generate returns a list of conditions to be used in Operations
-	Generate() ([]ovsdb.Condition, error)
+	// Generate returns a list of lists of conditions to be used in Operations
+	// Each element in the (outer) list corresponds to an operation
+	Generate() ([][]ovsdb.Condition, error)
 	// matches returns true if a model matches the condition
 	Matches(m Model) (bool, error)
 	// returns the table that this condition is associated with
@@ -25,6 +26,7 @@ type equalityConditional struct {
 	orm       *orm
 	tableName string
 	model     Model
+	singleOp  bool
 }
 
 func (c *equalityConditional) Matches(m Model) (bool, error) {
@@ -36,20 +38,30 @@ func (c *equalityConditional) Table() string {
 }
 
 // Generate returns a condition based on the model and the field pointers
-func (c *equalityConditional) Generate() ([]ovsdb.Condition, error) {
-	condition, err := c.orm.newEqualityCondition(c.tableName, c.model)
+func (c *equalityConditional) Generate() ([][]ovsdb.Condition, error) {
+	var result [][]ovsdb.Condition
+
+	conds, err := c.orm.newEqualityCondition(c.tableName, c.model)
 	if err != nil {
 		return nil, err
 	}
-	return condition, nil
+	if c.singleOp {
+		result = append(result, conds)
+	} else {
+		for _, c := range conds {
+			result = append(result, []ovsdb.Condition{c})
+		}
+	}
+	return result, nil
 }
 
 // newIndexCondition creates a new equalityConditional
-func newEqualityConditional(orm *orm, table string, model Model, fields ...interface{}) (Conditional, error) {
+func newEqualityConditional(orm *orm, table string, all bool, model Model, fields ...interface{}) (Conditional, error) {
 	return &equalityConditional{
 		orm:       orm,
 		tableName: table,
 		model:     model,
+		singleOp:  all,
 	}, nil
 }
 
@@ -59,6 +71,7 @@ type explicitConditional struct {
 	tableName  string
 	model      Model
 	conditions []Condition
+	singleOp   bool
 }
 
 func (c *explicitConditional) Matches(m Model) (bool, error) {
@@ -70,25 +83,36 @@ func (c *explicitConditional) Table() string {
 }
 
 // Generate returns a condition based on the model and the field pointers
-func (c *explicitConditional) Generate() ([]ovsdb.Condition, error) {
-	var result []ovsdb.Condition
+func (c *explicitConditional) Generate() ([][]ovsdb.Condition, error) {
+	var result [][]ovsdb.Condition
+	var conds []ovsdb.Condition
+
 	for _, cond := range c.conditions {
 		ovsdbCond, err := c.orm.newCondition(c.tableName, c.model, cond)
 		if err != nil {
 			return nil, err
 		}
-		result = append(result, *ovsdbCond)
+		if c.singleOp {
+			conds = append(conds, *ovsdbCond)
+		} else {
+			result = append(result, []ovsdb.Condition{*ovsdbCond})
+		}
+
+	}
+	if c.singleOp {
+		result = append(result, conds)
 	}
 	return result, nil
 }
 
 // newIndexCondition creates a new equalityConditional
-func newExplicitConditional(orm *orm, table string, model Model, cond ...Condition) (Conditional, error) {
+func newExplicitConditional(orm *orm, table string, all bool, model Model, cond ...Condition) (Conditional, error) {
 	return &explicitConditional{
 		orm:        orm,
 		tableName:  table,
 		model:      model,
 		conditions: cond,
+		singleOp:   all,
 	}, nil
 }
 
@@ -113,8 +137,8 @@ func (c *predicateConditional) Table() string {
 
 // generate returns a list of conditions that match, by _uuid equality, all the objects that
 // match the predicate
-func (c *predicateConditional) Generate() ([]ovsdb.Condition, error) {
-	allConditions := make([]ovsdb.Condition, 0)
+func (c *predicateConditional) Generate() ([][]ovsdb.Condition, error) {
+	allConditions := make([][]ovsdb.Condition, 0)
 	tableCache := c.cache.Table(c.tableName)
 	if tableCache == nil {
 		return nil, ErrNotFound
@@ -130,7 +154,7 @@ func (c *predicateConditional) Generate() ([]ovsdb.Condition, error) {
 			if err != nil {
 				return nil, err
 			}
-			allConditions = append(allConditions, elemCond...)
+			allConditions = append(allConditions, elemCond)
 		}
 	}
 	return allConditions, nil
@@ -159,7 +183,7 @@ func (e *errorConditional) Table() string {
 	return ""
 }
 
-func (e *errorConditional) Generate() ([]ovsdb.Condition, error) {
+func (e *errorConditional) Generate() ([][]ovsdb.Condition, error) {
 	return nil, e.err
 }
 


Test patch:
diff --git a/client/api_test.go b/client/api_test.go
index 8ac0267..087733c 100644
--- a/client/api_test.go
+++ b/client/api_test.go
@@ -398,7 +398,7 @@ func TestConditionFromModel(t *testing.T) {
 		t.Run(fmt.Sprintf("conditionFromModel: %s", tt.name), func(t *testing.T) {
 			cache := apiTestCache(t)
 			apiIface := newAPI(cache)
-			condition := apiIface.(api).conditionFromModel(tt.model, tt.conds...)
+			condition := apiIface.(api).conditionFromModel(false, tt.model, tt.conds...)
 			if tt.err {
 				assert.IsType(t, &errorConditional{}, condition)
 			} else {
@@ -867,6 +867,73 @@ func TestAPIUpdate(t *testing.T) {
 			},
 			err: false,
 		},
+		{
+			name: "multiple select any by field change multiple field",
+			condition: func(a API) ConditionalAPI {
+				t := testLogicalSwitchPort{}
+				return a.Where(&t,
+					Condition{
+						Field:    &t.Type,
+						Function: ovsdb.ConditionEqual,
+						Value:    "sometype",
+					},
+					Condition{
+						Field:    &t.Enabled,
+						Function: ovsdb.ConditionIncludes,
+						Value:    []bool{true},
+					})
+			},
+			prepare: func(t *testLogicalSwitchPort) {
+				t.Tag = []int{6}
+			},
+			result: []ovsdb.Operation{
+				{
+					Op:    opUpdate,
+					Table: "Logical_Switch_Port",
+					Row:   map[string]interface{}{"tag": testOvsSet(t, []int{6})},
+					Where: []ovsdb.Condition{{Column: "type", Function: ovsdb.ConditionEqual, Value: "sometype"}},
+				},
+				{
+					Op:    opUpdate,
+					Table: "Logical_Switch_Port",
+					Row:   map[string]interface{}{"tag": testOvsSet(t, []int{6})},
+					Where: []ovsdb.Condition{{Column: "enabled", Function: ovsdb.ConditionIncludes, Value: testOvsSet(t, []bool{true})}},
+				},
+			},
+			err: false,
+		},
+		{
+			name: "multiple select all by field change multiple field",
+			condition: func(a API) ConditionalAPI {
+				t := testLogicalSwitchPort{}
+				return a.WhereAll(&t,
+					Condition{
+						Field:    &t.Type,
+						Function: ovsdb.ConditionEqual,
+						Value:    "sometype",
+					},
+					Condition{
+						Field:    &t.Enabled,
+						Function: ovsdb.ConditionIncludes,
+						Value:    []bool{true},
+					})
+			},
+			prepare: func(t *testLogicalSwitchPort) {
+				t.Tag = []int{6}
+			},
+			result: []ovsdb.Operation{
+				{
+					Op:    opUpdate,
+					Table: "Logical_Switch_Port",
+					Row:   map[string]interface{}{"tag": testOvsSet(t, []int{6})},
+					Where: []ovsdb.Condition{
+						{Column: "type", Function: ovsdb.ConditionEqual, Value: "sometype"},
+						{Column: "enabled", Function: ovsdb.ConditionIncludes, Value: testOvsSet(t, []bool{true})},
+					},
+				},
+			},
+			err: false,
+		},
 		{
 			name: "select by field inequality change multiple field",
 			condition: func(a API) ConditionalAPI {
@@ -1027,6 +1094,66 @@ func TestAPIDelete(t *testing.T) {
 			},
 			err: false,
 		},
+		{
+			name: "select any by field ",
+			condition: func(a API) ConditionalAPI {
+				t := testLogicalSwitchPort{
+					Enabled: []bool{true},
+				}
+				return a.Where(&t,
+					Condition{
+						Field:    &t.Type,
+						Function: ovsdb.ConditionEqual,
+						Value:    "sometype",
+					}, Condition{
+						Field:    &t.Name,
+						Function: ovsdb.ConditionEqual,
+						Value:    "foo",
+					})
+			},
+			result: []ovsdb.Operation{
+				{
+					Op:    opDelete,
+					Table: "Logical_Switch_Port",
+					Where: []ovsdb.Condition{{Column: "type", Function: ovsdb.ConditionEqual, Value: "sometype"}},
+				},
+				{
+					Op:    opDelete,
+					Table: "Logical_Switch_Port",
+					Where: []ovsdb.Condition{{Column: "name", Function: ovsdb.ConditionEqual, Value: "foo"}},
+				},
+			},
+			err: false,
+		},
+		{
+			name: "select all by field ",
+			condition: func(a API) ConditionalAPI {
+				t := testLogicalSwitchPort{
+					Enabled: []bool{true},
+				}
+				return a.WhereAll(&t,
+					Condition{
+						Field:    &t.Type,
+						Function: ovsdb.ConditionEqual,
+						Value:    "sometype",
+					}, Condition{
+						Field:    &t.Name,
+						Function: ovsdb.ConditionEqual,
+						Value:    "foo",
+					})
+			},
+			result: []ovsdb.Operation{
+				{
+					Op:    opDelete,
+					Table: "Logical_Switch_Port",
+					Where: []ovsdb.Condition{
+						{Column: "type", Function: ovsdb.ConditionEqual, Value: "sometype"},
+						{Column: "name", Function: ovsdb.ConditionEqual, Value: "foo"},
+					},
+				},
+			},
+			err: false,
+		},
 		{
 			name: "select multiple by predicate",
 			condition: func(a API) ConditionalAPI {
diff --git a/client/condition_test.go b/client/condition_test.go
index 09e54ee..9487388 100644
--- a/client/condition_test.go
+++ b/client/condition_test.go
@@ -45,40 +45,77 @@ func TestEqualityConditional(t *testing.T) {
 	test := []struct {
 		name      string
 		model     Model
-		condition []ovsdb.Condition
+		condition [][]ovsdb.Condition
 		matches   map[Model]bool
+		all       bool
 		err       bool
 	}{
 		{
 			name:  "by uuid",
 			model: &testLogicalSwitchPort{UUID: aUUID0, Name: "different"},
-			condition: []ovsdb.Condition{
+			condition: [][]ovsdb.Condition{
 				{
-					Column:   "_uuid",
-					Function: ovsdb.ConditionEqual,
-					Value:    ovsdb.UUID{GoUUID: aUUID0},
-				}},
+					{
+						Column:   "_uuid",
+						Function: ovsdb.ConditionEqual,
+						Value:    ovsdb.UUID{GoUUID: aUUID0},
+					}}},
+			matches: map[Model]bool{
+				&testLogicalSwitchPort{UUID: aUUID0}:              true,
+				&testLogicalSwitchPort{UUID: aUUID1}:              false,
+				&testLogicalSwitchPort{UUID: aUUID0, Name: "foo"}: true,
+			},
+		},
+		{
+			name:  "by uuid all",
+			model: &testLogicalSwitchPort{UUID: aUUID0, Name: "different"},
+			condition: [][]ovsdb.Condition{
+				{
+					{
+						Column:   "_uuid",
+						Function: ovsdb.ConditionEqual,
+						Value:    ovsdb.UUID{GoUUID: aUUID0},
+					}}},
 			matches: map[Model]bool{
 				&testLogicalSwitchPort{UUID: aUUID0}:              true,
 				&testLogicalSwitchPort{UUID: aUUID1}:              false,
 				&testLogicalSwitchPort{UUID: aUUID0, Name: "foo"}: true,
 			},
+			all: true,
 		},
 		{
 			name:  "by index",
 			model: &testLogicalSwitchPort{Name: "lsp1"},
-			condition: []ovsdb.Condition{
+			condition: [][]ovsdb.Condition{
 				{
-					Column:   "name",
-					Function: ovsdb.ConditionEqual,
-					Value:    "lsp1",
-				}},
+					{
+						Column:   "name",
+						Function: ovsdb.ConditionEqual,
+						Value:    "lsp1",
+					}}},
 			matches: map[Model]bool{
 				&testLogicalSwitchPort{UUID: aUUID1}:               false,
 				&testLogicalSwitchPort{UUID: aUUID1, Name: "lsp1"}: true,
 				&testLogicalSwitchPort{UUID: aUUID0, Name: "lsp1"}: true,
 			},
 		},
+		{
+			name:  "by index all",
+			model: &testLogicalSwitchPort{Name: "lsp1"},
+			condition: [][]ovsdb.Condition{
+				{
+					{
+						Column:   "name",
+						Function: ovsdb.ConditionEqual,
+						Value:    "lsp1",
+					}}},
+			matches: map[Model]bool{
+				&testLogicalSwitchPort{UUID: aUUID1}:               false,
+				&testLogicalSwitchPort{UUID: aUUID1, Name: "lsp1"}: true,
+				&testLogicalSwitchPort{UUID: aUUID0, Name: "lsp1"}: true,
+			},
+			all: true,
+		},
 		{
 			name:  "by non index",
 			model: &testLogicalSwitchPort{ExternalIds: map[string]string{"foo": "baz"}},
@@ -87,7 +124,7 @@ func TestEqualityConditional(t *testing.T) {
 	}
 	for _, tt := range test {
 		t.Run(fmt.Sprintf("Equality Conditional: %s", tt.name), func(t *testing.T) {
-			cond, err := newEqualityConditional(cache.orm, "Logical_Switch_Port", tt.model)
+			cond, err := newEqualityConditional(cache.orm, "Logical_Switch_Port", tt.all, tt.model)
 			assert.Nil(t, err)
 			for model, shouldMatch := range tt.matches {
 				matches, err := cond.Matches(model)
@@ -146,7 +183,7 @@ func TestPredicateConditional(t *testing.T) {
 	test := []struct {
 		name      string
 		predicate interface{}
-		condition []ovsdb.Condition
+		condition [][]ovsdb.Condition
 		matches   map[Model]bool
 		err       bool
 	}{
@@ -155,12 +192,13 @@ func TestPredicateConditional(t *testing.T) {
 			predicate: func(lsp *testLogicalSwitchPort) bool {
 				return lsp.UUID == aUUID0
 			},
-			condition: []ovsdb.Condition{
+			condition: [][]ovsdb.Condition{
 				{
-					Column:   "_uuid",
-					Function: ovsdb.ConditionEqual,
-					Value:    ovsdb.UUID{GoUUID: aUUID0},
-				}},
+					{
+						Column:   "_uuid",
+						Function: ovsdb.ConditionEqual,
+						Value:    ovsdb.UUID{GoUUID: aUUID0},
+					}}},
 			matches: map[Model]bool{
 				&testLogicalSwitchPort{UUID: aUUID0}:              true,
 				&testLogicalSwitchPort{UUID: aUUID1}:              false,
@@ -172,17 +210,19 @@ func TestPredicateConditional(t *testing.T) {
 			predicate: func(lsp *testLogicalSwitchPort) bool {
 				return lsp.Enabled[0] == false
 			},
-			condition: []ovsdb.Condition{
+			condition: [][]ovsdb.Condition{
 				{
-					Column:   "_uuid",
-					Function: ovsdb.ConditionEqual,
-					Value:    ovsdb.UUID{GoUUID: aUUID1},
-				},
+					{
+						Column:   "_uuid",
+						Function: ovsdb.ConditionEqual,
+						Value:    ovsdb.UUID{GoUUID: aUUID1},
+					}},
 				{
-					Column:   "_uuid",
-					Function: ovsdb.ConditionEqual,
-					Value:    ovsdb.UUID{GoUUID: aUUID2},
-				}},
+					{
+						Column:   "_uuid",
+						Function: ovsdb.ConditionEqual,
+						Value:    ovsdb.UUID{GoUUID: aUUID2},
+					}}},
 			matches: map[Model]bool{
 				&testLogicalSwitchPort{UUID: aUUID1, Enabled: []bool{true}}:  false,
 				&testLogicalSwitchPort{UUID: aUUID1, Enabled: []bool{false}}: true,
@@ -252,7 +292,8 @@ func TestExplicitConditional(t *testing.T) {
 	test := []struct {
 		name   string
 		args   []Condition
-		result []ovsdb.Condition
+		result [][]ovsdb.Condition
+		all    bool
 		err    bool
 	}{
 		{
@@ -264,12 +305,31 @@ func TestExplicitConditional(t *testing.T) {
 					Value:    "lsp0",
 				},
 			},
-			result: []ovsdb.Condition{
+			result: [][]ovsdb.Condition{
 				{
-					Column:   "name",
+					{
+						Column:   "name",
+						Function: ovsdb.ConditionNotEqual,
+						Value:    "lsp0",
+					}}},
+		},
+		{
+			name: "inequality comparison all",
+			args: []Condition{
+				{
+					Field:    &testObj.Name,
 					Function: ovsdb.ConditionNotEqual,
 					Value:    "lsp0",
-				}},
+				},
+			},
+			result: [][]ovsdb.Condition{
+				{
+					{
+						Column:   "name",
+						Function: ovsdb.ConditionNotEqual,
+						Value:    "lsp0",
+					}}},
+			all: true,
 		},
 		{
 			name: "map comparison",
@@ -280,12 +340,13 @@ func TestExplicitConditional(t *testing.T) {
 					Value:    map[string]string{"foo": "baz"},
 				},
 			},
-			result: []ovsdb.Condition{
+			result: [][]ovsdb.Condition{
 				{
-					Column:   "external_ids",
-					Function: ovsdb.ConditionIncludes,
-					Value:    testOvsMap(t, map[string]string{"foo": "baz"}),
-				}},
+					{
+						Column:   "external_ids",
+						Function: ovsdb.ConditionIncludes,
+						Value:    testOvsMap(t, map[string]string{"foo": "baz"}),
+					}}},
 		},
 		{
 			name: "set comparison",
@@ -296,12 +357,13 @@ func TestExplicitConditional(t *testing.T) {
 					Value:    []bool{true},
 				},
 			},
-			result: []ovsdb.Condition{
+			result: [][]ovsdb.Condition{
 				{
-					Column:   "enabled",
-					Function: ovsdb.ConditionEqual,
-					Value:    testOvsSet(t, []bool{true}),
-				}},
+					{
+						Column:   "enabled",
+						Function: ovsdb.ConditionEqual,
+						Value:    testOvsSet(t, []bool{true}),
+					}}},
 		},
 		{
 			name: "multiple conditions",
@@ -317,7 +379,35 @@ func TestExplicitConditional(t *testing.T) {
 					Value:    "foo",
 				},
 			},
-			result: []ovsdb.Condition{
+			result: [][]ovsdb.Condition{
+				{
+					{
+						Column:   "enabled",
+						Function: ovsdb.ConditionEqual,
+						Value:    testOvsSet(t, []bool{true}),
+					}},
+				{
+					{
+						Column:   "name",
+						Function: ovsdb.ConditionNotEqual,
+						Value:    "foo",
+					}}},
+		},
+		{
+			name: "multiple conditions all",
+			args: []Condition{
+				{
+					Field:    &testObj.Enabled,
+					Function: ovsdb.ConditionEqual,
+					Value:    []bool{true},
+				},
+				{
+					Field:    &testObj.Name,
+					Function: ovsdb.ConditionNotEqual,
+					Value:    "foo",
+				},
+			},
+			result: [][]ovsdb.Condition{{
 				{
 					Column:   "enabled",
 					Function: ovsdb.ConditionEqual,
@@ -327,12 +417,13 @@ func TestExplicitConditional(t *testing.T) {
 					Column:   "name",
 					Function: ovsdb.ConditionNotEqual,
 					Value:    "foo",
-				}},
+				}}},
+			all: true,
 		},
 	}
 	for _, tt := range test {
 		t.Run(fmt.Sprintf("Explicit Conditional: %s", tt.name), func(t *testing.T) {
-			cond, err := newExplicitConditional(cache.orm, "Logical_Switch_Port", testObj, tt.args...)
+			cond, err := newExplicitConditional(cache.orm, "Logical_Switch_Port", tt.all, testObj, tt.args...)
 			assert.Nil(t, err)
 			_, err = cond.Matches(testObj)
 			assert.NotNilf(t, err, "Explicit conditions should fail to match on cache")