Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Support JSON.MERGE Command #132

Merged
merged 35 commits into from
May 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ac44147
Support JSON.MSET Command
shacharPash May 8, 2023
bd52427
add tests
shacharPash May 8, 2023
bc635e4
Merge branch 'master' into Issue128/JSON.MSET
shacharPash May 8, 2023
3936612
fixes
shacharPash May 8, 2023
8407c90
Support JSON.MERGE Command
shacharPash May 9, 2023
ed2751f
adding edge filter
slorello89 May 9, 2023
fd16eeb
bump
slorello89 May 9, 2023
eabd24d
applying to .NET 6 too
slorello89 May 9, 2023
f2ccf86
think it might be picking up the version env var
slorello89 May 9, 2023
7258335
Merge branch 'master' into Issue128/JSON.MSET
shacharPash May 10, 2023
cb72354
Merge branch 'Issue128/JSON.MSET' into Issue129/JSON.MERGE
shacharPash May 10, 2023
b92cbeb
Merge branch 'master' into Issue129/JSON.MERGE
shacharPash May 10, 2023
6faac3b
Sync test for json.merge
shacharPash May 10, 2023
f8ec082
push
shacharPash May 10, 2023
5812a16
add async test
shacharPash May 10, 2023
0019127
add JSON. to MERGE in commands.cs
shacharPash May 10, 2023
a9a5104
Merge branch 'master' into Issue129/JSON.MERGE
shacharPash May 10, 2023
0527323
test from redisJson
shacharPash May 10, 2023
2666927
add async test
shacharPash May 10, 2023
17534bf
fix windows test
shacharPash May 10, 2023
0adef83
fix word mistake
shacharPash May 10, 2023
1f936dd
Merge branch 'master' into Issue129/JSON.MERGE
chayim May 11, 2023
c99e258
check if its passing
shacharPash May 11, 2023
da7fa5f
check 2
shacharPash May 11, 2023
a91e3ae
check 3
shacharPash May 11, 2023
049162e
adding Merge input options
shacharPash May 11, 2023
9579342
change $.a.b test merge
shacharPash May 11, 2023
9ba17df
trying somthing else
shacharPash May 11, 2023
d0abeab
fix mistake
shacharPash May 11, 2023
b25242d
change order
shacharPash May 11, 2023
ae4e3c2
fix }
shacharPash May 11, 2023
af083fb
change to KeyPathValue
shacharPash May 11, 2023
a633e3b
null test
shacharPash May 11, 2023
53551f5
async test + fixes
shacharPash May 11, 2023
8279820
change order
shacharPash May 11, 2023
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
13 changes: 4 additions & 9 deletions src/NRedisStack/Json/DataTypes/KeyValuePath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,17 @@

namespace NRedisStack.Json.DataTypes;

public struct KeyValuePath
public struct KeyPathValue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a breaking change, so if this is right, make sure to apply the label and put it in the notes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this struct merged yesterday

