Repository: mlange-42/arche
Pull request #60 (2023-02-12 22:30:47)

Query with/without
With and Without methods for generic queries.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab3b139..3ef37fb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,14 @@
 
 ## [[unpublished]](https://github.com/mlange-42/arche/compare/v0.3.1...main)
 
-Nothing
+### Features
+
+* Generic queries support optional and excluded components (#53, #56, #58, #59)
+* Logic filters for complex queries (#54, #58)
+
+### Other
+
+* Overhaul of the module structure, with generics and filters in separate packages (#55, #57)
 
 ## [[v0.3.1]](https://github.com/mlange-42/arche/compare/v0.3.0...v0.3.1)
 
diff --git a/ecs/query.go b/ecs/query.go
index 3c20ca6..0e95355 100644
--- a/ecs/query.go
+++ b/ecs/query.go
@@ -31,8 +31,8 @@ func (f Mask) Matches(bits BitMask) bool {
 	return bits.Contains(f.BitMask)
 }
 
-// ButNot excludes the given components.
-func (f Mask) ButNot(comps ...ID) MaskPair {
+// Without excludes the given components.
+func (f Mask) Without(comps ...ID) MaskPair {
 	return MaskPair{
 		Mask:    f,
 		Exclude: All(comps...),
diff --git a/examples/base/main.go b/examples/base/main.go
index 9532926..0fbd182 100644
--- a/examples/base/main.go
+++ b/examples/base/main.go
@@ -18,12 +18,18 @@ type Velocity struct {
 	Y float64
 }
 
+// Rotation component
+type Rotation struct {
+	A float64
+}
+
 func main() {
 	// Create a World.
 	world := ecs.NewWorld()
 
 	posID := ecs.ComponentID[Position](&world)
 	velID := ecs.ComponentID[Velocity](&world)
+	rotID := ecs.ComponentID[Rotation](&world)
 
 	// Create entities
 	for i := 0; i < 1000; i++ {
@@ -42,15 +48,18 @@ func main() {
 		vel.Y = rand.NormFloat64()
 	}
 
+	// Create a query
+	query := ecs.All(posID, velID).Without(rotID)
+
 	// Time loop.
 	for t := 0; t < 1000; t++ {
 		// Get a fresh query iterator.
-		query := world.Query(ecs.All(posID, velID))
+		q := world.Query(query)
 		// Iterate it
-		for query.Next() {
+		for q.Next() {
 			// Component access through a Query.
-			pos := (*Position)(query.Get(posID))
-			vel := (*Position)(query.Get(velID))
+			pos := (*Position)(q.Get(posID))
+			vel := (*Position)(q.Get(velID))
 			// Update component fields.
 			pos.X += vel.X
 			pos.Y += vel.Y
diff --git a/examples/generic/main.go b/examples/generic/main.go
index 0a78303..d2abb37 100644
--- a/examples/generic/main.go
+++ b/examples/generic/main.go
@@ -19,6 +19,16 @@ type Velocity struct {
 	Y float64
 }
 
+// Rotation component
+type Rotation struct {
+	A float64
+}
+
+// Elevation component
+type Elevation struct {
+	E float64
+}
+
 func main() {
 	// Create a World.
 	world := ecs.NewWorld()
@@ -28,7 +38,7 @@ func main() {
 		// Create a new Entity.
 		entity := world.NewEntity()
 		// Add components to it.
-		pos, vel := generic.Add2[Position, Velocity](&world, entity)
+		pos, vel, _, _ := generic.Add4[Position, Velocity, Rotation, Elevation](&world, entity)
 
 		// Initialize component fields.
 		pos.X = rand.Float64() * 100
@@ -55,4 +65,20 @@ func main() {
 			pos.Y += vel.Y
 		}
 	}
+
+	// A more complex generic query using optional and excluded components:
+	query =
+		generic.
+			Query2[Position, Velocity]().        // Components provided through Get... methods
+			Optional(generic.Mask1[Velocity]()). // but those may be nil
+			With(generic.Mask1[Elevation]()).    // additional required components
+			Without(generic.Mask1[Rotation]())   // and entities with any of these are excluded.
+
+	q := query.Build(&world)
+
+	for q.Next() {
+		pos, vel := q.GetAll()
+		pos.X += vel.X
+		pos.Y += vel.Y
+	}
 }
diff --git a/generic/query_generic.go b/generic/query_generic.go
index 441e8c5..fe84d3e 100644
--- a/generic/query_generic.go
+++ b/generic/query_generic.go
@@ -36,6 +36,7 @@ func toMaskOptional(w *ecs.World, include []ecs.ID, optional []reflect.Type) ecs
 
 // Q0Builder builds a [Q0] query
 type Q0Builder struct {
+	include []reflect.Type
 	exclude []reflect.Type
 }
 
@@ -46,19 +47,28 @@ func Query0() Q0Builder {
 	return Q0Builder{}
 }
 
-// ButNot excludes entities with the given components from the query.
+// With adds more required components that are not accessible using Get... methods.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q0Builder) ButNot(mask []reflect.Type) Q0Builder {
+func (q Q0Builder) With(mask []reflect.Type) Q0Builder {
+	q.include = append(q.include, mask...)
+	return q
+}
+
+// Without excludes entities with any of the given components from the query.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+func (q Q0Builder) Without(mask []reflect.Type) Q0Builder {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
 
 // Build builds a Q0 query for iteration.
 func (q Q0Builder) Build(w *ecs.World) Q0 {
+	ids := toIds(w, q.include)
 	return Q0{
 		w.Query(ecs.MaskPair{
-			Mask:    ecs.All(),
+			Mask:    ecs.Mask{BitMask: ecs.NewBitMask(ids...)},
 			Exclude: toMask(w, q.exclude),
 		}),
 	}
@@ -97,23 +107,31 @@ func (q Q1Builder[A]) Optional(mask []reflect.Type) Q1Builder[A] {
 	return q
 }
 
-// ButNot excludes entities with the given components from the query.
+// With adds more required components that are not accessible using Get... methods.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q1Builder[A]) ButNot(mask []reflect.Type) Q1Builder[A] {
+func (q Q1Builder[A]) With(mask []reflect.Type) Q1Builder[A] {
+	q.include = append(q.include, mask...)
+	return q
+}
+
+// Without excludes entities with any of the given components from the query.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+func (q Q1Builder[A]) Without(mask []reflect.Type) Q1Builder[A] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
 
 // Build builds a Q1 query for iteration.
 func (q Q1Builder[A]) Build(w *ecs.World) Q1[A] {
-	id := ecs.TypeID(w, q.include[0])
+	ids := toIds(w, q.include)
 	return Q1[A]{
 		w.Query(ecs.MaskPair{
-			Mask:    toMaskOptional(w, []ecs.ID{id}, q.optional),
+			Mask:    toMaskOptional(w, ids, q.optional),
 			Exclude: toMask(w, q.exclude),
 		}),
-		id,
+		ids[0],
 	}
 }
 
@@ -158,10 +176,18 @@ func (q Q2Builder[A, B]) Optional(mask []reflect.Type) Q2Builder[A, B] {
 	return q
 }
 
-// ButNot excludes entities with the given components from the query.
+// With adds more required components that are not accessible using Get... methods.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+func (q Q2Builder[A, B]) With(mask []reflect.Type) Q2Builder[A, B] {
+	q.include = append(q.include, mask...)
+	return q
+}
+
+// Without excludes entities with any of the given components from the query.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q2Builder[A, B]) ButNot(mask []reflect.Type) Q2Builder[A, B] {
+func (q Q2Builder[A, B]) Without(mask []reflect.Type) Q2Builder[A, B] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -229,10 +255,18 @@ func (q Q3Builder[A, B, C]) Optional(mask []reflect.Type) Q3Builder[A, B, C] {
 	return q
 }
 
-// ButNot excludes entities with the given components from the query.
+// With adds more required components that are not accessible using Get... methods.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q3Builder[A, B, C]) ButNot(mask []reflect.Type) Q3Builder[A, B, C] {
+func (q Q3Builder[A, B, C]) With(mask []reflect.Type) Q3Builder[A, B, C] {
+	q.include = append(q.include, mask...)
+	return q
+}
+
+// Without excludes entities with any of the given components from the query.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+func (q Q3Builder[A, B, C]) Without(mask []reflect.Type) Q3Builder[A, B, C] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -305,10 +339,18 @@ func (q Q4Builder[A, B, C, D]) Optional(mask []reflect.Type) Q4Builder[A, B, C,
 	return q
 }
 
-// ButNot excludes entities with the given components from the query.
+// With adds more required components that are not accessible using Get... methods.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+func (q Q4Builder[A, B, C, D]) With(mask []reflect.Type) Q4Builder[A, B, C, D] {
+	q.include = append(q.include, mask...)
+	return q
+}
+
+// Without excludes entities with any of the given components from the query.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q4Builder[A, B, C, D]) ButNot(mask []reflect.Type) Q4Builder[A, B, C, D] {
+func (q Q4Builder[A, B, C, D]) Without(mask []reflect.Type) Q4Builder[A, B, C, D] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -392,10 +434,18 @@ func (q Q5Builder[A, B, C, D, E]) Optional(mask []reflect.Type) Q5Builder[A, B,
 	return q
 }
 
-// ButNot excludes entities with the given components from the query.
+// With adds more required components that are not accessible using Get... methods.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+func (q Q5Builder[A, B, C, D, E]) With(mask []reflect.Type) Q5Builder[A, B, C, D, E] {
+	q.include = append(q.include, mask...)
+	return q
+}
+
+// Without excludes entities with any of the given components from the query.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q5Builder[A, B, C, D, E]) ButNot(mask []reflect.Type) Q5Builder[A, B, C, D, E] {
+func (q Q5Builder[A, B, C, D, E]) Without(mask []reflect.Type) Q5Builder[A, B, C, D, E] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -485,10 +535,18 @@ func (q Q6Builder[A, B, C, D, E, F]) Optional(mask []reflect.Type) Q6Builder[A,
 	return q
 }
 
-// ButNot excludes entities with the given components from the query.
+// With adds more required components that are not accessible using Get... methods.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+func (q Q6Builder[A, B, C, D, E, F]) With(mask []reflect.Type) Q6Builder[A, B, C, D, E, F] {
+	q.include = append(q.include, mask...)
+	return q
+}
+
+// Without excludes entities with any of the given components from the query.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q6Builder[A, B, C, D, E, F]) ButNot(mask []reflect.Type) Q6Builder[A, B, C, D, E, F] {
+func (q Q6Builder[A, B, C, D, E, F]) Without(mask []reflect.Type) Q6Builder[A, B, C, D, E, F] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -584,10 +642,18 @@ func (q Q7Builder[A, B, C, D, E, F, G]) Optional(mask []reflect.Type) Q7Builder[
 	return q
 }
 
-// ButNot excludes entities with the given components from the query.
+// With adds more required components that are not accessible using Get... methods.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+func (q Q7Builder[A, B, C, D, E, F, G]) With(mask []reflect.Type) Q7Builder[A, B, C, D, E, F, G] {
+	q.include = append(q.include, mask...)
+	return q
+}
+
+// Without excludes entities with any of the given components from the query.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q7Builder[A, B, C, D, E, F, G]) ButNot(mask []reflect.Type) Q7Builder[A, B, C, D, E, F, G] {
+func (q Q7Builder[A, B, C, D, E, F, G]) Without(mask []reflect.Type) Q7Builder[A, B, C, D, E, F, G] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -689,10 +755,18 @@ func (q Q8Builder[A, B, C, D, E, F, G, H]) Optional(mask []reflect.Type) Q8Build
 	return q
 }
 
-// ButNot excludes entities with the given components from the query.
+// With adds more required components that are not accessible using Get... methods.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+func (q Q8Builder[A, B, C, D, E, F, G, H]) With(mask []reflect.Type) Q8Builder[A, B, C, D, E, F, G, H] {
+	q.include = append(q.include, mask...)
+	return q
+}
+
+// Without excludes entities with any of the given components from the query.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q8Builder[A, B, C, D, E, F, G, H]) ButNot(mask []reflect.Type) Q8Builder[A, B, C, D, E, F, G, H] {
+func (q Q8Builder[A, B, C, D, E, F, G, H]) Without(mask []reflect.Type) Q8Builder[A, B, C, D, E, F, G, H] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }


Test patch:
diff --git a/ecs/query_test.go b/ecs/query_test.go
index b13a79a..be2d790 100644
--- a/ecs/query_test.go
+++ b/ecs/query_test.go
@@ -73,7 +73,7 @@ func TestQuery(t *testing.T) {
 
 	assert.Panics(t, func() { q.Next() })
 
-	q = w.Query(All(rotID).ButNot(posID))
+	q = w.Query(All(rotID).Without(posID))
 
 	cnt = 0
 	for q.Next() {
diff --git a/generic/query_generic_test.go b/generic/query_generic_test.go
index 22a7769..e002ce3 100644
--- a/generic/query_generic_test.go
+++ b/generic/query_generic_test.go
@@ -22,9 +22,11 @@ func TestQueryOptionalNot(t *testing.T) {
 
 	w.Assign(e1, 0, &testStruct0{2})
 	w.Assign(e1, 1, &testStruct1{2})
+	w.Assign(e1, 2, &testStruct2{1, 1})
 
 	w.Assign(e2, 0, &testStruct0{3})
 	w.Assign(e2, 1, &testStruct1{3})
+	w.Assign(e2, 2, &testStruct2{1, 1})
 	w.Assign(e2, 9, &testStruct9{})
 
 	query2 := Query2[testStruct0, testStruct1]().Build(&w)
@@ -34,13 +36,20 @@ func TestQueryOptionalNot(t *testing.T) {
 	}
 	assert.Equal(t, 3, cnt)
 
-	query2 = Query2[testStruct0, testStruct1]().ButNot(Mask1[testStruct9]()).Build(&w)
+	query2 = Query2[testStruct0, testStruct1]().Without(Mask1[testStruct9]()).Build(&w)
 	cnt = 0
 	for query2.Next() {
 		cnt++
 	}
 	assert.Equal(t, 2, cnt)
 
+	query2 = Query2[testStruct0, testStruct1]().With(Mask1[testStruct2]()).Without(Mask1[testStruct9]()).Build(&w)
+	cnt = 0
+	for query2.Next() {
+		cnt++
+	}
+	assert.Equal(t, 1, cnt)
+
 	query3 := Query3[testStruct0, testStruct1, testStruct9]().Build(&w)
 	cnt = 0
 	for query3.Next() {
@@ -66,17 +75,24 @@ func TestQuery0(t *testing.T) {
 	e2 := w.NewEntity()
 
 	w.Assign(e0, 0, &testStruct0{1})
+	w.Assign(e0, 8, &testStruct8{})
+
 	w.Assign(e1, 0, &testStruct0{2})
+
 	w.Assign(e2, 0, &testStruct0{2})
 	w.Assign(e2, 9, &testStruct9{})
 
 	cnt := 0
-	query := Query0().ButNot(Mask1[testStruct9]()).Build(&w)
+	query :=
+		Query0().
+			With(Mask1[testStruct8]()).
+			Without(Mask1[testStruct9]()).
+			Build(&w)
 
 	for query.Next() {
 		cnt++
 	}
-	assert.Equal(t, 2, cnt)
+	assert.Equal(t, 1, cnt)
 }
 
 func TestQuery1(t *testing.T) {
@@ -89,15 +105,20 @@ func TestQuery1(t *testing.T) {
 	e2 := w.NewEntity()
 
 	w.Assign(e0, 0, &testStruct0{1})
+
 	w.Assign(e1, 0, &testStruct0{2})
+
+	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+
 	w.Assign(e2, 0, &testStruct0{0})
 	w.Assign(e2, 9, &testStruct9{})
 
 	cnt := 0
 	query :=
 		Query1[testStruct0]().
-			Optional(Mask1[testStruct8]()).
-			ButNot(Mask1[testStruct9]()).
+			Optional(Mask1[testStruct9]()).
+			With(Mask1[testStruct8]()).
+			Without(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -105,7 +126,7 @@ func TestQuery1(t *testing.T) {
 		assert.Equal(t, cnt+1, int(c0.val))
 		cnt++
 	}
-	assert.Equal(t, 2, cnt)
+	assert.Equal(t, 1, cnt)
 }
 
 func TestQuery2(t *testing.T) {
@@ -125,13 +146,16 @@ func TestQuery2(t *testing.T) {
 	w.Assign(e1, 1, &testStruct1{3})
 	w.Assign(e2, 1, &testStruct1{4})
 
+	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+
 	w.Assign(e2, 9, &testStruct9{})
 
 	cnt := 0
 	query :=
 		Query2[testStruct0, testStruct1]().
 			Optional(Mask1[testStruct1]()).
-			ButNot(Mask1[testStruct9]()).
+			With(Mask1[testStruct8]()).
+			Without(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -145,7 +169,7 @@ func TestQuery2(t *testing.T) {
 		assert.Equal(t, c2, c22)
 		cnt++
 	}
-	assert.Equal(t, 2, cnt)
+	assert.Equal(t, 1, cnt)
 }
 
 func TestQuery3(t *testing.T) {
@@ -169,13 +193,16 @@ func TestQuery3(t *testing.T) {
 	w.Assign(e1, 2, &testStruct2{4, 0})
 	w.Assign(e2, 2, &testStruct2{5, 0})
 
+	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+
 	w.Assign(e2, 9, &testStruct9{})
 
 	cnt := 0
 	query :=
 		Query3[testStruct0, testStruct1, testStruct2]().
 			Optional(Mask1[testStruct1]()).
-			ButNot(Mask1[testStruct9]()).
+			With(Mask1[testStruct8]()).
+			Without(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -192,7 +219,7 @@ func TestQuery3(t *testing.T) {
 		assert.Equal(t, c3, c32)
 		cnt++
 	}
-	assert.Equal(t, 2, cnt)
+	assert.Equal(t, 1, cnt)
 }
 
 func TestQuery4(t *testing.T) {
@@ -220,13 +247,16 @@ func TestQuery4(t *testing.T) {
 	w.Assign(e1, 3, &testStruct3{5, 0, 0})
 	w.Assign(e2, 3, &testStruct3{5, 0, 0})
 
+	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+
 	w.Assign(e2, 9, &testStruct9{})
 
 	cnt := 0
 	query :=
 		Query4[testStruct0, testStruct1, testStruct2, testStruct3]().
 			Optional(Mask1[testStruct1]()).
-			ButNot(Mask1[testStruct9]()).
+			With(Mask1[testStruct8]()).
+			Without(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -246,7 +276,7 @@ func TestQuery4(t *testing.T) {
 		assert.Equal(t, c4, c42)
 		cnt++
 	}
-	assert.Equal(t, 2, cnt)
+	assert.Equal(t, 1, cnt)
 }
 
 func TestQuery5(t *testing.T) {
@@ -278,13 +308,16 @@ func TestQuery5(t *testing.T) {
 	w.Assign(e1, 4, &testStruct4{6, 0, 0, 0})
 	w.Assign(e2, 4, &testStruct4{6, 0, 0, 0})
 
+	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+
 	w.Assign(e2, 9, &testStruct9{})
 
 	cnt := 0
 	query :=
 		Query5[testStruct0, testStruct1, testStruct2, testStruct3, testStruct4]().
 			Optional(Mask1[testStruct1]()).
-			ButNot(Mask1[testStruct9]()).
+			With(Mask1[testStruct8]()).
+			Without(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -307,7 +340,7 @@ func TestQuery5(t *testing.T) {
 		assert.Equal(t, c5, c52)
 		cnt++
 	}
-	assert.Equal(t, 2, cnt)
+	assert.Equal(t, 1, cnt)
 }
 
 func TestQuery6(t *testing.T) {
@@ -343,13 +376,16 @@ func TestQuery6(t *testing.T) {
 	w.Assign(e1, 5, &testStruct5{7, 0, 0, 0, 0})
 	w.Assign(e2, 5, &testStruct5{7, 0, 0, 0, 0})
 
+	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+
 	w.Assign(e2, 9, &testStruct9{})
 
 	cnt := 0
 	query :=
 		Query6[testStruct0, testStruct1, testStruct2, testStruct3, testStruct4, testStruct5]().
 			Optional(Mask1[testStruct1]()).
-			ButNot(Mask1[testStruct9]()).
+			With(Mask1[testStruct8]()).
+			Without(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -375,7 +411,7 @@ func TestQuery6(t *testing.T) {
 		assert.Equal(t, c6, c62)
 		cnt++
 	}
-	assert.Equal(t, 2, cnt)
+	assert.Equal(t, 1, cnt)
 }
 
 func TestQuery7(t *testing.T) {
@@ -411,9 +447,11 @@ func TestQuery7(t *testing.T) {
 	w.Assign(e1, 5, &testStruct5{7, 0, 0, 0, 0})
 	w.Assign(e2, 5, &testStruct5{7, 0, 0, 0, 0})
 
-	w.Assign(e0, 6, &testStruct5{7, 0, 0, 0, 0})
-	w.Assign(e1, 6, &testStruct5{8, 0, 0, 0, 0})
-	w.Assign(e2, 6, &testStruct5{8, 0, 0, 0, 0})
+	w.Assign(e0, 6, &testStruct6{7, 0, 0, 0, 0, 0})
+	w.Assign(e1, 6, &testStruct6{8, 0, 0, 0, 0, 0})
+	w.Assign(e2, 6, &testStruct6{8, 0, 0, 0, 0, 0})
+
+	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
 
 	w.Assign(e2, 9, &testStruct9{})
 
@@ -424,7 +462,8 @@ func TestQuery7(t *testing.T) {
 			testStruct5, testStruct6,
 		]().
 			Optional(Mask1[testStruct1]()).
-			ButNot(Mask1[testStruct9]()).
+			With(Mask1[testStruct8]()).
+			Without(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -453,7 +492,7 @@ func TestQuery7(t *testing.T) {
 		assert.Equal(t, c7, c72)
 		cnt++
 	}
-	assert.Equal(t, 2, cnt)
+	assert.Equal(t, 1, cnt)
 }
 
 func TestQuery8(t *testing.T) {
@@ -483,11 +522,13 @@ func TestQuery8(t *testing.T) {
 	w.Assign(e0, 5, &testStruct5{6, 0, 0, 0, 0})
 	w.Assign(e1, 5, &testStruct5{7, 0, 0, 0, 0})
 
-	w.Assign(e0, 6, &testStruct5{7, 0, 0, 0, 0})
-	w.Assign(e1, 6, &testStruct5{8, 0, 0, 0, 0})
+	w.Assign(e0, 6, &testStruct6{7, 0, 0, 0, 0, 0})
+	w.Assign(e1, 6, &testStruct6{8, 0, 0, 0, 0, 0})
+
+	w.Assign(e0, 7, &testStruct7{8, 0, 0, 0, 0, 0, 0})
+	w.Assign(e1, 7, &testStruct7{9, 0, 0, 0, 0, 0, 0})
 
-	w.Assign(e0, 7, &testStruct5{8, 0, 0, 0, 0})
-	w.Assign(e1, 7, &testStruct5{9, 0, 0, 0, 0})
+	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
 
 	w.Assign(e2, 9, &testStruct9{})
 
@@ -498,7 +539,8 @@ func TestQuery8(t *testing.T) {
 			testStruct4, testStruct5, testStruct6, testStruct7,
 		]().
 			Optional(Mask1[testStruct1]()).
-			ButNot(Mask1[testStruct9]()).
+			With(Mask1[testStruct8]()).
+			Without(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -530,7 +572,7 @@ func TestQuery8(t *testing.T) {
 		assert.Equal(t, c8, c82)
 		cnt++
 	}
-	assert.Equal(t, 2, cnt)
+	assert.Equal(t, 1, cnt)
 }
 
 func TestGenericMask(t *testing.T) {