Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
36 changes: 36 additions & 0 deletions local_examples/cmds_hash/NRedisStack/CmdsHashExample.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// EXAMPLE: cmds_hash
using StackExchange.Redis;
using Xunit;
using System.Linq;

namespace Doc;

Expand Down Expand Up @@ -110,5 +111,40 @@ public void Run()
// >>> Hello, World
// STEP_END
Assert.Equal("Hello, World", string.Join(", ", hValsResult));
db.KeyDelete("myhash");

// STEP_START hexpire
// Set up hash with fields
db.HashSet("myhash",
[
new("field1", "Hello"),
new("field2", "World")
]
);

// Set expiration on hash fields using raw Execute
RedisResult hexpireRes1 = db.Execute("HEXPIRE", "myhash", 10, "FIELDS", 2, "field1", "field2");
Copy link
Contributor

Choose a reason for hiding this comment

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

This is actually implemented in the library now (HashFieldExpire(), etc) so the catchall Execute isn't necessary. I've found you sometimes need to nudge Augie in the right direction for C# because the API method names typically don't closely resemble the command names (other clients occasionally have trip-ups like this too).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I couldn't get Auggie to pony up the goods on HashFieldExpire; it insists that it doesn't exist. I give up.

Copy link
Contributor

Choose a reason for hiding this comment

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

@dwdougherty I guess you pulled the latest from the NRedisStack repo into your local clone but I think you sometimes need to do a dotnet build or whatever to update the intellisense with the latest from StackExchange.Redis (and even then, as you say, the Augster also struggles to find the non-standard method names in the repo). I've often found there's considerable trial and error involved with this client :-)

Anyway, I've fixed it well enough to get a test pass when I copied/pasted this into the existing CmdsHashExample.cs in my NRedisStack clone, so I've just copied that back into this PR. I think what the user sees is valid, but we'll see if any further fixing up is required when we eventually submit this to the NRedisStack repo.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thank you for doing that. I do have the latest NRedisStack repo in my clients directory and that's what I point Auggie to. I'm not following the bit about doing a dotnet build. Do you mean in the repo itself? Also, I deleted the .Net stuff from VS Code--it kept crashing. ><

Is there anything left to do on this PR? If not, would you mind approving it so I can get it merged?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think things like HEXPIRE are in StackExchange.Redis rather than NRedisStack, so the intellisense and Augie stuff might not get updated until you rebuild and pull everything in from SE (not 100% sure this is how it works, but seems to solve problems sometimes). Anyway, I can definitely see HashFieldExpire and Augie can suggest it, so presumably I've got some update that you haven't.

Console.WriteLine(string.Join(", ", (RedisValue[])hexpireRes1));
// >>> 1, 1

// Check TTL of the fields using raw Execute
RedisResult hexpireRes2 = db.Execute("HTTL", "myhash", "FIELDS", 2, "field1", "field2");
RedisValue[] ttlValues = (RedisValue[])hexpireRes2;
Console.WriteLine(ttlValues.Length);
// >>> 2

// Try to set expiration on non-existent field
RedisResult hexpireRes3 = db.Execute("HEXPIRE", "myhash", 10, "FIELDS", 1, "nonexistent");
Console.WriteLine(string.Join(", ", (RedisValue[])hexpireRes3));
// >>> -2
// STEP_END

RedisValue[] expireResult1 = (RedisValue[])hexpireRes1;
RedisValue[] expireResult3 = (RedisValue[])hexpireRes3;
Assert.Equal("1, 1", string.Join(", ", expireResult1));
Assert.Equal(2, ttlValues.Length);
Assert.True(ttlValues.All(ttl => (int)ttl > 0)); // TTL should be positive
Assert.Equal("-2", string.Join(", ", expireResult3));
db.KeyDelete("myhash");
}
}
51 changes: 51 additions & 0 deletions local_examples/cmds_hash/go-redis/cmds_hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"fmt"
"sort"
"time"

"github.com/redis/go-redis/v9"
)
Expand Down Expand Up @@ -293,4 +294,54 @@ func ExampleClient_hdel() {
// 1
// 1
// 0
}

func ExampleClient_hexpire() {
ctx := context.Background()

rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password
DB: 0, // use default DB
})

// STEP_START hexpire
// Set up hash with fields
rdb.HSet(ctx, "myhash", "field1", "Hello", "field2", "World")

// Set expiration on hash fields
res1, err := rdb.HExpire(ctx, "myhash", 10*time.Second, "field1", "field2").Result()

if err != nil {
fmt.Println(err)
}

fmt.Println(res1) // >>> [1 1]

// Check TTL of the fields
res2, err := rdb.HTTL(ctx, "myhash", "field1", "field2").Result()

if err != nil {
fmt.Println(err)
}

fmt.Println(len(res2)) // >>> 2

// Try to set expiration on non-existent field
res3, err := rdb.HExpire(ctx, "myhash", 10*time.Second, "nonexistent").Result()

if err != nil {
fmt.Println(err)
}

fmt.Println(res3) // >>> [-2]

// Clean up
rdb.Del(ctx, "myhash")
// STEP_END

