Commit 9bc2defd authored by Marc Gravell's avatar Marc Gravell

SSCAN, HSCAN, ZSCAN

parent 9b5cdfe2
......@@ -11,3 +11,4 @@ Mono/
*.sln.ide
*.rdb
*.orig
redis-cli.exe
\ No newline at end of file
......@@ -16,3 +16,4 @@ Mono/
*.sln.ide
*.rdb
*.orig
redis-cli.exe
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
namespace StackExchange.Redis.Tests
{
[TestFixture]
public class Scans : TestBase
{
protected override string GetConfiguration()
{
return "ubuntu";
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void SetScan(bool supported)
{
string[] disabledCommands = supported ? null : new[] { "sscan" };
using(var conn = Create(disabledCommands: disabledCommands))
{
RedisKey key = Me();
var db = conn.GetDatabase();
db.KeyDelete(key);
db.SetAdd(key, "a");
db.SetAdd(key, "b");
db.SetAdd(key, "c");
var arr = db.SetScan(key).ToArray();
Assert.AreEqual(3, arr.Length);
Assert.IsTrue(arr.Contains("a"), "a");
Assert.IsTrue(arr.Contains("b"), "b");
Assert.IsTrue(arr.Contains("c"), "c");
}
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void SortedSetScan(bool supported)
{
string[] disabledCommands = supported ? null : new[] { "zscan" };
using (var conn = Create(disabledCommands: disabledCommands))
{
RedisKey key = Me();
var db = conn.GetDatabase();
db.KeyDelete(key);
db.SortedSetAdd(key, "a", 1);
db.SortedSetAdd(key, "b", 2);
db.SortedSetAdd(key, "c", 3);
var arr = db.SortedSetScan(key).ToArray();
Assert.AreEqual(3, arr.Length);
Assert.IsTrue(arr.Any(x => x.Key == "a" && x.Value == 1), "a");
Assert.IsTrue(arr.Any(x => x.Key == "b" && x.Value == 2), "b");
Assert.IsTrue(arr.Any(x => x.Key == "c" && x.Value == 3), "c");
}
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void HashScan(bool supported)
{
string[] disabledCommands = supported ? null : new[] { "hscan" };
using (var conn = Create(disabledCommands: disabledCommands))
{
RedisKey key = Me();
var db = conn.GetDatabase();
db.KeyDelete(key);
db.HashSet(key, "a", "1");
db.HashSet(key, "b", "2");
db.HashSet(key, "c", "3");
var arr = db.HashScan(key).ToArray();
Assert.AreEqual(3, arr.Length);
Assert.IsTrue(arr.Any(x => x.Key == "a" && x.Value == "1"), "a");
Assert.IsTrue(arr.Any(x => x.Key == "b" && x.Value == "2"), "b");
Assert.IsTrue(arr.Any(x => x.Key == "c" && x.Value == "3"), "c");
}
}
}
}
......@@ -81,6 +81,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PubSub.cs" />
<Compile Include="RealWorld.cs" />
<Compile Include="Scans.cs" />
<Compile Include="Scripting.cs" />
<Compile Include="Secure.cs" />
<Compile Include="Sets.cs" />
......
......@@ -94,5 +94,13 @@ internal static Exception ConnectionFailure(bool includeDetail, ConnectionFailur
if (includeDetail) AddDetail(ex, null, server, null);
return ex;
}
internal static Exception NotSupported(bool includeDetail, RedisCommand command)
{
string s = GetLabel(includeDetail, command, null);
var ex = new RedisCommandException("Command is not available on your server: " + s);
if (includeDetail) AddDetail(ex, null, null, s);
return ex;
}
}
}
......@@ -507,11 +507,26 @@ public interface IDatabase : IRedis, IDatabaseAsync
long SetRemove(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None);
/// <summary>
/// The SSCAN command is used to incrementally iterate over a collection of elements.
/// The SSCAN command is used to incrementally iterate over set
/// </summary>
/// <returns>yields all elements of the set.</returns>
/// <remarks>http://redis.io/commands/sscan</remarks>
IEnumerable<RedisValue> SetScan(RedisKey key, RedisValue pattern = default(RedisValue), int pageSize = RedisDatabase.SetScanIterator.DefaultPageSize, CommandFlags flags = CommandFlags.None);
IEnumerable<RedisValue> SetScan(RedisKey key, RedisValue pattern = default(RedisValue), int pageSize = RedisDatabase.ScanIterator.DefaultPageSize, CommandFlags flags = CommandFlags.None);
/// <summary>
/// The ZSCAN command is used to incrementally iterate over a sorted set
/// </summary>
/// <returns>yields all elements of the sorted set.</returns>
/// <remarks>http://redis.io/commands/zscan</remarks>
IEnumerable<KeyValuePair<RedisValue, double>> SortedSetScan(RedisKey key, RedisValue pattern = default(RedisValue), int pageSize = RedisDatabase.ScanIterator.DefaultPageSize, CommandFlags flags = CommandFlags.None);
/// <summary>
/// The HSCAN command is used to incrementally iterate over a hash
/// </summary>
/// <returns>yields all elements of the hash.</returns>
/// <remarks>http://redis.io/commands/hscan</remarks>
IEnumerable<KeyValuePair<RedisValue, RedisValue>> HashScan(RedisKey key, RedisValue pattern = default(RedisValue), int pageSize = RedisDatabase.ScanIterator.DefaultPageSize, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Sorts a list, set or sorted set (numerically or alphabetically, ascending by default); By default, the elements themselves are compared, but the values can also be
/// used to perform external key-lookups using the <c>by</c> parameter. By default, the elements themselves are returned, but external key-lookups (one or many) can
......
......@@ -75,10 +75,10 @@ abstract class ResultProcessor
public static readonly TimeSpanProcessor
TimeSpanFromMilliseconds = new TimeSpanProcessor(true),
TimeSpanFromSeconds = new TimeSpanProcessor(false);
public static readonly ResultProcessor<KeyValuePair<RedisValue, RedisValue>[]>
public static readonly ValuePairInterleavedProcessor
ValuePairInterleaved = new ValuePairInterleavedProcessor();
public static readonly ResultProcessor<KeyValuePair<RedisValue, double>[]>
public static readonly SortedSetWithScoresProcessor
SortedSetWithScores = new SortedSetWithScoresProcessor();
public static readonly ResultProcessor<RedisResult>
......@@ -928,26 +928,39 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
}
}
sealed class ValuePairInterleavedProcessor : ValuePairInterleavedProcessorBase<RedisValue, RedisValue>
internal sealed class ValuePairInterleavedProcessor : ValuePairInterleavedProcessorBase<RedisValue, RedisValue>
{
protected override RedisValue ParseKey(RawResult key) { return key.AsRedisValue(); }
protected override RedisValue ParseValue(RawResult key) { return key.AsRedisValue(); }
}
abstract class ValuePairInterleavedProcessorBase<TKey, TValue> : ResultProcessor<KeyValuePair<TKey, TValue>[]>
internal sealed class SortedSetWithScoresProcessor : ValuePairInterleavedProcessorBase<RedisValue, double>
{
protected override RedisValue ParseKey(RawResult key) { return key.AsRedisValue(); }
protected override double ParseValue(RawResult value)
{
double val;
return value.TryGetDouble(out val) ? val: double.NaN;
}
}
internal abstract class ValuePairInterleavedProcessorBase<TKey, TValue> : ResultProcessor<KeyValuePair<TKey, TValue>[]>
{
static readonly KeyValuePair<TKey, TValue>[] nix = new KeyValuePair<TKey, TValue>[0];
protected abstract TKey ParseKey(RawResult key);
protected abstract TValue ParseValue(RawResult value);
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
public bool TryParse(RawResult result, out KeyValuePair<TKey, TValue>[] pairs)
{
switch (result.Type)
{
case ResultType.MultiBulk:
var arr = result.GetItems();
if (arr == null)
{
pairs = null;
}
else
{
int count = arr.Length / 2;
KeyValuePair<TKey, TValue>[] pairs;
if (count == 0)
{
pairs = nix;
......@@ -963,30 +976,20 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
pairs[i] = new KeyValuePair<TKey, TValue>(setting, value);
}
}
SetResult(message, pairs);
}
return true;
default:
pairs = null;
return false;
}
}
}
private class SortedSetWithScoresProcessor : ResultProcessor<KeyValuePair<RedisValue, double>[]>
{
protected abstract TKey ParseKey(RawResult key);
protected abstract TValue ParseValue(RawResult value);
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
{
if(result.Type == ResultType.MultiBulk)
{
var items = result.GetItems();
var arr = new KeyValuePair<RedisValue, double>[items.Length / 2];
int index = 0;
for(int i = 0; i < arr.Length; i++)
KeyValuePair<TKey, TValue>[] arr;
if(TryParse(result, out arr))
{
var member = items[index++].AsRedisValue();
double score;
if (!items[index++].TryGetDouble(out score)) return false;
arr[i] = new KeyValuePair<RedisValue, double>(member, score);
}
SetResult(message, arr);
return true;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment