Commit d4be9a3b authored by Marc Gravell's avatar Marc Gravell

Implement ZUNIONSTORE and ZINTERSTORE

parent e4370fa9
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
<Reference Include="System.IO.Compression" /> <Reference Include="System.IO.Compression" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="StackExchange\Redis\Aggregate.cs" />
<Compile Include="StackExchange\Redis\RedisChannel.cs" /> <Compile Include="StackExchange\Redis\RedisChannel.cs" />
<Compile Include="StackExchange\Redis\Bitwise.cs" /> <Compile Include="StackExchange\Redis\Bitwise.cs" />
<Compile Include="StackExchange\Redis\ClientFlags.cs" /> <Compile Include="StackExchange\Redis\ClientFlags.cs" />
......
namespace StackExchange.Redis
{
/// <summary>
/// Specifies how elements should be aggregated when combining sorted sets
/// </summary>
public enum Aggregate
{
/// <summary>
/// The values of the combined elements are added
/// </summary>
Sum,
/// <summary>
/// The least value of the combined elements is used
/// </summary>
Min,
/// <summary>
/// The greatest value of the combined elements is used
/// </summary>
Max
}
}
...@@ -456,15 +456,6 @@ public interface IDatabase : IRedis, IDatabaseAsync ...@@ -456,15 +456,6 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks>http://redis.io/commands/smembers</remarks> /// <remarks>http://redis.io/commands/smembers</remarks>
RedisValue[] SetMembers(RedisKey key, CommandFlags flags = CommandFlags.None); RedisValue[] SetMembers(RedisKey key, CommandFlags flags = CommandFlags.None);
/// <summary>
/// The SSCAN command is used to incrementally iterate over a collection of elements.
/// </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);
/// <summary> /// <summary>
/// Move member from the set at source to the set at destination. This operation is atomic. In every given moment the element will appear to be a member of source or destination for other clients. /// Move member from the set at source to the set at destination. This operation is atomic. In every given moment the element will appear to be a member of source or destination for other clients.
/// When the specified element already exists in the destination set, it is only removed from the source set. /// When the specified element already exists in the destination set, it is only removed from the source set.
...@@ -509,6 +500,36 @@ public interface IDatabase : IRedis, IDatabaseAsync ...@@ -509,6 +500,36 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks>http://redis.io/commands/srem</remarks> /// <remarks>http://redis.io/commands/srem</remarks>
long SetRemove(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None); long SetRemove(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None);
/// <summary>
/// The SSCAN command is used to incrementally iterate over a collection of elements.
/// </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);
/// <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
/// be performed instead by specifying the <c>get</c> parameter (note that <c>#</c> specifies the element itself, when used in <c>get</c>).
/// Referring to the <a href="http://redis.io/commands/sort">redis SORT documentation </a> for examples is recommended. When used in hashes, <c>by</c> and <c>get</c>
/// can be used to specify fields using <c>-&gt;</c> notation (again, refer to redis documentation).
/// </summary>
/// <remarks>http://redis.io/commands/sort</remarks>
/// <returns>Returns the sorted elements, or the external values if <c>get</c> is specified</returns>
[IgnoreNamePrefix]
RedisValue[] Sort(RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, 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
/// be performed instead by specifying the <c>get</c> parameter (note that <c>#</c> specifies the element itself, when used in <c>get</c>).
/// Referring to the <a href="http://redis.io/commands/sort">redis SORT documentation </a> for examples is recommended. When used in hashes, <c>by</c> and <c>get</c>
/// can be used to specify fields using <c>-&gt;</c> notation (again, refer to redis documentation).
/// </summary>
/// <remarks>http://redis.io/commands/sort</remarks>
/// <returns>Returns the number of elements stored in the new list</returns>
[IgnoreNamePrefix]
long SortAndStore(RedisKey destination, RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None);
/// <summary> /// <summary>
/// Adds the specified member with the specified score to the sorted set stored at key. If the specified member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering. /// Adds the specified member with the specified score to the sorted set stored at key. If the specified member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering.
/// </summary> /// </summary>
...@@ -523,6 +544,24 @@ public interface IDatabase : IRedis, IDatabaseAsync ...@@ -523,6 +544,24 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks>http://redis.io/commands/zadd</remarks> /// <remarks>http://redis.io/commands/zadd</remarks>
long SortedSetAdd(RedisKey key, KeyValuePair<RedisValue, double>[] values, CommandFlags flags = CommandFlags.None); long SortedSetAdd(RedisKey key, KeyValuePair<RedisValue, double>[] values, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Computes a set operation over two sorted sets, and stores the result in destination, optionally performing
/// a specific aggregation (defaults to sum)
/// </summary>
/// <remarks>http://redis.io/commands/zunionstore</remarks>
/// <remarks>http://redis.io/commands/zinterstore</remarks>
/// <returns>the number of elements in the resulting sorted set at destination</returns>
long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Computes a set operation over multiple sorted sets (optionally using per-set weights), and stores the result in destination, optionally performing
/// a specific aggregation (defaults to sum)
/// </summary>
/// <remarks>http://redis.io/commands/zunionstore</remarks>
/// <remarks>http://redis.io/commands/zinterstore</remarks>
/// <returns>the number of elements in the resulting sorted set at destination</returns>
long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey[] keys, double[] weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None);
/// <summary> /// <summary>
/// Decrements the score of member in the sorted set stored at key by decrement. If member does not exist in the sorted set, it is added with -decrement as its score (as if its previous score was 0.0). /// Decrements the score of member in the sorted set stored at key by decrement. If member does not exist in the sorted set, it is added with -decrement as its score (as if its previous score was 0.0).
/// </summary> /// </summary>
...@@ -729,6 +768,13 @@ public interface IDatabase : IRedis, IDatabaseAsync ...@@ -729,6 +768,13 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <returns> the old value stored at key, or nil when key did not exist.</returns> /// <returns> the old value stored at key, or nil when key did not exist.</returns>
RedisValue StringGetSet(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None); RedisValue StringGetSet(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Get the value of key. If the key does not exist the special value nil is returned. An error is returned if the value stored at key is not a string, because GET only handles string values.
/// </summary>
/// <returns>the value of key, or nil when key does not exist.</returns>
/// <remarks>http://redis.io/commands/get</remarks>
RedisValueWithExpiry StringGetWithExpiry(RedisKey key, CommandFlags flags = CommandFlags.None);
/// <summary> /// <summary>
/// Increments the number stored at key by increment. If the key does not exist, it is set to 0 before performing the operation. An error is returned if the key contains a value of the wrong type or contains a string that is not representable as integer. This operation is limited to 64 bit signed integers. /// Increments the number stored at key by increment. If the key does not exist, it is set to 0 before performing the operation. An error is returned if the key contains a value of the wrong type or contains a string that is not representable as integer. This operation is limited to 64 bit signed integers.
/// </summary> /// </summary>
...@@ -777,37 +823,5 @@ public interface IDatabase : IRedis, IDatabaseAsync ...@@ -777,37 +823,5 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <returns>the length of the string after it was modified by the command.</returns> /// <returns>the length of the string after it was modified by the command.</returns>
/// <remarks>http://redis.io/commands/setrange</remarks> /// <remarks>http://redis.io/commands/setrange</remarks>
RedisValue StringSetRange(RedisKey key, long offset, RedisValue value, CommandFlags flags = CommandFlags.None); RedisValue StringSetRange(RedisKey key, long offset, RedisValue value, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Get the value of key. If the key does not exist the special value nil is returned. An error is returned if the value stored at key is not a string, because GET only handles string values.
/// </summary>
/// <returns>the value of key, or nil when key does not exist.</returns>
/// <remarks>http://redis.io/commands/get</remarks>
RedisValueWithExpiry StringGetWithExpiry(RedisKey key, 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
/// be performed instead by specifying the <c>get</c> parameter (note that <c>#</c> specifies the element itself, when used in <c>get</c>).
/// Referring to the <a href="http://redis.io/commands/sort">redis SORT documentation </a> for examples is recommended. When used in hashes, <c>by</c> and <c>get</c>
/// can be used to specify fields using <c>-&gt;</c> notation (again, refer to redis documentation).
/// </summary>
/// <remarks>http://redis.io/commands/sort</remarks>
/// <returns>Returns the sorted elements, or the external values if <c>get</c> is specified</returns>
[IgnoreNamePrefix]
RedisValue[] Sort(RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, 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
/// be performed instead by specifying the <c>get</c> parameter (note that <c>#</c> specifies the element itself, when used in <c>get</c>).
/// Referring to the <a href="http://redis.io/commands/sort">redis SORT documentation </a> for examples is recommended. When used in hashes, <c>by</c> and <c>get</c>
/// can be used to specify fields using <c>-&gt;</c> notation (again, refer to redis documentation).
/// </summary>
/// <remarks>http://redis.io/commands/sort</remarks>
/// <returns>Returns the number of elements stored in the new list</returns>
[IgnoreNamePrefix]
long SortAndStore(RedisKey destination, RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None);
} }
} }
\ No newline at end of file
...@@ -489,6 +489,30 @@ public interface IDatabaseAsync : IRedisAsync ...@@ -489,6 +489,30 @@ public interface IDatabaseAsync : IRedisAsync
Task<long> SetRemoveAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None); Task<long> SetRemoveAsync(RedisKey key, RedisValue[] values, 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
/// be performed instead by specifying the <c>get</c> parameter (note that <c>#</c> specifies the element itself, when used in <c>get</c>).
/// Referring to the <a href="http://redis.io/commands/sort">redis SORT documentation </a> for examples is recommended. When used in hashes, <c>by</c> and <c>get</c>
/// can be used to specify fields using <c>-&gt;</c> notation (again, refer to redis documentation).
/// </summary>
/// <remarks>http://redis.io/commands/sort</remarks>
/// <returns>Returns the number of elements stored in the new list</returns>
[IgnoreNamePrefix]
Task<long> SortAndStoreAsync(RedisKey destination, RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, 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
/// be performed instead by specifying the <c>get</c> parameter (note that <c>#</c> specifies the element itself, when used in <c>get</c>).
/// Referring to the <a href="http://redis.io/commands/sort">redis SORT documentation </a> for examples is recommended. When used in hashes, <c>by</c> and <c>get</c>
/// can be used to specify fields using <c>-&gt;</c> notation (again, refer to redis documentation).
/// </summary>
/// <remarks>http://redis.io/commands/sort</remarks>
/// <returns>Returns the sorted elements, or the external values if <c>get</c> is specified</returns>
[IgnoreNamePrefix]
Task<RedisValue[]> SortAsync(RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None);
/// <summary> /// <summary>
/// Adds the specified member with the specified score to the sorted set stored at key. If the specified member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering. /// Adds the specified member with the specified score to the sorted set stored at key. If the specified member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering.
/// </summary> /// </summary>
...@@ -503,6 +527,24 @@ public interface IDatabaseAsync : IRedisAsync ...@@ -503,6 +527,24 @@ public interface IDatabaseAsync : IRedisAsync
/// <remarks>http://redis.io/commands/zadd</remarks> /// <remarks>http://redis.io/commands/zadd</remarks>
Task<long> SortedSetAddAsync(RedisKey key, KeyValuePair<RedisValue, double>[] values, CommandFlags flags = CommandFlags.None); Task<long> SortedSetAddAsync(RedisKey key, KeyValuePair<RedisValue, double>[] values, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Computes a set operation over two sorted sets, and stores the result in destination, optionally performing
/// a specific aggregation (defaults to sum)
/// </summary>
/// <remarks>http://redis.io/commands/zunionstore</remarks>
/// <remarks>http://redis.io/commands/zinterstore</remarks>
/// <returns>the number of elements in the resulting sorted set at destination</returns>
Task<long> SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Computes a set operation over multiple sorted sets (optionally using per-set weights), and stores the result in destination, optionally performing
/// a specific aggregation (defaults to sum)
/// </summary>
/// <remarks>http://redis.io/commands/zunionstore</remarks>
/// <remarks>http://redis.io/commands/zinterstore</remarks>
/// <returns>the number of elements in the resulting sorted set at destination</returns>
Task<long> SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey[] keys, double[] weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None);
/// <summary> /// <summary>
/// Decrements the score of member in the sorted set stored at key by decrement. If member does not exist in the sorted set, it is added with -decrement as its score (as if its previous score was 0.0). /// Decrements the score of member in the sorted set stored at key by decrement. If member does not exist in the sorted set, it is added with -decrement as its score (as if its previous score was 0.0).
/// </summary> /// </summary>
...@@ -705,6 +747,13 @@ public interface IDatabaseAsync : IRedisAsync ...@@ -705,6 +747,13 @@ public interface IDatabaseAsync : IRedisAsync
/// <returns> the old value stored at key, or nil when key did not exist.</returns> /// <returns> the old value stored at key, or nil when key did not exist.</returns>
Task<RedisValue> StringGetSetAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None); Task<RedisValue> StringGetSetAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Get the value of key. If the key does not exist the special value nil is returned. An error is returned if the value stored at key is not a string, because GET only handles string values.
/// </summary>
/// <returns>the value of key, or nil when key does not exist.</returns>
/// <remarks>http://redis.io/commands/get</remarks>
Task<RedisValueWithExpiry> StringGetWithExpiryAsync(RedisKey key, CommandFlags flags = CommandFlags.None);
/// <summary> /// <summary>
/// Increments the number stored at key by increment. If the key does not exist, it is set to 0 before performing the operation. An error is returned if the key contains a value of the wrong type or contains a string that is not representable as integer. This operation is limited to 64 bit signed integers. /// Increments the number stored at key by increment. If the key does not exist, it is set to 0 before performing the operation. An error is returned if the key contains a value of the wrong type or contains a string that is not representable as integer. This operation is limited to 64 bit signed integers.
/// </summary> /// </summary>
...@@ -752,38 +801,6 @@ public interface IDatabaseAsync : IRedisAsync ...@@ -752,38 +801,6 @@ public interface IDatabaseAsync : IRedisAsync
/// <returns>the length of the string after it was modified by the command.</returns> /// <returns>the length of the string after it was modified by the command.</returns>
/// <remarks>http://redis.io/commands/setrange</remarks> /// <remarks>http://redis.io/commands/setrange</remarks>
Task<RedisValue> StringSetRangeAsync(RedisKey key, long offset, RedisValue value, CommandFlags flags = CommandFlags.None); Task<RedisValue> StringSetRangeAsync(RedisKey key, long offset, RedisValue value, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Get the value of key. If the key does not exist the special value nil is returned. An error is returned if the value stored at key is not a string, because GET only handles string values.
/// </summary>
/// <returns>the value of key, or nil when key does not exist.</returns>
/// <remarks>http://redis.io/commands/get</remarks>
Task<RedisValueWithExpiry> StringGetWithExpiryAsync(RedisKey key, 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
/// be performed instead by specifying the <c>get</c> parameter (note that <c>#</c> specifies the element itself, when used in <c>get</c>).
/// Referring to the <a href="http://redis.io/commands/sort">redis SORT documentation </a> for examples is recommended. When used in hashes, <c>by</c> and <c>get</c>
/// can be used to specify fields using <c>-&gt;</c> notation (again, refer to redis documentation).
/// </summary>
/// <remarks>http://redis.io/commands/sort</remarks>
/// <returns>Returns the sorted elements, or the external values if <c>get</c> is specified</returns>
[IgnoreNamePrefix]
Task<RedisValue[]> SortAsync(RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, 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
/// be performed instead by specifying the <c>get</c> parameter (note that <c>#</c> specifies the element itself, when used in <c>get</c>).
/// Referring to the <a href="http://redis.io/commands/sort">redis SORT documentation </a> for examples is recommended. When used in hashes, <c>by</c> and <c>get</c>
/// can be used to specify fields using <c>-&gt;</c> notation (again, refer to redis documentation).
/// </summary>
/// <remarks>http://redis.io/commands/sort</remarks>
/// <returns>Returns the number of elements stored in the new list</returns>
[IgnoreNamePrefix]
Task<long> SortAndStoreAsync(RedisKey destination, RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None);
} }
...@@ -792,20 +809,21 @@ public interface IDatabaseAsync : IRedisAsync ...@@ -792,20 +809,21 @@ public interface IDatabaseAsync : IRedisAsync
/// </summary> /// </summary>
public struct RedisValueWithExpiry public struct RedisValueWithExpiry
{ {
private readonly RedisValue value;
private readonly TimeSpan? expiry; private readonly TimeSpan? expiry;
private readonly RedisValue value;
internal RedisValueWithExpiry(RedisValue value, TimeSpan? expiry) internal RedisValueWithExpiry(RedisValue value, TimeSpan? expiry)
{ {
this.value = value; this.value = value;
this.expiry = expiry; this.expiry = expiry;
} }
/// <summary> /// <summary>
/// The value of this record
/// </summary>
public RedisValue Value { get { return value; } }
/// <summary>
/// The expiry of this record /// The expiry of this record
/// </summary> /// </summary>
public TimeSpan? Expiry { get { return expiry; } } public TimeSpan? Expiry { get { return expiry; } }
/// <summary>
/// The value of this record
/// </summary>
public RedisValue Value { get { return value; } }
} }
} }
...@@ -854,6 +854,48 @@ public Task<long> SetRemoveAsync(RedisKey key, RedisValue[] values, CommandFlags ...@@ -854,6 +854,48 @@ public Task<long> SetRemoveAsync(RedisKey key, RedisValue[] values, CommandFlags
return ExecuteAsync(msg, ResultProcessor.Int64); return ExecuteAsync(msg, ResultProcessor.Int64);
} }
public IEnumerable<RedisValue> SetScan(RedisKey key, RedisValue pattern = default(RedisValue), int pageSize = RedisDatabase.SetScanIterator.DefaultPageSize, CommandFlags flags = CommandFlags.None)
{
if (pageSize <= 0) throw new ArgumentOutOfRangeException("pageSize");
if (SetScanIterator.IsNil(pattern)) pattern = (byte[])null;
multiplexer.CommandMap.AssertAvailable(RedisCommand.SCAN);
ServerEndPoint server;
var features = GetFeatures(Db, key, flags, out server);
if (!features.Scan)
{
if (pattern.IsNull)
{
return SetMembers(key, flags);
}
}
return new SetScanIterator(this, server, key, pattern, pageSize, flags).Read();
}
public RedisValue[] Sort(RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetAddMessage(default(RedisKey), key, skip, take, order, sortType, by, get, flags);
return ExecuteSync(msg, ResultProcessor.RedisValueArray);
}
public long SortAndStore(RedisKey destination, RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetAddMessage(destination, key, skip, take, order, sortType, by, get, flags);
return ExecuteSync(msg, ResultProcessor.Int64);
}
public Task<long> SortAndStoreAsync(RedisKey destination, RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetAddMessage(destination, key, skip, take, order, sortType, by, get, flags);
return ExecuteAsync(msg, ResultProcessor.Int64);
}
public Task<RedisValue[]> SortAsync(RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetAddMessage(default(RedisKey), key, skip, take, order, sortType, by, get, flags);
return ExecuteAsync(msg, ResultProcessor.RedisValueArray);
}
public bool SortedSetAdd(RedisKey key, RedisValue member, double score, CommandFlags flags = CommandFlags.None) public bool SortedSetAdd(RedisKey key, RedisValue member, double score, CommandFlags flags = CommandFlags.None)
{ {
var msg = Message.Create(Db, flags, RedisCommand.ZADD, key, score, member); var msg = Message.Create(Db, flags, RedisCommand.ZADD, key, score, member);
...@@ -878,6 +920,30 @@ public Task<long> SortedSetAddAsync(RedisKey key, KeyValuePair<RedisValue, doubl ...@@ -878,6 +920,30 @@ public Task<long> SortedSetAddAsync(RedisKey key, KeyValuePair<RedisValue, doubl
return ExecuteAsync(msg, ResultProcessor.Int64); return ExecuteAsync(msg, ResultProcessor.Int64);
} }
public long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetCombineAndStoreCommandMessage(operation, destination, new[] { first, second }, null, aggregate, flags);
return ExecuteSync(msg, ResultProcessor.Int64);
}
public long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey[] keys, double[] weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetCombineAndStoreCommandMessage(operation, destination, keys, weights, aggregate, flags);
return ExecuteSync(msg, ResultProcessor.Int64);
}
public Task<long> SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetCombineAndStoreCommandMessage(operation, destination, new[] { first, second }, null, aggregate, flags);
return ExecuteAsync(msg, ResultProcessor.Int64);
}
public Task<long> SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey[] keys, double[] weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetCombineAndStoreCommandMessage(operation, destination, keys, weights, aggregate, flags);
return ExecuteAsync(msg, ResultProcessor.Int64);
}
public double SortedSetDecrement(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None) public double SortedSetDecrement(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None)
{ {
return SortedSetIncrement(key, member, -value, flags); return SortedSetIncrement(key, member, -value, flags);
...@@ -1171,6 +1237,22 @@ public Task<RedisValue> StringGetSetAsync(RedisKey key, RedisValue value, Comman ...@@ -1171,6 +1237,22 @@ public Task<RedisValue> StringGetSetAsync(RedisKey key, RedisValue value, Comman
return ExecuteAsync(msg, ResultProcessor.RedisValue); return ExecuteAsync(msg, ResultProcessor.RedisValue);
} }
public RedisValueWithExpiry StringGetWithExpiry(RedisKey key, CommandFlags flags = CommandFlags.None)
{
ServerEndPoint server;
ResultProcessor<RedisValueWithExpiry> processor;
var msg = GetStringGetWithExpiryMessage(key, flags, out processor, out server);
return ExecuteSync(msg, processor, server);
}
public Task<RedisValueWithExpiry> StringGetWithExpiryAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
{
ServerEndPoint server;
ResultProcessor<RedisValueWithExpiry> processor;
var msg = GetStringGetWithExpiryMessage(key, flags, out processor, out server);
return ExecuteAsync(msg, processor, server);
}
public long StringIncrement(RedisKey key, long value = 1, CommandFlags flags = CommandFlags.None) public long StringIncrement(RedisKey key, long value = 1, CommandFlags flags = CommandFlags.None)
{ {
var msg = IncrMessage(key, value, flags); var msg = IncrMessage(key, value, flags);
...@@ -1389,6 +1471,119 @@ private Message GetSortedSetAddMessage(RedisKey key, KeyValuePair<RedisValue, do ...@@ -1389,6 +1471,119 @@ private Message GetSortedSetAddMessage(RedisKey key, KeyValuePair<RedisValue, do
} }
} }
private Message GetSortedSetAddMessage(RedisKey destination, RedisKey key, long skip, long take, Order order, SortType sortType, RedisValue by, RedisValue[] get, CommandFlags flags)
{
// most common cases; no "get", no "by", no "destination", no "skip", no "take"
if (destination.IsNull && skip == 0 && take == -1 && by.IsNull && (get == null || get.Length == 0))
{
switch (order)
{
case Order.Ascending:
switch (sortType)
{
case SortType.Numeric: return Message.Create(Db, flags, RedisCommand.SORT, key);
case SortType.Alphabetic: return Message.Create(Db, flags, RedisCommand.SORT, key, RedisLiterals.ALPHA);
}
break;
case Order.Descending:
switch (sortType)
{
case SortType.Numeric: return Message.Create(Db, flags, RedisCommand.SORT, key, RedisLiterals.DESC);
case SortType.Alphabetic: return Message.Create(Db, flags, RedisCommand.SORT, key, RedisLiterals.DESC, RedisLiterals.ALPHA);
}
break;
}
}
// and now: more complicated scenarios...
List<RedisValue> values = new List<RedisValue>();
if (!by.IsNull)
{
values.Add(RedisLiterals.BY);
values.Add(by);
}
if (skip != 0 || take != -1)// these are our defaults that mean "everything"; anything else needs to be sent explicitly
{
values.Add(RedisLiterals.LIMIT);
values.Add(skip);
values.Add(take);
}
switch (order)
{
case Order.Ascending:
break; // default
case Order.Descending:
values.Add(RedisLiterals.DESC);
break;
default:
throw new ArgumentOutOfRangeException("order");
}
switch (sortType)
{
case SortType.Numeric:
break; // default
case SortType.Alphabetic:
values.Add(RedisLiterals.ALPHA);
break;
default:
throw new ArgumentOutOfRangeException("sortType");
}
if (get != null && get.Length != 0)
{
foreach (var item in get)
{
values.Add(RedisLiterals.GET);
values.Add(item);
}
}
if (destination.IsNull) return Message.Create(Db, flags, RedisCommand.SORT, key, values.ToArray());
// because we are using STORE, we need to push this to a master
if (Message.GetMasterSlaveFlags(flags) == CommandFlags.DemandSlave)
{
throw ExceptionFactory.MasterOnly(RedisCommand.SORT);
}
flags = Message.SetMasterSlaveFlags(flags, CommandFlags.DemandMaster);
values.Add(RedisLiterals.STORE);
return Message.Create(Db, flags, RedisCommand.SORT, key, values.ToArray(), destination);
}
private Message GetSortedSetCombineAndStoreCommandMessage(SetOperation operation, RedisKey destination, RedisKey[] keys, double[] weights, Aggregate aggregate, CommandFlags flags)
{
RedisCommand command;
switch (operation)
{
case SetOperation.Intersect: command = RedisCommand.ZINTERSTORE; break;
case SetOperation.Union: command = RedisCommand.ZUNIONSTORE; break;
default: throw new ArgumentOutOfRangeException("operation");
}
if (keys == null) throw new ArgumentNullException("keys");
List<RedisValue> values = null;
if (weights != null && weights.Length != 0)
{
(values ?? (values = new List<RedisValue>())).Add(RedisLiterals.WEIGHTS);
foreach (var weight in weights)
values.Add(weight);
}
switch (aggregate)
{
case Aggregate.Sum: break; // default
case Aggregate.Min:
(values ?? (values = new List<RedisValue>())).Add(RedisLiterals.AGGREGATE);
values.Add(RedisLiterals.MIN);
break;
case Aggregate.Max:
(values ?? (values = new List<RedisValue>())).Add(RedisLiterals.AGGREGATE);
values.Add(RedisLiterals.MAX);
break;
default:
throw new ArgumentOutOfRangeException("aggregate");
}
return new SortedSetCombineAndStoreCommandMessage(Db, flags, command, destination, keys, values == null ? RedisValue.EmptyArray : values.ToArray());
}
private Message GetSortedSetLengthMessage(RedisKey key, double min, double max, Exclude exclude, CommandFlags flags) private Message GetSortedSetLengthMessage(RedisKey key, double min, double max, Exclude exclude, CommandFlags flags)
{ {
if (double.IsNegativeInfinity(min) && double.IsPositiveInfinity(max)) if (double.IsNegativeInfinity(min) && double.IsPositiveInfinity(max))
...@@ -1456,6 +1651,24 @@ private Message GetStringBitOperationMessage(Bitwise operation, RedisKey destina ...@@ -1456,6 +1651,24 @@ private Message GetStringBitOperationMessage(Bitwise operation, RedisKey destina
slot = serverSelectionStrategy.CombineSlot(slot, second); slot = serverSelectionStrategy.CombineSlot(slot, second);
return Message.CreateInSlot(Db, slot, flags, RedisCommand.BITOP, new[] { op, destination.AsRedisValue(), first.AsRedisValue(), second.AsRedisValue() }); return Message.CreateInSlot(Db, slot, flags, RedisCommand.BITOP, new[] { op, destination.AsRedisValue(), first.AsRedisValue(), second.AsRedisValue() });
} }
Message GetStringGetWithExpiryMessage(RedisKey key, CommandFlags flags, out ResultProcessor<RedisValueWithExpiry> processor, out ServerEndPoint server)
{
if (this is IBatch)
{
throw new NotSupportedException("This operation is not possible inside a transaction or batch; please issue separate GetString and KeyTimeToLive requests");
}
var features = GetFeatures(Db, key, flags, out server);
if (server != null && features.MillisecondExpiry && multiplexer.CommandMap.IsAvailable(RedisCommand.PTTL))
{
processor = StringGetWithExpiryProcessor.PTTL;
return new StringGetWithExpiryMessage(Db, flags, RedisCommand.PTTL, key);
}
// if we use TTL, it doesn't matter which server
server = null;
processor = StringGetWithExpiryProcessor.TTL;
return new StringGetWithExpiryMessage(Db, flags, RedisCommand.TTL, key);
}
private Message GetStringSetMessage(KeyValuePair<RedisKey, RedisValue>[] values, When when = When.Always, CommandFlags flags = CommandFlags.None) private Message GetStringSetMessage(KeyValuePair<RedisKey, RedisValue>[] values, When when = When.Always, CommandFlags flags = CommandFlags.None)
{ {
if (values == null) throw new ArgumentNullException("values"); if (values == null) throw new ArgumentNullException("values");
...@@ -1540,232 +1753,6 @@ private RedisCommand SetOperationCommand(SetOperation operation, bool store) ...@@ -1540,232 +1753,6 @@ private RedisCommand SetOperationCommand(SetOperation operation, bool store)
default: throw new ArgumentOutOfRangeException("operation"); default: throw new ArgumentOutOfRangeException("operation");
} }
} }
public IEnumerable<RedisValue> SetScan(RedisKey key, RedisValue pattern = default(RedisValue), int pageSize = RedisDatabase.SetScanIterator.DefaultPageSize, CommandFlags flags = CommandFlags.None)
{
if (pageSize <= 0) throw new ArgumentOutOfRangeException("pageSize");
if (SetScanIterator.IsNil(pattern)) pattern = (byte[])null;
multiplexer.CommandMap.AssertAvailable(RedisCommand.SCAN);
ServerEndPoint server;
var features = GetFeatures(Db, key, flags, out server);
if (!features.Scan)
{
if(pattern.IsNull)
{
return SetMembers(key, flags);
}
}
return new SetScanIterator(this, server, key, pattern, pageSize, flags).Read();
}
Message GetStringGetWithExpiryMessage(RedisKey key, CommandFlags flags, out ResultProcessor<RedisValueWithExpiry> processor, out ServerEndPoint server)
{
if (this is IBatch)
{
throw new NotSupportedException("This operation is not possible inside a transaction or batch; please issue separate GetString and KeyTimeToLive requests");
}
var features = GetFeatures(Db, key, flags, out server);
if (server != null && features.MillisecondExpiry && multiplexer.CommandMap.IsAvailable(RedisCommand.PTTL))
{
processor = StringGetWithExpiryProcessor.PTTL;
return new StringGetWithExpiryMessage(Db, flags, RedisCommand.PTTL, key);
}
// if we use TTL, it doesn't matter which server
server = null;
processor = StringGetWithExpiryProcessor.TTL;
return new StringGetWithExpiryMessage(Db, flags, RedisCommand.TTL, key);
}
public RedisValueWithExpiry StringGetWithExpiry(RedisKey key, CommandFlags flags = CommandFlags.None)
{
ServerEndPoint server;
ResultProcessor<RedisValueWithExpiry> processor;
var msg = GetStringGetWithExpiryMessage(key, flags, out processor, out server);
return ExecuteSync(msg, processor, server);
}
public Task<RedisValueWithExpiry> StringGetWithExpiryAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
{
ServerEndPoint server;
ResultProcessor<RedisValueWithExpiry> processor;
var msg = GetStringGetWithExpiryMessage(key, flags, out processor, out server);
return ExecuteAsync(msg, processor, server);
}
private Message GetSortedSetAddMessage(RedisKey destination, RedisKey key, long skip, long take, Order order, SortType sortType, RedisValue by, RedisValue[] get, CommandFlags flags)
{
// most common cases; no "get", no "by", no "destination", no "skip", no "take"
if(destination.IsNull && skip == 0 && take == -1 && by.IsNull && (get == null || get.Length == 0))
{
switch(order)
{
case Order.Ascending:
switch(sortType)
{
case SortType.Numeric: return Message.Create(Db, flags, RedisCommand.SORT, key);
case SortType.Alphabetic: return Message.Create(Db, flags, RedisCommand.SORT, key, RedisLiterals.ALPHA);
}
break;
case Order.Descending:
switch (sortType)
{
case SortType.Numeric: return Message.Create(Db, flags, RedisCommand.SORT, key, RedisLiterals.DESC);
case SortType.Alphabetic: return Message.Create(Db, flags, RedisCommand.SORT, key, RedisLiterals.DESC, RedisLiterals.ALPHA);
}
break;
}
}
// and now: more complicated scenarios...
List<RedisValue> values = new List<RedisValue>();
if (!by.IsNull) {
values.Add(RedisLiterals.BY);
values.Add(by);
}
if (skip != 0 || take != -1)// these are our defaults that mean "everything"; anything else needs to be sent explicitly
{
values.Add(RedisLiterals.LIMIT);
values.Add(skip);
values.Add(take);
}
switch(order)
{
case Order.Ascending:
break; // default
case Order.Descending:
values.Add(RedisLiterals.DESC);
break;
default:
throw new ArgumentOutOfRangeException("order");
}
switch(sortType)
{
case SortType.Numeric:
break; // default
case SortType.Alphabetic:
values.Add(RedisLiterals.ALPHA);
break;
default:
throw new ArgumentOutOfRangeException("sortType");
}
if(get != null && get.Length != 0)
{
foreach(var item in get)
{
values.Add(RedisLiterals.GET);
values.Add(item);
}
}
if(destination.IsNull) return Message.Create(Db, flags, RedisCommand.SORT, key, values.ToArray());
// because we are using STORE, we need to push this to a master
if(Message.GetMasterSlaveFlags(flags) == CommandFlags.DemandSlave)
{
throw ExceptionFactory.MasterOnly(RedisCommand.SORT);
}
flags = Message.SetMasterSlaveFlags(flags, CommandFlags.DemandMaster);
values.Add(RedisLiterals.STORE);
return Message.Create(Db, flags, RedisCommand.SORT, key, values.ToArray(), destination);
}
public RedisValue[] Sort(RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetAddMessage(default(RedisKey), key, skip, take, order, sortType, by, get, flags);
return ExecuteSync(msg, ResultProcessor.RedisValueArray);
}
public long SortAndStore(RedisKey destination, RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetAddMessage(destination, key, skip, take, order, sortType, by, get, flags);
return ExecuteSync(msg, ResultProcessor.Int64);
}
public Task<RedisValue[]> SortAsync(RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetAddMessage(default(RedisKey), key, skip, take, order, sortType, by, get, flags);
return ExecuteAsync(msg, ResultProcessor.RedisValueArray);
}
public Task<long> SortAndStoreAsync(RedisKey destination, RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default(RedisValue), RedisValue[] get = null, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetAddMessage(destination, key, skip, take, order, sortType, by, get, flags);
return ExecuteAsync(msg, ResultProcessor.Int64);
}
private class StringGetWithExpiryProcessor : ResultProcessor<RedisValueWithExpiry>
{
public static readonly ResultProcessor<RedisValueWithExpiry> TTL = new StringGetWithExpiryProcessor(false), PTTL = new StringGetWithExpiryProcessor(true);
private readonly bool isMilliseconds;
private StringGetWithExpiryProcessor(bool isMilliseconds)
{
this.isMilliseconds = isMilliseconds;
}
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
{
switch(result.Type)
{
case ResultType.Integer:
case ResultType.SimpleString:
case ResultType.BulkString:
RedisValue value = result.AsRedisValue();
var sgwem = message as StringGetWithExpiryMessage;
TimeSpan? expiry;
Exception ex;
if (sgwem != null && sgwem.UnwrapValue(out expiry, out ex))
{
if (ex == null)
{
SetResult(message, new RedisValueWithExpiry(value, expiry));
}
else
{
SetException(message, ex);
}
return true;
}
break;
}
return false;
}
}
private class StringGetWithExpiryMessage : Message.CommandKeyBase, IMultiMessage
{
private readonly RedisCommand ttlCommand;
public StringGetWithExpiryMessage(int db, CommandFlags flags, RedisCommand ttlCommand, RedisKey key)
: base(db, flags | CommandFlags.NoRedirect /* <== not implemented/tested */, RedisCommand.GET, key)
{
this.ttlCommand = ttlCommand;
}
private ResultBox<TimeSpan?> box;
public bool UnwrapValue(out TimeSpan? value, out Exception ex)
{
if(box != null)
{
ResultBox<TimeSpan?>.UnwrapAndRecycle(box, out value, out ex);
box = null;
return ex == null;
}
value = null;
ex = null;
return false;
}
public override string CommandAndKey { get { return ttlCommand + "+" + RedisCommand.GET + " " + Key; } }
internal override void WriteImpl(PhysicalConnection physical)
{
physical.WriteHeader(command, 1);
physical.Write(Key);
}
public IEnumerable<Message> GetMessages(PhysicalConnection connection)
{
box = ResultBox<TimeSpan?>.Get(null);
var ttl = Message.Create(Db, Flags, ttlCommand, Key);
var proc = ttlCommand == RedisCommand.PTTL ? ResultProcessor.TimeSpanFromMilliseconds : ResultProcessor.TimeSpanFromSeconds;
ttl.SetSource(proc, box);
yield return ttl;
yield return this;
}
}
struct SetScanResult struct SetScanResult
{ {
public static readonly ResultProcessor<SetScanResult> Processor = new SetScanResultProcessor(); public static readonly ResultProcessor<SetScanResult> Processor = new SetScanResultProcessor();
...@@ -1774,7 +1761,7 @@ struct SetScanResult ...@@ -1774,7 +1761,7 @@ struct SetScanResult
public SetScanResult(long cursor, RedisValue[] values) public SetScanResult(long cursor, RedisValue[] values)
{ {
this.Cursor = cursor; this.Cursor = cursor;
this.Values= values; this.Values = values;
} }
private class SetScanResultProcessor : ResultProcessor<SetScanResult> private class SetScanResultProcessor : ResultProcessor<SetScanResult>
{ {
...@@ -1797,6 +1784,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes ...@@ -1797,6 +1784,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
} }
} }
} }
internal sealed class SetScanIterator internal sealed class SetScanIterator
{ {
internal const int DefaultPageSize = 10; internal const int DefaultPageSize = 10;
...@@ -1805,8 +1793,8 @@ internal sealed class SetScanIterator ...@@ -1805,8 +1793,8 @@ internal sealed class SetScanIterator
private readonly CommandFlags flags; private readonly CommandFlags flags;
private readonly int pageSize;
private readonly RedisKey key; private readonly RedisKey key;
private readonly int pageSize;
private readonly RedisValue pattern; private readonly RedisValue pattern;
private readonly ServerEndPoint server; private readonly ServerEndPoint server;
...@@ -1880,5 +1868,114 @@ Message CreateMessage(long cursor, bool running) ...@@ -1880,5 +1868,114 @@ Message CreateMessage(long cursor, bool running)
} }
} }
} }
sealed class SortedSetCombineAndStoreCommandMessage : Message.CommandKeyBase // ZINTERSTORE and ZUNIONSTORE have a very unusual signature
{
private readonly RedisKey[] keys;
private readonly RedisValue[] values;
public SortedSetCombineAndStoreCommandMessage(int db, CommandFlags flags, RedisCommand command, RedisKey destination, RedisKey[] keys, RedisValue[] values) : base(db, flags, command, destination)
{
for (int i = 0; i < keys.Length; i++)
keys[i].Assert();
this.keys = keys;
for (int i = 0; i < values.Length; i++)
values[i].Assert();
this.values = values;
}
public override int GetHashSlot(ServerSelectionStrategy serverSelectionStrategy)
{
int slot = base.GetHashSlot(serverSelectionStrategy);
for (int i = 0; i < keys.Length; i++)
slot = serverSelectionStrategy.CombineSlot(slot, keys[i]);
return slot;
}
internal override void WriteImpl(PhysicalConnection physical)
{
physical.WriteHeader(Command, 2 + keys.Length + values.Length);
physical.Write(Key);
physical.Write(keys.Length);
for (int i = 0; i < keys.Length; i++)
physical.Write(keys[i]);
for (int i = 0; i < values.Length; i++)
physical.Write(values[i]);
}
}
private class StringGetWithExpiryMessage : Message.CommandKeyBase, IMultiMessage
{
private readonly RedisCommand ttlCommand;
private ResultBox<TimeSpan?> box;
public StringGetWithExpiryMessage(int db, CommandFlags flags, RedisCommand ttlCommand, RedisKey key)
: base(db, flags | CommandFlags.NoRedirect /* <== not implemented/tested */, RedisCommand.GET, key)
{
this.ttlCommand = ttlCommand;
}
public override string CommandAndKey { get { return ttlCommand + "+" + RedisCommand.GET + " " + Key; } }
public IEnumerable<Message> GetMessages(PhysicalConnection connection)
{
box = ResultBox<TimeSpan?>.Get(null);
var ttl = Message.Create(Db, Flags, ttlCommand, Key);
var proc = ttlCommand == RedisCommand.PTTL ? ResultProcessor.TimeSpanFromMilliseconds : ResultProcessor.TimeSpanFromSeconds;
ttl.SetSource(proc, box);
yield return ttl;
yield return this;
}
public bool UnwrapValue(out TimeSpan? value, out Exception ex)
{
if (box != null)
{
ResultBox<TimeSpan?>.UnwrapAndRecycle(box, out value, out ex);
box = null;
return ex == null;
}
value = null;
ex = null;
return false;
}
internal override void WriteImpl(PhysicalConnection physical)
{
physical.WriteHeader(command, 1);
physical.Write(Key);
}
}
private class StringGetWithExpiryProcessor : ResultProcessor<RedisValueWithExpiry>
{
public static readonly ResultProcessor<RedisValueWithExpiry> TTL = new StringGetWithExpiryProcessor(false), PTTL = new StringGetWithExpiryProcessor(true);
private readonly bool isMilliseconds;
private StringGetWithExpiryProcessor(bool isMilliseconds)
{
this.isMilliseconds = isMilliseconds;
}
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
{
switch(result.Type)
{
case ResultType.Integer:
case ResultType.SimpleString:
case ResultType.BulkString:
RedisValue value = result.AsRedisValue();
var sgwem = message as StringGetWithExpiryMessage;
TimeSpan? expiry;
Exception ex;
if (sgwem != null && sgwem.UnwrapValue(out expiry, out ex))
{
if (ex == null)
{
SetResult(message, new RedisValueWithExpiry(value, expiry));
}
else
{
SetException(message, ex);
}
return true;
}
break;
}
return false;
}
}
} }
} }
...@@ -44,6 +44,10 @@ public static readonly RedisValue ...@@ -44,6 +44,10 @@ public static readonly RedisValue
DESC = "DESC", DESC = "DESC",
ALPHA = "ALPHA", ALPHA = "ALPHA",
STORE = "STORE", STORE = "STORE",
WEIGHTS = "WEIGHTS",
MIN = "MIN",
MAX = "MAX",
AGGREGATE = "AGGREGATE",
// DO NOT CHANGE CASE: these are configuration settings and MUST be as-is // DO NOT CHANGE CASE: these are configuration settings and MUST be as-is
databases = "databases", databases = "databases",
......
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