// Output:
// [1 1]
// 2
// [-2]
}
32 changes: 31 additions & 1 deletion local_examples/cmds_hash/jedis/CmdsHashExample.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.Map;
import java.util.List;
import java.util.Collections;
import java.util.Arrays;

// HIDE_START
import redis.clients.jedis.UnifiedJedis;
Expand All @@ -17,6 +18,7 @@
import static java.util.stream.Collectors.toList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

// HIDE_START
public class CmdsHashExample {
Expand Down Expand Up @@ -159,13 +161,41 @@ public void run() {
System.out.println(hValsResult2);
// >>> [Hello, World]
// STEP_END
// REMOVE_START
// REMOVE_START
// Tests for 'hvals' step.
assertEquals(2, hValsResult1);
assertEquals("[Hello, World]", hValsResult2.toString());
jedis.del("myhash");
// REMOVE_END

// STEP_START hexpire
// Set up hash with fields
Map<String, String> hExpireExampleParams = new HashMap<>();
hExpireExampleParams.put("field1", "Hello");
hExpireExampleParams.put("field2", "World");
jedis.hset("myhash", hExpireExampleParams);

// Set expiration on hash fields
List<Long> hExpireResult1 = jedis.hexpire("myhash", 10, "field1", "field2");
System.out.println(hExpireResult1); // >>> [1, 1]

// Check TTL of the fields
List<Long> hExpireResult2 = jedis.httl("myhash", "field1", "field2");
System.out.println(hExpireResult2.size()); // >>> 2

// Try to set expiration on non-existent field
List<Long> hExpireResult3 = jedis.hexpire("myhash", 10, "nonexistent");
System.out.println(hExpireResult3); // >>> [-2]
// STEP_END
// REMOVE_START
// Tests for 'hexpire' step.
assertEquals(Arrays.asList(1L, 1L), hExpireResult1);
assertEquals(2, hExpireResult2.size());
assertTrue(hExpireResult2.stream().allMatch(ttl -> ttl > 0)); // TTL should be positive
assertEquals(Arrays.asList(-2L), hExpireResult3);
jedis.del("myhash");
// REMOVE_END

// HIDE_START
jedis.close();
}
Expand Down
43 changes: 43 additions & 0 deletions local_examples/cmds_hash/lettuce-async/CmdsHashExample.java
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,49 @@ public void run() {
// REMOVE_START
asyncCommands.del("myhash").toCompletableFuture().join();
// REMOVE_END

// STEP_START hexpire
// Set up hash with fields
Map<String, String> hExpireExampleParams = new HashMap<>();
hExpireExampleParams.put("field1", "Hello");
hExpireExampleParams.put("field2", "World");

CompletableFuture<Void> hExpireExample = asyncCommands.hset("myhash", hExpireExampleParams).thenCompose(res1 -> {
// REMOVE_START
assertThat(res1).isEqualTo(2L);
// REMOVE_END
// Set expiration on hash fields
return asyncCommands.hexpire("myhash", 10, "field1", "field2");
}).thenCompose(res2 -> {
System.out.println(res2);
// >>> [1, 1]
// REMOVE_START
assertThat(res2).isEqualTo(Arrays.asList(1L, 1L));
// REMOVE_END
// Check TTL of the fields
return asyncCommands.httl("myhash", "field1", "field2");
}).thenCompose(res3 -> {
System.out.println(res3.size());
// >>> 2
// REMOVE_START
assertThat(res3.size()).isEqualTo(2);
assertThat(res3.stream().allMatch(ttl -> ttl > 0)).isTrue(); // TTL should be positive
// REMOVE_END
// Try to set expiration on non-existent field
return asyncCommands.hexpire("myhash", 10, "nonexistent");
}).thenAccept(res4 -> {
System.out.println(res4);
// >>> [-2]
// REMOVE_START
assertThat(res4).isEqualTo(Arrays.asList(-2L));
// REMOVE_END
}).toCompletableFuture();
// STEP_END

hExpireExample.join();
// REMOVE_START
asyncCommands.del("myhash").toCompletableFuture().join();
// REMOVE_END
} finally {
redisClient.shutdown();
}
Expand Down
52 changes: 52 additions & 0 deletions local_examples/cmds_hash/lettuce-reactive/CmdsHashExample.java
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,58 @@ public void run() {
// REMOVE_START
reactiveCommands.del("myhash").block();
// REMOVE_END

// STEP_START hexpire
// Set up hash with fields
Map<String, String> hExpireExampleParams = new HashMap<>();
hExpireExampleParams.put("field1", "Hello");
hExpireExampleParams.put("field2", "World");

Mono<Long> hExpireExample1 = reactiveCommands.hset("myhash", hExpireExampleParams).doOnNext(result -> {
// REMOVE_START
assertThat(result).isEqualTo(2L);
// REMOVE_END
});

hExpireExample1.block();

// Set expiration on hash fields
Mono<List<Long>> hExpireExample2 = reactiveCommands.hexpire("myhash", 10, "field1", "field2").collectList().doOnNext(result -> {
System.out.println(result);
// >>> [1, 1]
// REMOVE_START
assertThat(result).isEqualTo(Arrays.asList(1L, 1L));
// REMOVE_END
});

hExpireExample2.block();

// Check TTL of the fields
Mono<List<Long>> hExpireExample3 = reactiveCommands.httl("myhash", "field1", "field2").collectList().doOnNext(result -> {
System.out.println(result.size());
// >>> 2
// REMOVE_START
assertThat(result.size()).isEqualTo(2);
assertThat(result.stream().allMatch(ttl -> ttl > 0)).isTrue(); // TTL should be positive
// REMOVE_END
});

hExpireExample3.block();

// Try to set expiration on non-existent field
Mono<List<Long>> hExpireExample4 = reactiveCommands.hexpire("myhash", 10, "nonexistent").collectList().doOnNext(result -> {
System.out.println(result);
// >>> [-2]
// REMOVE_START
assertThat(result).isEqualTo(Arrays.asList(-2L));
// REMOVE_END
});
// STEP_END

hExpireExample4.block();
// REMOVE_START
reactiveCommands.del("myhash").block();
// REMOVE_END
} finally {
redisClient.shutdown();
}
Expand Down
27 changes: 27 additions & 0 deletions local_examples/cmds_hash/node-redis/cmds-hash.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,33 @@ await client.del('myhash')
// REMOVE_END
// STEP_END

// STEP_START hexpire
// Set up hash with fields
await client.hSet('myhash', {
'field1': 'Hello',
'field2': 'World'
})

// Set expiration on hash fields
const res14 = await client.hExpire('myhash', ['field1', 'field2'], 10)
console.log(res14) // [1, 1]

// Check TTL of the fields
const res15 = await client.hTTL('myhash', ['field1', 'field2'])
console.log(res15) // [10, 10] (or close to 10)

// Try to set expiration on non-existent field
const res16 = await client.hExpire('myhash', ['nonexistent'], 10)
console.log(res16) // [-2]

// REMOVE_START
assert.deepEqual(res14, [1, 1]);
assert(res15.every(ttl => ttl > 0)); // TTL should be positive
assert.deepEqual(res16, [-2]);
await client.del('myhash')
// REMOVE_END
// STEP_END

// HIDE_START
await client.close();
// HIDE_END
28 changes: 28 additions & 0 deletions local_examples/cmds_hash/predis/CmdsHashTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,34 @@ public function testCmdsHash(): void
$this->assertEquals('OK', $hValsResult1);
$this->assertEquals(['Hello', 'World'], $hValsResult2);

// STEP_START hexpire
echo "\n--- HEXPIRE Command ---\n";
// Clean up first
$this->redis->del('myhash');

// Set up hash with fields
$hExpireResult1 = $this->redis->hmset('myhash', ['field1' => 'Hello', 'field2' => 'World']);
echo "HMSET myhash field1 Hello field2 World: " . ($hExpireResult1 ? 'OK' : 'FAIL') . "\n"; // >>> OK

// Set expiration on hash fields
$hExpireResult2 = $this->redis->hexpire('myhash', 10, ['field1', 'field2']);
echo "HEXPIRE myhash 10 FIELDS field1 field2: " . json_encode($hExpireResult2) . "\n"; // >>> [1,1]

// Check TTL of the fields
$hExpireResult3 = $this->redis->httl('myhash', ['field1', 'field2']);
echo "HTTL myhash FIELDS field1 field2 count: " . count($hExpireResult3) . "\n"; // >>> 2

// Try to set expiration on non-existent field
$hExpireResult4 = $this->redis->hexpire('myhash', 10, ['nonexistent']);
echo "HEXPIRE myhash 10 FIELDS nonexistent: " . json_encode($hExpireResult4) . "\n"; // >>> [-2]
// STEP_END

$this->assertEquals('OK', $hExpireResult1);
$this->assertEquals([1, 1], $hExpireResult2);
$this->assertEquals(2, count($hExpireResult3));
$this->assertTrue(array_reduce($hExpireResult3, function($carry, $ttl) { return $carry && $ttl > 0; }, true)); // TTL should be positive
$this->assertEquals([-2], $hExpireResult4);

echo "\n=== All Hash Commands Tests Passed! ===\n";
}

Expand Down
24 changes: 24 additions & 0 deletions local_examples/cmds_hash/redis-py/cmds_hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,28 @@
assert res11 == [ "Hello", "World" ]
r.delete("myhash")
# REMOVE_END
# STEP_END

# STEP_START hexpire
# Set up hash with fields
r.hset("myhash", mapping={"field1": "Hello", "field2": "World"})

# Set expiration on hash fields
res12 = r.hexpire("myhash", 10, "field1", "field2")
print(res12) # >>> [1, 1]

# Check TTL of the fields
res13 = r.httl("myhash", "field1", "field2")
print(res13) # >>> [10, 10] (or close to 10)

# Try to set expiration on non-existent field
res14 = r.hexpire("myhash", 10, "nonexistent")
print(res14) # >>> [-2]

# REMOVE_START
assert res12 == [1, 1]
assert all(ttl > 0 for ttl in res13) # TTL should be positive
assert res14 == [-2]
r.delete("myhash")
# REMOVE_END
# STEP_END
Loading