{
public string Key { get; set; }
public object Value { get; set; }
public string Path { get; set; }
public object Value { get; set; }

public KeyValuePath(string key, object value, string path = "$")
public KeyPathValue(string key, string path, object value)
{
if (key == null || value == null)
{
throw new ArgumentNullException("Key and value cannot be null.");
}

Key = key;
Value = value;
Path = path;
Value = value;
}
public string[] ToArray()
{
Expand Down
30 changes: 26 additions & 4 deletions src/NRedisStack/Json/IJsonCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,31 @@ public interface IJsonCommands
/// <summary>
/// Sets or updates the JSON value of one or more keys.
/// </summary>
/// <param name="keyValuePathList">The key, The value to set and
/// <param name="KeyPathValueList">The key, The value to set and
/// The path to set within the key, must be > 1 </param>
/// <returns>The disposition of the command</returns>
/// <remarks><seealso href="https://redis.io/commands/json.mset"/></remarks>
bool MSet(KeyValuePath[] keyValuePathList);
bool MSet(KeyPathValue[] KeyPathValueList);

/// <summary>
/// Sets or updates the JSON value at a path.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is accurate?

/// </summary>
/// <param name="key">The key.</param>
/// <param name="path">The path to set within the key.</param>
/// <param name="json">The value to set.</param>
/// <returns>The disposition of the command</returns>
/// <remarks><seealso href="https://redis.io/commands/json.merge"/></remarks>
bool Merge(RedisKey key, RedisValue path, RedisValue json);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So clean!


/// <summary>
/// Sets or updates the JSON value at a path.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="path">The path to set within the key.</param>
/// <param name="obj">The value to set.</param>
/// <returns>The disposition of the command</returns>
/// <remarks><seealso href="https://redis.io/commands/json.merge"/></remarks>
bool Merge(RedisKey key, RedisValue path, object obj);

/// <summary>
/// Sets or updates the JSON value of one or more keys.
Expand Down Expand Up @@ -242,7 +262,8 @@ public interface IJsonCommands
/// <param name="key">The key to append to.</param>
/// <param name="path">The path of the string(s) to append to.</param>
/// <param name="value">The value to append.</param>
/// <returns>The new length of the string(s) appended to, those lengths will be null if the path did not resolve ot a string.</returns>
/// <returns>The new length of the string(s) appended to, those lengths
/// will be null if the path did not resolve ot a string.</returns>
/// <remarks><seealso href="https://redis.io/commands/json.strappend"/></remarks>
long?[] StrAppend(RedisKey key, string value, string? path = null);

Expand All @@ -251,7 +272,8 @@ public interface IJsonCommands
/// </summary>
/// <param name="key">The key of the json object.</param>
/// <param name="path">The path of the string(s) within the json object.</param>
/// <returns>The length of the string(s) appended to, those lengths will be null if the path did not resolve ot a string.</returns>
/// <returns>The length of the string(s) appended to, those lengths
/// will be null if the path did not resolve ot a string.</returns>
/// <remarks><seealso href="https://redis.io/commands/json.strlen"/></remarks>
public long?[] StrLen(RedisKey key, string? path = null);

Expand Down
24 changes: 22 additions & 2 deletions src/NRedisStack/Json/IJsonCommandsAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,31 @@ public interface IJsonCommandsAsync
/// <summary>
/// Sets or updates the JSON value of one or more keys.
/// </summary>
/// <param name="keyValuePathList">The key, The value to set and
/// <param name="KeyPathValueList">The key, The value to set and
/// The path to set within the key, must be > 1 </param>
/// <returns>The disposition of the command</returns>
/// <remarks><seealso href="https://redis.io/commands/json.mset"/></remarks>
Task<bool> MSetAsync(KeyValuePath[] keyValuePathList);
Task<bool> MSetAsync(KeyPathValue[] KeyPathValueList);

/// <summary>
/// Sets or updates the JSON value at a path.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="path">The path to set within the key.</param>
/// <param name="json">The value to set.</param>
/// <returns>The disposition of the command</returns>
/// <remarks><seealso href="https://redis.io/commands/json.merge"/></remarks>
Task<bool> MergeAsync(RedisKey key, RedisValue path, RedisValue json);

/// <summary>
/// Sets or updates the JSON value at a path.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="path">The path to set within the key.</param>
/// <param name="obj">The value to set.</param>
/// <returns>The disposition of the command</returns>
/// <remarks><seealso href="https://redis.io/commands/json.merge"/></remarks>
Task<bool> MergeAsync(RedisKey key, RedisValue path, object obj);

/// <summary>
/// Set json file from the provided file Path.
Expand Down
13 changes: 9 additions & 4 deletions src/NRedisStack/Json/JsonCommandBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,20 @@ public static SerializedCommand Set(RedisKey key, RedisValue path, RedisValue js
};
}

public static SerializedCommand MSet(KeyValuePath[] keyValuePathList)
public static SerializedCommand MSet(KeyPathValue[] KeyPathValueList)
{
if (keyValuePathList.Length < 1)
throw new ArgumentOutOfRangeException(nameof(keyValuePathList));
if (KeyPathValueList.Length < 1)
throw new ArgumentOutOfRangeException(nameof(KeyPathValueList));

var args = keyValuePathList.SelectMany(x => x.ToArray()).ToArray();
var args = KeyPathValueList.SelectMany(x => x.ToArray()).ToArray();
return new SerializedCommand(JSON.MSET, args);
}

public static SerializedCommand Merge(RedisKey key, RedisValue path, RedisValue json)
{
return new SerializedCommand(JSON.MERGE, key, path, json);
}

public static SerializedCommand StrAppend(RedisKey key, string value, string? path = null)
{
if (path == null)
Expand Down
17 changes: 15 additions & 2 deletions src/NRedisStack/Json/JsonCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,22 @@ public bool Set(RedisKey key, RedisValue path, RedisValue json, When when = When
}

/// <inheritdoc/>
public bool MSet(KeyValuePath[] keyValuePathList)
public bool MSet(KeyPathValue[] KeyPathValueList)
{
return _db.Execute(JsonCommandBuilder.MSet(keyValuePathList)).OKtoBoolean();
return _db.Execute(JsonCommandBuilder.MSet(KeyPathValueList)).OKtoBoolean();
}

/// <inheritdoc/>
public bool Merge(RedisKey key, RedisValue path, RedisValue json)
{
return _db.Execute(JsonCommandBuilder.Merge(key, path, json)).OKtoBoolean();
}

/// <inheritdoc/>
public bool Merge(RedisKey key, RedisValue path, object obj)
{
string json = JsonSerializer.Serialize(obj);
return _db.Execute(JsonCommandBuilder.Merge(key, path, json)).OKtoBoolean();
}

/// <inheritdoc/>
Expand Down
17 changes: 15 additions & 2 deletions src/NRedisStack/Json/JsonCommandsAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,22 @@ public async Task<bool> SetAsync(RedisKey key, RedisValue path, RedisValue json,
return (await _db.ExecuteAsync(JsonCommandBuilder.Set(key, path, json, when))).OKtoBoolean();
}

public async Task<bool> MSetAsync(KeyValuePath[] keyValuePathList)
public async Task<bool> MSetAsync(KeyPathValue[] KeyPathValueList)
{
return (await _db.ExecuteAsync(JsonCommandBuilder.MSet(keyValuePathList))).OKtoBoolean();
return (await _db.ExecuteAsync(JsonCommandBuilder.MSet(KeyPathValueList))).OKtoBoolean();
}

/// <inheritdoc/>
public async Task<bool> MergeAsync(RedisKey key, RedisValue path, RedisValue json)
{
return (await _db.ExecuteAsync(JsonCommandBuilder.Merge(key, path, json))).OKtoBoolean();
}

/// <inheritdoc/>
public async Task<bool> MergeAsync(RedisKey key, RedisValue path, object obj)
{
string json = JsonSerializer.Serialize(obj);
return (await _db.ExecuteAsync(JsonCommandBuilder.Merge(key, path, json))).OKtoBoolean();
}

public async Task<bool> SetFromFileAsync(RedisKey key, RedisValue path, string filePath, When when = When.Always)
Expand Down
1 change: 1 addition & 0 deletions src/NRedisStack/Json/Literals/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ internal class JSON
public const string FORGET = "JSON.FORGET";
public const string GET = "JSON.GET";
public const string MEMORY = "MEMORY";
public const string MERGE = "JSON.MERGE";
public const string MSET = "JSON.MSET";
public const string MGET = "JSON.MGET";
public const string NUMINCRBY = "JSON.NUMINCRBY";
Expand Down
68 changes: 50 additions & 18 deletions tests/NRedisStack.Tests/Json/JsonTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -726,18 +726,18 @@ public async Task GetAsync()
}

[Fact]
[Trait("Category","edge")]
[Trait("Category", "edge")]
public void MSet()
{
IJsonCommands commands = new JsonCommands(redisFixture.Redis.GetDatabase());
var keys = CreateKeyNames(2);
var key1 = keys[0];
var key2 = keys[1];

KeyValuePath[] values = new[]
KeyPathValue[] values = new[]
{
new KeyValuePath(key1, new { a = "hello" }),
new KeyValuePath(key2, new { a = "world" })
new KeyPathValue(key1, "$", new { a = "hello" }),
new KeyPathValue(key2, "$", new { a = "world" })
};
commands.MSet(values)
;
Expand All @@ -747,22 +747,21 @@ public void MSet()
Assert.Equal("[\"world\"]", result[1].ToString());

// test errors:
Assert.Throws<ArgumentOutOfRangeException>(() => commands.MSet(new KeyValuePath[0]));

Assert.Throws<ArgumentOutOfRangeException>(() => commands.MSet(new KeyPathValue[0]));
}

[Fact]
[Trait("Category","edge")]
[Trait("Category", "edge")]
public async Task MSetAsync()
{
IJsonCommandsAsync commands = new JsonCommands(redisFixture.Redis.GetDatabase());
var keys = CreateKeyNames(2);
var key1 = keys[0];
var key2 = keys[1];
KeyValuePath[] values = new[]
KeyPathValue[] values = new[]
{
new KeyValuePath(key1, new { a = "hello" }),
new KeyValuePath(key2, new { a = "world" })
new KeyPathValue(key1, "$", new { a = "hello" }),
new KeyPathValue(key2, "$", new { a = "world" })
};
await commands.MSetAsync(values)
;
Expand All @@ -772,14 +771,47 @@ await commands.MSetAsync(values)
Assert.Equal("[\"world\"]", result[1].ToString());

// test errors:
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () => await commands.MSetAsync(new KeyValuePath[0]));
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () => await commands.MSetAsync(new KeyPathValue[0]));
}

