nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: algolia/algoliasearch-client-go
Pull request #734 (2023-05-11 09:56:21)

fix(recommend): Handle taskIDs below 1000 as index scope
| Q                 | A
| ----------------- | ----------
| Bug fix?          | yes
| New feature?      | no
| BC breaks?        | no     
| Need Doc update   | no


## Describe your change

Treat taskIDs below 1000 as being of "index" scope.

## What problem is this fixing?

An error was thrown when taskIDs were below 1000 instead of accepting it as being of "index" scope.


Patch:
diff --git a/algolia/search/utils.go b/algolia/search/utils.go
index e207b393..46fbab95 100644
--- a/algolia/search/utils.go
+++ b/algolia/search/utils.go
@@ -103,6 +103,9 @@ func hasObjectID(object interface{}) bool {
 }
 
 func getScopeFromTaskID(taskID int64) (string, error) {
+	if taskID < 1000 {
+		return "index", nil
+	}
 	scopeID := (taskID / 10) % 100
 	switch scopeID {
 	case 0:
@@ -114,6 +117,6 @@ func getScopeFromTaskID(taskID int64) (string, error) {
 	case 3:
 		return "recommend", nil
 	default:
-		return "", fmt.Errorf("invalid taskID scope")
+		return "", fmt.Errorf("invalid taskID scope: %d", scopeID)
 	}
 }


Test patch:
diff --git a/algolia/search/utils_test.go b/algolia/search/utils_test.go
index b77fab17..56445c0c 100644
--- a/algolia/search/utils_test.go
+++ b/algolia/search/utils_test.go
@@ -58,14 +58,17 @@ func TestGetScopeFromTaskID(t *testing.T) {
 	for _, c := range []struct {
 		taskID        int64
 		expectedScope string
+		expectedError error
 	}{
-		{4001, "index"},
-		{4011, "app"},
-		{4021, "metis"},
-		{4031, "recommend"},
+		{123, "index", nil},
+		{4001, "index", nil},
+		{4011, "app", nil},
+		{4021, "metis", nil},
+		{4031, "recommend", nil},
+		{4041, "", fmt.Errorf("invalid taskID scope: 4")},
 	} {
 		scope, err := getScopeFromTaskID(c.taskID)
-		require.NoError(t, err)
 		require.Equal(t, c.expectedScope, scope)
+		require.Equal(t, c.expectedError, err)
 	}
 }
Repository: algolia/algoliasearch-client-go
Pull request #737 (2023-06-06 10:20:11)

feat(renderingContent): parse the redirect attribute
| Q                 | A
| ----------------- | ----------
| Bug fix?          | yes
| New feature?      | yes
| BC breaks?        | no     
| Need Doc update   | no


## Describe your change

Parse the `redirect` attribute and prevent to serialize `FacetOrdering` when nil

## What problem is this fixing?

When unmarshalling a rendering content with only `redirect`, the attribute was ignored. Then marshalling the struct resulted in an invalid renderint content because `FacetOrdering` set to nil is invalid



Patch:
diff --git a/algolia/search/rendering_content.go b/algolia/search/rendering_content.go
index aa84ed21..8057c9d9 100644
--- a/algolia/search/rendering_content.go
+++ b/algolia/search/rendering_content.go
@@ -3,7 +3,8 @@ package search
 // Content defining how the search interface should be rendered.
 // This is set via the settings for a default value and can be overridden via rules
 type RenderingContent struct {
-	FacetOrdering *FacetOrdering `json:"facetOrdering"`
+	FacetOrdering *FacetOrdering `json:"facetOrdering,omitempty"`
+	Redirect      *Redirect      `json:"redirect,omitempty"`
 }
 
 // Facets and facets values ordering rules container
@@ -15,6 +16,11 @@ type FacetOrdering struct {
 	Values map[string]FacetValuesOrder `json:"values,omitempty"`
 }
 
+// Redirect rule container
+type Redirect struct {
+	Url string `json:"url"`
+}
+
 // Facets ordering rule container
 type FacetsOrder struct {
 	// Pinned order of facet lists.


Test patch:
diff --git a/algolia/search/rendering_content_test.go b/algolia/search/rendering_content_test.go
index 50eac4c5..5c543225 100644
--- a/algolia/search/rendering_content_test.go
+++ b/algolia/search/rendering_content_test.go
@@ -26,6 +26,9 @@ func TestUnmarshalRenderingContent(t *testing.T) {
 					"sortRemainingBy": "alpha"
 				}
 			}
+		},
+		"redirect": {
+			"url": "https://algolia.com/doc"
 		}
 	}`
 	var r RenderingContent
@@ -40,6 +43,8 @@ func TestUnmarshalRenderingContent(t *testing.T) {
 	require.Nil(t, r.FacetOrdering.Values["color"].SortRemainingBy)
 
 	require.Nil(t, r.FacetOrdering.Values["size"].Order)
+
+	require.Equal(t, *r.Redirect, Redirect{Url: "https://algolia.com/doc"})
 }
 
 func TestMarshalRenderingContent(t *testing.T) {
@@ -108,6 +113,26 @@ func TestMarshalRenderingContent(t *testing.T) {
 				}
 			}`,
 		},
