Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- Adds: Support for `ZMPOP` with `.SortedSetPop()`/`.SortedSetPopAsync()` ([#2094 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2094))
- Adds: Support for `XAUTOCLAIM` with `.StreamAutoClaim()`/.`StreamAutoClaimAsync()` and `.StreamAutoClaimIdsOnly()`/.`StreamAutoClaimIdsOnlyAsync()` ([#2095 by ttingen](https://github.com/StackExchange/StackExchange.Redis/pull/2095))
- Fix [#2071](https://github.com/StackExchange/StackExchange.Redis/issues/2071): Add `.StringSet()`/`.StringSetAsync()` overloads for source compat broken for 1 case in 2.5.61 ([#2098 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2098))
- Adds: Support for `LCS` with `.StringLongestCommonSubsequence()`/`.StringLongestCommonSubsequence()`, `.StringLongestCommonSubsequenceLength()`/`.StringLongestCommonSubsequenceLengthAsync()`, and `.StringLongestCommonSubsequenceWithMatches()`/`.StringLongestCommonSubsequenceWithMatchesAsync()` ([#2104 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2104))
- Adds: Support for `OBJECT FREQ` with `.KeyFrequency()`/`.KeyFrequencyAsync()` ([#2105 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2105))
- Performance: Avoids allocations when computing cluster hash slots or testing key equality ([#2110 by Marc Gravell](https://github.com/StackExchange/StackExchange.Redis/pull/2110))
- Adds: Support for `SORT_RO` with `.Sort()`/`.SortAsync()` ([#2111 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2111))
Expand Down
67 changes: 67 additions & 0 deletions src/StackExchange.Redis/APITypes/LCSMatchResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;

namespace StackExchange.Redis;

/// <summary>
/// The result of a LongestCommonSubsequence command with IDX feature.
/// Returns a list of the positions of each sub-match.
/// </summary>
public readonly struct LCSMatchResult
{
internal static LCSMatchResult Null { get; } = new LCSMatchResult(Array.Empty<LCSMatch>(), 0);

/// <summary>
/// The matched positions of all the sub-matched strings.
/// </summary>
public LCSMatch[] Matches { get; }

/// <summary>
/// The length of the longest match.
/// </summary>
public long LongestMatchLength { get; }

/// <summary>
/// Returns a new <see cref="LCSMatchResult"/>.
/// </summary>
/// <param name="matches">The matched positions in each string.</param>
/// <param name="matchLength">The length of the match.</param>
internal LCSMatchResult(LCSMatch[] matches, long matchLength)
{
Matches = matches;
LongestMatchLength = matchLength;
}

/// <summary>
/// Represents a sub-match of the longest match. i.e first indexes the matched substring in each string.
/// </summary>
public readonly struct LCSMatch
{
/// <summary>
/// The first index of the matched substring in the first string.
/// </summary>
public long FirstStringIndex { get; }

/// <summary>
/// The first index of the matched substring in the second string.
/// </summary>
public long SecondStringIndex { get; }

/// <summary>
/// The length of the match.
/// </summary>
public long Length { get; }

/// <summary>
/// Returns a new Match.
/// </summary>
/// <param name="firstStringIndex">The first index of the matched substring in the first string.</param>
/// <param name="secondStringIndex">The first index of the matched substring in the second string.</param>
/// <param name="length">The length of the match.</param>
internal LCSMatch(long firstStringIndex, long secondStringIndex, long length)
{
FirstStringIndex = firstStringIndex;
SecondStringIndex = secondStringIndex;
Length = length;
}
}
}
2 changes: 2 additions & 0 deletions src/StackExchange.Redis/Enums/RedisCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ internal enum RedisCommand

LASTSAVE,
LATENCY,
LCS,
LINDEX,
LINSERT,
LLEN,
Expand Down Expand Up @@ -393,6 +394,7 @@ internal static bool IsPrimaryOnly(this RedisCommand command)
case RedisCommand.KEYS:
case RedisCommand.LASTSAVE:
case RedisCommand.LATENCY:
case RedisCommand.LCS:
case RedisCommand.LINDEX:
case RedisCommand.LLEN:
case RedisCommand.LPOS:
Expand Down
40 changes: 40 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2769,6 +2769,46 @@ IEnumerable<SortedSetEntry> SortedSetScan(RedisKey key,
/// <remarks><seealso href="https://redis.io/commands/strlen"/></remarks>
long StringLength(RedisKey key, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Implements the longest common subsequence algorithm between the values at <paramref name="first"/> and <paramref name="second"/>,
/// returning a string containing the common sequence.
/// Note that this is different than the longest common string algorithm,
/// since matching characters in the string does not need to be contiguous.
/// </summary>
/// <param name="first">The key of the first string.</param>
/// <param name="second">The key of the second string.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>A string (sequence of characters) of the LCS match.</returns>
/// <remarks><seealso href="https://redis.io/commands/lcs"/></remarks>
string? StringLongestCommonSubsequence(RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Implements the longest common subsequence algorithm between the values at <paramref name="first"/> and <paramref name="second"/>,
/// returning the legnth of the common sequence.
/// Note that this is different than the longest common string algorithm,
/// since matching characters in the string does not need to be contiguous.
/// </summary>
/// <param name="first">The key of the first string.</param>
/// <param name="second">The key of the second string.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The length of the LCS match.</returns>
/// <remarks><seealso href="https://redis.io/commands/lcs"/></remarks>
long StringLongestCommonSubsequenceLength(RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Implements the longest common subsequence algorithm between the values at <paramref name="first"/> and <paramref name="second"/>,
/// returning a list of all common sequences.
/// Note that this is different than the longest common string algorithm,
/// since matching characters in the string does not need to be contiguous.
/// </summary>
/// <param name="first">The key of the first string.</param>
/// <param name="second">The key of the second string.</param>
/// <param name="minLength">Can be used to restrict the list of matches to the ones of a given minimum length.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The result of LCS algorithm, based on the given parameters.</returns>
/// <remarks><seealso href="https://redis.io/commands/lcs"/></remarks>
LCSMatchResult StringLongestCommonSubsequenceWithMatches(RedisKey first, RedisKey second, long minLength = 0, CommandFlags flags = CommandFlags.None);

/// <inheritdoc cref="StringSet(RedisKey, RedisValue, TimeSpan?, bool, When, CommandFlags)" />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
bool StringSet(RedisKey key, RedisValue value, TimeSpan? expiry, When when);
Expand Down
40 changes: 40 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2721,6 +2721,46 @@ IAsyncEnumerable<SortedSetEntry> SortedSetScanAsync(RedisKey key,
/// <remarks><seealso href="https://redis.io/commands/strlen"/></remarks>
Task<long> StringLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Implements the longest common subsequence algorithm between the values at <paramref name="first"/> and <paramref name="second"/>,
/// returning a string containing the common sequence.
/// Note that this is different than the longest common string algorithm,
/// since matching characters in the string does not need to be contiguous.
/// </summary>
/// <param name="first">The key of the first string.</param>
/// <param name="second">The key of the second string.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>A string (sequence of characters) of the LCS match.</returns>
/// <remarks><seealso href="https://redis.io/commands/lcs"/></remarks>
Task<string?> StringLongestCommonSubsequenceAsync(RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Implements the longest common subsequence algorithm between the values at <paramref name="first"/> and <paramref name="second"/>,
/// returning the legnth of the common sequence.
/// Note that this is different than the longest common string algorithm,
/// since matching characters in the string does not need to be contiguous.
/// </summary>
/// <param name="first">The key of the first string.</param>
/// <param name="second">The key of the second string.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The length of the LCS match.</returns>
/// <remarks><seealso href="https://redis.io/commands/lcs"/></remarks>
Task<long> StringLongestCommonSubsequenceLengthAsync(RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Implements the longest common subsequence algorithm between the values at <paramref name="first"/> and <paramref name="second"/>,
/// returning a list of all common sequences.
/// Note that this is different than the longest common string algorithm,
/// since matching characters in the string does not need to be contiguous.
/// </summary>
/// <param name="first">The key of the first string.</param>
/// <param name="second">The key of the second string.</param>
/// <param name="minLength">Can be used to restrict the list of matches to the ones of a given minimum length.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The result of LCS algorithm, based on the given parameters.</returns>
/// <remarks><seealso href="https://redis.io/commands/lcs"/></remarks>
Task<LCSMatchResult> StringLongestCommonSubsequenceWithMatchesAsync(RedisKey first, RedisKey second, long minLength = 0, CommandFlags flags = CommandFlags.None);

/// <inheritdoc cref="StringSetAsync(RedisKey, RedisValue, TimeSpan?, bool, When, CommandFlags)" />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
Task<bool> StringSetAsync(RedisKey key, RedisValue value, TimeSpan? expiry, When when);
Expand Down
9 changes: 9 additions & 0 deletions src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,15 @@ public bool LockRelease(RedisKey key, RedisValue value, CommandFlags flags = Com
public bool LockTake(RedisKey key, RedisValue value, TimeSpan expiry, CommandFlags flags = CommandFlags.None) =>
Inner.LockTake(ToInner(key), value, expiry, flags);

public string? StringLongestCommonSubsequence(RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) =>
Inner.StringLongestCommonSubsequence(ToInner(first), ToInner(second), flags);

public long StringLongestCommonSubsequenceLength(RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) =>
Inner.StringLongestCommonSubsequenceLength(ToInner(first), ToInner(second), flags);

public LCSMatchResult StringLongestCommonSubsequenceWithMatches(RedisKey first, RedisKey second, long minLength = 0, CommandFlags flags = CommandFlags.None) =>
Inner.StringLongestCommonSubsequenceWithMatches(ToInner(first), ToInner(second), minLength, flags);

public long Publish(RedisChannel channel, RedisValue message, CommandFlags flags = CommandFlags.None) =>
Inner.Publish(ToInner(channel), message, flags);

Expand Down
9 changes: 9 additions & 0 deletions src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,15 @@ public Task<bool> LockReleaseAsync(RedisKey key, RedisValue value, CommandFlags
public Task<bool> LockTakeAsync(RedisKey key, RedisValue value, TimeSpan expiry, CommandFlags flags = CommandFlags.None) =>
Inner.LockTakeAsync(ToInner(key), value, expiry, flags);

public Task<string?> StringLongestCommonSubsequenceAsync(RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) =>
Inner.StringLongestCommonSubsequenceAsync(ToInner(first), ToInner(second), flags);

public Task<long> StringLongestCommonSubsequenceLengthAsync(RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) =>
Inner.StringLongestCommonSubsequenceLengthAsync(ToInner(first), ToInner(second), flags);

public Task<LCSMatchResult> StringLongestCommonSubsequenceWithMatchesAsync(RedisKey first, RedisKey second, long minLength = 0, CommandFlags flags = CommandFlags.None) =>
Inner.StringLongestCommonSubsequenceWithMatchesAsync(ToInner(first), ToInner(second), minLength, flags);

public Task<long> PublishAsync(RedisChannel channel, RedisValue message, CommandFlags flags = CommandFlags.None) =>
Inner.PublishAsync(ToInner(channel), message, flags);

Expand Down
Loading