[Fact]
public void TestKeyValuePathErrors()
[Trait("Category", "edge")]
public void Merge()
{
Assert.Throws<ArgumentNullException>(() => new KeyValuePath(null!, new { a = "hello" }));
Assert.Throws<ArgumentNullException>(() => new KeyValuePath("key", null!) );
// Create a connection to Redis
IJsonCommands commands = new JsonCommands(redisFixture.Redis.GetDatabase());

Assert.True(commands.Set("test_merge", "$", new { person = new { name = "John Doe", age = 25, address = new {home = "123 Main Street"}, phone = "123-456-7890" } }));
Assert.True(commands.Merge("test_merge", "$", new { person = new { age = 30 } }));
Assert.Equal("{\"person\":{\"name\":\"John Doe\",\"age\":30,\"address\":{\"home\":\"123 Main Street\"},\"phone\":\"123-456-7890\"}}", commands.Get("test_merge").ToString());

// Test with root path path $.a.b
Assert.True(commands.Merge("test_merge", "$.person.address", new {work = "Redis office"}));
Assert.Equal("{\"person\":{\"name\":\"John Doe\",\"age\":30,\"address\":{\"home\":\"123 Main Street\",\"work\":\"Redis office\"},\"phone\":\"123-456-7890\"}}", commands.Get("test_merge").ToString());

// Test with null value to delete a value
Assert.True(commands.Merge("test_merge", "$.person", "{\"age\":null}"));
Assert.Equal("{\"person\":{\"name\":\"John Doe\",\"phone\":\"123-456-7890\",\"address\":{\"home\":\"123 Main Street\",\"work\":\"Redis office\"}}}", commands.Get("test_merge").ToString());
}