+		{
+			name: "values with one empty facet value `order`",
+			input: RenderingContent{
+				FacetOrdering: nil,
+			},
+			expected: `{}`,
+		},
+		{
+			name: "values with one empty facet value `order`",
+			input: RenderingContent{
+				Redirect: &Redirect{
+					Url: "https://algolia.com/doc",
+				},
+			},
+			expected: `{
+				"redirect": {
+					"url": "https://algolia.com/doc"
+				}
+			}`,
+		},
 	}
 
 	for _, test := range tests {
Repository: algolia/algoliasearch-client-go
Pull request #740 (2023-08-24 11:33:04)

fix(rules): missing redirect field in RuleConsequence
Previously the SDK would silently drop fields specific to redirect-based rules.  This change adds the missing fields so that it is possible to fetch and re-submit rules without losing the redirect information.

Ideally the SDK would support this natively for all types, but that is a bigger job!

Ant-hem: Could you please update `algolia/search/rule_test.go` and enrich the PR's description?

Patch:
diff --git a/algolia/search/rule.go b/algolia/search/rule.go
index 6f8afc82..43b682ea 100644
--- a/algolia/search/rule.go
+++ b/algolia/search/rule.go
@@ -20,6 +20,7 @@ type Rule struct {
 	ObjectID    string             `json:"objectID,omitempty"`
 	Validity    []TimeRange        `json:"validity,omitempty"`
 	Tags        []string           `json:"tags,omitempty"`
+	Scope       string             `json:"scope,omitempty"`
 }
 
 // TimeRange is a pair of begin/end time.Time used to represent a rule validity
diff --git a/algolia/search/rule_consequence.go b/algolia/search/rule_consequence.go
index 2d8608d0..1333215b 100644
--- a/algolia/search/rule_consequence.go
+++ b/algolia/search/rule_consequence.go
@@ -8,6 +8,11 @@ type RuleConsequence struct {
 	FilterPromotes *opt.FilterPromotesOption `json:"filterPromotes,omitempty"`
 	Hide           []HiddenObject            `json:"hide,omitempty"`
 	UserData       interface{}               `json:"userData,omitempty"`
+	Redirect       *RuleRedirect             `json:"redirect,omitempty"`
+}
+
+type RuleRedirect struct {
+	IndexName string `json:"indexName,omitempty"`
 }
 
 type PromotedObject struct {


Test patch:
diff --git a/algolia/search/rule_test.go b/algolia/search/rule_test.go
index 7267f35e..f888c39e 100644
--- a/algolia/search/rule_test.go
+++ b/algolia/search/rule_test.go
@@ -58,6 +58,17 @@ func TestRule_MarshalJSON(t *testing.T) {
 			},
 			`{"consequence":{},"tags":["visual-editor"]}`,
 		},
+		{
+			Rule{
+				Scope: "redirect",
+				Consequence: RuleConsequence{
+					Redirect: &RuleRedirect{
+						IndexName: "virtual_replica_A",
+					},
+				},
+			},
+			`{"consequence":{"redirect":{"indexName":"virtual_replica_A"}},"scope":"redirect"}`,
+		},
 	} {
 		// Encode the Rule to JSON
 		data, err := json.Marshal(&c.rule)
Repository: mlange-42/arche
Pull request #18 (2023-02-07 09:32:04)

RemEntity panics on dead entity


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f41a7b5..bacfc11 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 ### Other
 
 * Avoid allocation in `World.Has(entity, compID)` (#16)
+* `World.RemEntity(entity)` panics on dead entity, like all other `World` methods (#18)
 
 ## [[v0.1.0]](https://github.com/mlange-42/arche/tree/v0.1.0)
 
diff --git a/ecs/world.go b/ecs/world.go
index 01f4596..c863bc8 100644
--- a/ecs/world.go
+++ b/ecs/world.go
@@ -41,11 +41,7 @@ func (w *World) NewEntity() Entity {
 // RemEntity recycles an entity.
 //
 // Do not use during Query iteration!
-func (w *World) RemEntity(entity Entity) bool {
-	if !w.entityPool.Alive(entity) {
-		return false
-	}
-
+func (w *World) RemEntity(entity Entity) {
 	index := w.entities[entity.id]
 	oldArch := &w.archetypes[index.arch]
 	swapped := oldArch.Remove(int(index.index))
@@ -58,7 +54,6 @@ func (w *World) RemEntity(entity Entity) bool {
 	}
 
 	w.entities[entity.id].arch = -1
-	return true
 }
 
 // Get returns a component for an entity


Test patch:
diff --git a/ecs/world_test.go b/ecs/world_test.go
index 8d43d3a..d3c60dc 100644
--- a/ecs/world_test.go
+++ b/ecs/world_test.go
@@ -17,7 +17,7 @@ func TestWorldEntites(t *testing.T) {
 	assert.Equal(t, 1, int(w.entities[1].index))
 	assert.Equal(t, 2, int(w.entities[2].index))
 
-	assert.True(t, w.RemEntity(Entity{1, 0}))
+	w.RemEntity(Entity{1, 0})
 	assert.False(t, w.Alive(Entity{1, 0}))
 
 	assert.Equal(t, 0, int(w.entities[0].index))
@@ -29,11 +29,13 @@ func TestWorldEntites(t *testing.T) {
 
 	assert.Equal(t, 2, int(w.entities[1].index))
 
-	assert.False(t, w.RemEntity(Entity{1, 0}))
+	w.RemEntity(Entity{2, 0})
+	w.RemEntity(Entity{1, 1})
+	w.RemEntity(Entity{0, 0})
 
-	assert.True(t, w.RemEntity(Entity{2, 0}))
-	assert.True(t, w.RemEntity(Entity{1, 1}))
-	assert.True(t, w.RemEntity(Entity{0, 0}))
+	assert.Panics(t, func() { w.RemEntity(Entity{2, 0}) })
+	assert.Panics(t, func() { w.RemEntity(Entity{1, 1}) })
+	assert.Panics(t, func() { w.RemEntity(Entity{0, 0}) })
 }
 
 func TestWorldComponents(t *testing.T) {
Repository: mlange-42/arche
Pull request #30 (2023-02-07 22:44:04)

Add method Count to type Query
Returns the total number of matched entities.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4700ec0..ecce342 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 ## Features
 
 * Add `Config` to allow for configuration of the world (currently only storage capacity increment) (#28)
+* `Query` has a method `Count()`, reporting the total number of matching entities (#30)
 
 ## [[v0.1.2]](https://github.com/mlange-42/arche/compare/v0.1.1...v0.1.2)
 
diff --git a/ecs/query.go b/ecs/query.go
index b7972d9..8069a83 100644
--- a/ecs/query.go
+++ b/ecs/query.go
@@ -10,16 +10,18 @@ type Query struct {
 	archetypes []archetypeIter
 	index      int
 	done       bool
+	count      int
 	lockBit    uint8
 }
 
 // newQuery creates a new QueryIter
-func newQuery(world *World, arches []archetypeIter, lockBit uint8) Query {
+func newQuery(world *World, arches []archetypeIter, count int, lockBit uint8) Query {
 	return Query{
 		world:      world,
 		archetypes: arches,
 		index:      0,
 		done:       false,
+		count:      count,
 		lockBit:    lockBit,
 	}
 }
@@ -75,6 +77,11 @@ func (q *Query) Close() {
 	q.world.closeQuery(q)
 }
 
+// Count returns the number of matching entities
+func (q *Query) Count() int {
+	return q.count
+}
+
 type archetypeIter struct {
 	Archetype *archetype
 	Length    int
diff --git a/ecs/world.go b/ecs/world.go
index 2773944..9ac79aa 100644
--- a/ecs/world.go
+++ b/ecs/world.go
@@ -226,15 +226,17 @@ func (w *World) Query(comps ...ID) Query {
 	mask := NewMask(comps...)
 	arches := []archetypeIter{}
 	length := len(w.archetypes)
+	count := 0
 	for i := 0; i < length; i++ {
 		arch := &w.archetypes[i]
 		if arch.mask.Contains(mask) {
 			arches = append(arches, newArchetypeIter(arch))
+			count += int(arch.Len())
 		}
 	}
 	lock := w.bitPool.Get()
 	w.locks.Set(ID(lock), true)
-	return newQuery(w, arches, lock)
+	return newQuery(w, arches, count, lock)
 }
 
 // closeQuery closes a query and unlocks the world


Test patch:
diff --git a/ecs/query_test.go b/ecs/query_test.go
index 212b864..8a9e599 100644
--- a/ecs/query_test.go
+++ b/ecs/query_test.go
@@ -36,6 +36,7 @@ func TestQuery(t *testing.T) {
 		cnt++
 	}
 	assert.Equal(t, 2, cnt)
+	assert.Equal(t, 2, q.Count())
 
 	q = w.Query(posID)
 	cnt = 0
@@ -47,6 +48,7 @@ func TestQuery(t *testing.T) {
 		cnt++
 	}
 	assert.Equal(t, 3, cnt)
+	assert.Equal(t, 3, q.Count())
 
 	q = w.Query(rotID)
 	cnt = 0
@@ -60,6 +62,7 @@ func TestQuery(t *testing.T) {
 		cnt++
 	}
 	assert.Equal(t, 4, cnt)
+	assert.Equal(t, 4, q.Count())
 
 	assert.Panics(t, func() { q.Next() })
 	assert.Panics(t, func() { q.Entity() })
Repository: mlange-42/arche
Pull request #63 (2023-02-12 23:51:12)

Fix exclusion filter
Fix ecs Without filter to exclude all components, not any


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd7b966..d1f1aa7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,7 @@
 
 ### Features
 
-* Generic queries support optional, additional and excluded components (#53, #56, #58, #59, #60)
+* Generic queries support optional, additional and excluded components (#53, #56, #58, #59, #60, #63)
 * Logic filters for complex queries (#54, #58, #61)
 
 ### Other
diff --git a/ecs/query.go b/ecs/query.go
index 0e95355..7cfb793 100644
--- a/ecs/query.go
+++ b/ecs/query.go
@@ -48,7 +48,7 @@ type MaskPair struct {
 // Matches matches a filter against a mask
 func (f MaskPair) Matches(bits BitMask) bool {
 	ex := f.Exclude.BitMask
-	return bits.Contains(f.Mask.BitMask) && (ex == 0 || !bits.Contains(ex))
+	return bits.Contains(f.Mask.BitMask) && (ex == 0 || !bits.ContainsAny(ex))
 }
 
 // EntityIter is the interface for iterable queries


Test patch:
diff --git a/ecs/query_test.go b/ecs/query_test.go
index be2d790..1472bea 100644
--- a/ecs/query_test.go
+++ b/ecs/query_test.go
@@ -21,6 +21,7 @@ func TestQuery(t *testing.T) {
 
 	posID := ComponentID[position](&w)
 	rotID := ComponentID[rotation](&w)
+	velID := ComponentID[velocity](&w)
 
 	e0 := w.NewEntity()
 	e1 := w.NewEntity()
@@ -31,7 +32,7 @@ func TestQuery(t *testing.T) {
 	w.Add(e0, posID)
 	w.Add(e1, posID, rotID)
 	w.Add(e2, posID, rotID)
-	w.Add(e3, rotID)
+	w.Add(e3, rotID, velID)
 	w.Add(e4, rotID)
 
 	q := w.Query(All(posID, rotID))
@@ -81,6 +82,15 @@ func TestQuery(t *testing.T) {
 		cnt++
 	}
 	assert.Equal(t, 2, cnt)
+
+	q = w.Query(All(rotID).Without(posID, velID))
+
+	cnt = 0
+	for q.Next() {
+		_ = q.Entity()
+		cnt++
+	}
+	assert.Equal(t, 1, cnt)
 }
 
 func TestInterface(t *testing.T) {
diff --git a/generic/query_generic_test.go b/generic/query_generic_test.go
index 591c2ea..682b068 100644
--- a/generic/query_generic_test.go
+++ b/generic/query_generic_test.go
@@ -23,6 +23,7 @@ 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(e1, 8, &testStruct8{})
 
 	w.Assign(e2, 0, &testStruct0{3})
 	w.Assign(e2, 1, &testStruct1{3})
@@ -43,6 +44,13 @@ func TestQueryOptionalNot(t *testing.T) {
 	}
 	assert.Equal(t, 2, cnt)
 
+	query2 = Query2[testStruct0, testStruct1]().Without(Mask2[testStruct8, testStruct9]()).Build(&w)
+	cnt = 0
+	for query2.Next() {
+		cnt++
+	}
+	assert.Equal(t, 1, cnt)
+
 	query2 = Query2[testStruct0, testStruct1]().With(Mask1[testStruct2]()).Without(Mask1[testStruct9]()).Build(&w)
 	cnt = 0
 	for query2.Next() {
Repository: mlange-42/arche
Pull request #115 (2023-02-23 01:10:24)

More consistent `EntityEvent` values on removed entity
* `EntityEvent` has more consistent values when an entity is removed
  * `EntityEvent.NewMask` is zero
  * `EntityEvent.Removed` is contains all former components
  * `EntityEvent.Current` is `nil`


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 345d5e3..17a236b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,18 @@
 # Changelog
 
-## [[unpublished]](https://github.com/mlange-42/arche/compare/v0.4.2...main)
+## [[unpublished]](https://github.com/mlange-42/arche/compare/v0.4.3...main)
 
 Nothing
 
+## [[v0.4.3]](https://github.com/mlange-42/arche/compare/v0.4.2...v0.4.3)
+
+## Bugfixes
+
+* `EntityEvent` has more consistent values when an entity is removed (#115)
+  * `EntityEvent.NewMask` is zero
+  * `EntityEvent.Removed` is contains all former components
+  * `EntityEvent.Current` is `nil`
+
 ## [[v0.4.2]](https://github.com/mlange-42/arche/compare/v0.4.1...v0.4.2)
 
 ## Other
diff --git a/ecs/world.go b/ecs/world.go
index 77c51ef..eaedeb4 100644
--- a/ecs/world.go
+++ b/ecs/world.go
@@ -164,7 +164,7 @@ func (w *World) RemoveEntity(entity Entity) {
 	oldArch := index.arch
 
 	if w.listener != nil {
-		w.listener(EntityEvent{entity, oldArch.Mask, oldArch.Mask, nil, nil, oldArch.Ids, -1})
+		w.listener(EntityEvent{entity, oldArch.Mask, Mask{}, nil, oldArch.Ids, nil, -1})
 	}
 
 	swapped := oldArch.Remove(index.index)


Test patch:
diff --git a/ecs/world_test.go b/ecs/world_test.go
index ed4fc5c..e511dae 100644
--- a/ecs/world_test.go
+++ b/ecs/world_test.go
@@ -481,8 +481,9 @@ func TestWorldListener(t *testing.T) {
 	assert.Equal(t, EntityEvent{
 		Entity:       e0,
 		OldMask:      All(posID, velID),
-		NewMask:      All(posID, velID),
-		Current:      []ID{posID, velID},
+		NewMask:      Mask{},
+		Removed:      []ID{posID, velID},
+		Current:      nil,
 		AddedRemoved: -1,
 	}, events[len(events)-1])
Repository: mlange-42/arche
Pull request #117 (2023-02-24 13:25:04)

Fix: Ensure panic on closed query


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5008048..ea88222 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 ## [[unpublished]](https://github.com/mlange-42/arche/compare/v0.4.3...main)
 
+## Bugfixes
+
+* `Query.Next`, `Query.Get`, etc. now always panic when called on a closed query (#117)
+
 ### Other
 
 * Update to [go-gameengine-ecs](https://github.com/marioolofo/go-gameengine-ecs) v0.9.0 in benchmarks (#116)
diff --git a/ecs/world.go b/ecs/world.go
index eaedeb4..149828c 100644
--- a/ecs/world.go
+++ b/ecs/world.go
@@ -530,6 +530,7 @@ func (w *World) closeQuery(query *queryIter) {
 	if !w.locks.Get(ID(l)) {
 		panic("unbalanced query unlock")
 	}
+	query.index = -2
 	w.locks.Set(ID(l), false)
 	w.bitPool.Recycle(l)
 }


Test patch:
diff --git a/ecs/query_test.go b/ecs/query_test.go
index 0417347..5d3fe16 100644
--- a/ecs/query_test.go
+++ b/ecs/query_test.go
@@ -106,3 +106,23 @@ func TestQuery(t *testing.T) {
 	}
 	assert.Equal(t, 0, cnt)
 }
+
+func TestQueryClosed(t *testing.T) {
+	w := NewWorld()
+
+	posID := ComponentID[position](&w)
+	rotID := ComponentID[rotation](&w)
+
+	e0 := w.NewEntity()
+	e1 := w.NewEntity()
+	e2 := w.NewEntity()
+
+	w.Add(e0, posID)
+	w.Add(e1, posID, rotID)
+	w.Add(e2, posID, rotID)
+
+	q := w.Query(All(posID, rotID))
+	q.Close()
+	assert.Panics(t, func() { q.Next() })
+	assert.Panics(t, func() { q.Get(posID) })
+}
diff --git a/ecs/world_test.go b/ecs/world_test.go
index e511dae..d7094bc 100644
--- a/ecs/world_test.go
+++ b/ecs/world_test.go
@@ -385,6 +385,21 @@ func TestWorldLock(t *testing.T) {
 	assert.Panics(t, func() { world.Remove(entity, posID) })
 }
 
+func TestWorldNextArchetype(t *testing.T) {
+	world := NewWorld()
+
+	posID := ComponentID[position](&world)
+
+	var entity Entity
+	for i := 0; i < 10; i++ {
+		entity = world.NewEntity()
+		world.Add(entity, posID)
+	}
+
+	world.nextArchetype(All(posID), 0)
+	assert.Panics(t, func() { world.nextArchetype(All(posID), 2) })
+}
+
 func TestWorldStats(t *testing.T) {
 	w := NewWorld()