Skip to content

Commit 850ccfd

Browse files
committed
more vectorsets
1 parent 8ed38d5 commit 850ccfd

3 files changed

Lines changed: 221 additions & 0 deletions

File tree

src/StackExchange.Redis/ResultProcessor.Lease.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ private abstract class FlattenedLeaseProcessor<T> : ResultProcessor<Lease<T>?>
6565
{
6666
protected virtual long GetArrayLength(in RawResult array) => array.GetItems().Length;
6767

68+
protected virtual long GetArrayLength(in RespReader reader) => reader.AggregateLength();
69+
6870
protected virtual bool TryReadOne(ref Sequence<RawResult>.Enumerator reader, out T value)
6971
{
7072
if (reader.MoveNext())
@@ -81,6 +83,83 @@ protected virtual bool TryReadOne(in RawResult result, out T value)
8183
return false;
8284
}
8385

86+
protected virtual bool TryReadOne(ref RespReader reader, out T value)
87+
{
88+
value = default!;
89+
return false;
90+
}
91+
92+
protected override bool SetResultCore(PhysicalConnection connection, Message message, ref RespReader reader)
93+
{
94+
if (!reader.IsAggregate)
95+
{
96+
return false; // not an array
97+
}
98+
99+
// deal with null
100+
if (reader.IsNull)
101+
{
102+
SetResult(message, Lease<T>.Empty);
103+
return true;
104+
}
105+
106+
// First pass: count total elements across all nested arrays
107+
long totalLength = 0;
108+
var iter = reader.AggregateChildren();
109+
while (iter.MoveNext())
110+
{
111+
if (iter.Value.IsAggregate && !iter.Value.IsNull)
112+
{
113+
totalLength += GetArrayLength(in iter.Value);
114+
}
115+
}
116+
117+
if (totalLength == 0)
118+
{
119+
SetResult(message, Lease<T>.Empty);
120+
return true;
121+
}
122+
123+
// Second pass: fill the lease
124+
var lease = Lease<T>.Create(checked((int)totalLength), clear: false);
125+
int index = 0;
126+
var target = lease.Span;
127+
128+
try
129+
{
130+
iter = reader.AggregateChildren();
131+
while (iter.MoveNext())
132+
{
133+
if (iter.Value.IsAggregate && !iter.Value.IsNull)
134+
{
135+
var childReader = iter.Value;
136+
while (childReader.TryMoveNext() && index < target.Length)
137+
{
138+
if (!TryReadOne(ref childReader, out target[index]))
139+
{
140+
lease.Dispose();
141+
return false;
142+
}
143+
index++;
144+
}
145+
}
146+
}
147+
148+
if (index == totalLength)
149+
{
150+
SetResult(message, lease);
151+
return true;
152+
}
153+
lease.Dispose(); // failed to fill?
154+
return false;
155+
}
156+
catch
157+
{
158+
lease.Dispose();
159+
throw;
160+
}
161+
}
162+
84163
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
85164
{
86165
if (result.Resp2TypeArray != ResultType.Array)

src/StackExchange.Redis/ResultProcessor.VectorSets.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,27 @@ private sealed class VectorSetLinksWithScoresProcessor : FlattenedLeaseProcessor
2121
{
2222
protected override long GetArrayLength(in RawResult array) => array.GetItems().Length / 2;
2323

24+
protected override long GetArrayLength(in RespReader reader) => reader.AggregateLength() / 2;
25+
26+
protected override bool TryReadOne(ref RespReader reader, out VectorSetLink value)
27+
{
28+
if (!reader.IsScalar)
29+
{
30+
value = default;
31+
return false;
32+
}
33+
34+
var member = reader.ReadRedisValue();
35+
if (!reader.TryMoveNext() || !reader.IsScalar || !reader.TryReadDouble(out var score))
36+
{
37+
value = default;
38+
return false;
39+
}
40+
41+
value = new VectorSetLink(member, score);
42+
return true;
43+
}
44+
2445
protected override bool TryReadOne(ref Sequence<RawResult>.Enumerator reader, out VectorSetLink value)
2546
{
2647
if (reader.MoveNext())
@@ -40,6 +61,18 @@ protected override bool TryReadOne(ref Sequence<RawResult>.Enumerator reader, ou
4061

4162
private sealed class VectorSetLinksProcessor : FlattenedLeaseProcessor<RedisValue>
4263
{
64+
protected override bool TryReadOne(ref RespReader reader, out RedisValue value)
65+
{
66+
if (!reader.IsScalar)
67+
{
68+
value = default;
69+
return false;
70+
}
71+
72+
value = reader.ReadRedisValue();
73+
return true;
74+
}
75+
4376
protected override bool TryReadOne(in RawResult result, out RedisValue value)
4477
{
4578
value = result.AsRedisValue();

tests/StackExchange.Redis.Tests/ResultProcessorUnitTests/VectorSet.cs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,113 @@ public void VectorSetInfo_SkipsNonScalarValues()
7878
Assert.Equal(VectorSetQuantization.Unknown, result.Value.Quantization);
7979
Assert.Equal(0, result.Value.Dimension);
8080
}
81+
82+
[Fact]
83+
public void VectorSetLinks_EmptyArray()
84+
{
85+
// VLINKS returns empty array
86+
var resp = "*0\r\n";
87+
var processor = ResultProcessor.VectorSetLinks;
88+
using var result = Execute(resp, processor);
89+
90+
Assert.NotNull(result);
91+
Assert.Equal(0, result.Length);
92+
}
93+
94+
[Theory]
95+
[InlineData("*-1\r\n")] // null array (RESP2)
96+
[InlineData("_\r\n")] // null (RESP3)
97+
public void VectorSetLinks_NullArray(string resp)
98+
{
99+
var processor = ResultProcessor.VectorSetLinks;
100+
using var result = Execute(resp, processor);
101+
102+
Assert.NotNull(result);
103+
Assert.Equal(0, result.Length);
104+
}
105+
106+
[Fact]
107+
public void VectorSetLinks_SingleNestedArray()
108+
{
109+
// VLINKS returns [[element1]]
110+
var resp = "*1\r\n*1\r\n$8\r\nelement1\r\n";
111+
var processor = ResultProcessor.VectorSetLinks;
112+
using var result = Execute(resp, processor);
113+
114+
Assert.NotNull(result);
115+
Assert.Equal(1, result.Length);
116+
Assert.Equal("element1", result.Span[0].ToString());
117+
}
118+
119+
[Fact]
120+
public void VectorSetLinks_MultipleNestedArrays()
121+
{
122+
// VLINKS returns [[element1], [element2, element3], [element4]]
123+
var resp = "*3\r\n*1\r\n$8\r\nelement1\r\n*2\r\n$8\r\nelement2\r\n$8\r\nelement3\r\n*1\r\n$8\r\nelement4\r\n";
124+
var processor = ResultProcessor.VectorSetLinks;
125+
using var result = Execute(resp, processor);
126+
127+
Assert.NotNull(result);
128+
Assert.Equal(4, result.Length);
129+
Assert.Equal("element1", result.Span[0].ToString());
130+
Assert.Equal("element2", result.Span[1].ToString());
131+
Assert.Equal("element3", result.Span[2].ToString());
132+
Assert.Equal("element4", result.Span[3].ToString());
133+
}
134+
135+
[Fact]
136+
public void VectorSetLinksWithScores_EmptyArray()
137+
{
138+
// VLINKS WITHSCORES returns empty array
139+
var resp = "*0\r\n";
140+
var processor = ResultProcessor.VectorSetLinksWithScores;
141+
using var result = Execute(resp, processor);
142+
143+
Assert.NotNull(result);
144+
Assert.Equal(0, result.Length);
145+
}
146+
147+
[Theory]
148+
[InlineData("*-1\r\n")] // null array (RESP2)
149+
[InlineData("_\r\n")] // null (RESP3)
150+
public void VectorSetLinksWithScores_NullArray(string resp)
151+
{
152+
var processor = ResultProcessor.VectorSetLinksWithScores;
153+
using var result = Execute(resp, processor);
154+
155+
Assert.NotNull(result);
156+
Assert.Equal(0, result.Length);
157+
}
158+
159+
[Fact]
160+
public void VectorSetLinksWithScores_SingleNestedArray()
161+
{
162+
// VLINKS WITHSCORES returns [[element1, score1]]
163+
var resp = "*1\r\n*2\r\n$8\r\nelement1\r\n$3\r\n1.5\r\n";
164+
var processor = ResultProcessor.VectorSetLinksWithScores;
165+
using var result = Execute(resp, processor);
166+
167+
Assert.NotNull(result);
168+
Assert.Equal(1, result.Length);
169+
Assert.Equal("element1", result.Span[0].Member.ToString());
170+
Assert.Equal(1.5, result.Span[0].Score);
171+
}
172+
173+
[Fact]
174+
public void VectorSetLinksWithScores_MultipleNestedArrays()
175+
{
176+
// VLINKS WITHSCORES returns [[element1, score1], [element2, score2], [element3, score3]]
177+
var resp = "*3\r\n*2\r\n$8\r\nelement1\r\n$3\r\n1.5\r\n*2\r\n$8\r\nelement2\r\n$3\r\n2.5\r\n*2\r\n$8\r\nelement3\r\n$3\r\n3.5\r\n";
178+
var processor = ResultProcessor.VectorSetLinksWithScores;
179+
using var result = Execute(resp, processor);
180+
181+
Assert.NotNull(result);
182+
Assert.Equal(3, result.Length);
183+
Assert.Equal("element1", result.Span[0].Member.ToString());
184+
Assert.Equal(1.5, result.Span[0].Score);
185+
Assert.Equal("element2", result.Span[1].Member.ToString());
186+
Assert.Equal(2.5, result.Span[1].Score);
187+
Assert.Equal("element3", result.Span[2].Member.ToString());
188+
Assert.Equal(3.5, result.Span[2].Score);
189+
}
81190
}

0 commit comments

Comments
 (0)