[Fact]
[Trait("Category", "edge")]
public async Task MergeAsync()
{
// Create a connection to Redis
IJsonCommandsAsync commands = new JsonCommands(redisFixture.Redis.GetDatabase());

Assert.True(await commands.SetAsync("test_merge", "$", new { person = new { name = "John Doe", age = 25, address = new {home = "123 Main Street"}, phone = "123-456-7890" } }));
Assert.True(await commands.MergeAsync("test_merge", "$", new { person = new { age = 30 } }));
Assert.Equal("{\"person\":{\"name\":\"John Doe\",\"age\":30,\"address\":{\"home\":\"123 Main Street\"},\"phone\":\"123-456-7890\"}}", (await commands.GetAsync("test_merge")).ToString());

// Test with root path path $.a.b
Assert.True(await commands.MergeAsync("test_merge", "$.person.address", new {work = "Redis office"}));
Assert.Equal("{\"person\":{\"name\":\"John Doe\",\"age\":30,\"address\":{\"home\":\"123 Main Street\",\"work\":\"Redis office\"},\"phone\":\"123-456-7890\"}}", (await commands.GetAsync("test_merge")).ToString());

// Test with null value to delete a value
Assert.True(await commands.MergeAsync("test_merge", "$.person", "{\"age\":null}"));
Assert.Equal("{\"person\":{\"name\":\"John Doe\",\"phone\":\"123-456-7890\",\"address\":{\"home\":\"123 Main Street\",\"work\":\"Redis office\"}}}", (await commands.GetAsync("test_merge")).ToString());
}

[Fact]
Expand Down Expand Up @@ -1067,18 +1099,18 @@ public async Task TestSetFromDirectoryAsync()
public void TestJsonCommandBuilder()
{
var getBuild1 = JsonCommandBuilder.Get("key", "indent", "newline", "space", "path");
var getBuild2 = JsonCommandBuilder.Get("key",new string[]{"path1", "path2", "path3"}, "indent", "newline", "space");
var expectedArgs1 = new object[] { "key", "INDENT", "indent", "NEWLINE","newline", "SPACE", "space", "path" };
var getBuild2 = JsonCommandBuilder.Get("key", new string[] { "path1", "path2", "path3" }, "indent", "newline", "space");
var expectedArgs1 = new object[] { "key", "INDENT", "indent", "NEWLINE", "newline", "SPACE", "space", "path" };
var expectedArgs2 = new object[] { "key", "INDENT", "indent", "NEWLINE", "newline", "SPACE", "space", "path1", "path2", "path3" };


for(int i = 0; i < expectedArgs1.Length; i++)
for (int i = 0; i < expectedArgs1.Length; i++)
{
Assert.Equal(expectedArgs1[i].ToString(), getBuild1.Args[i].ToString());
}
Assert.Equal("JSON.GET", getBuild1.Command);

for(int i = 0; i < expectedArgs2.Length; i++)
for (int i = 0; i < expectedArgs2.Length; i++)
{
Assert.Equal(expectedArgs2[i].ToString(), getBuild2.Args[i].ToString());
}
Expand Down