From 48553bcdda89e5e9e943dbca53d73f055d99b5a8 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Fri, 10 Jul 2026 10:09:09 +0100 Subject: [PATCH 1/3] DOC-6827 Add Lettuce probabilistic data type examples and rundown page Stage Lettuce async + reactive doctests for the bloom, cuckoo, CMS, top-k, t-digest and HLL tutorial sets plus the combined home_prob_dts set, and add the client-specific rundown page at content/develop/clients/lettuce/prob.md. This is preemptive work: probabilistic command support is not in a released lettuce-core yet, so the examples are written against the Lettuce feature PRs (BF/CF/top-k merged to main; CMS and t-digest still open on the Dgramada fork, so their signatures may still shift). A Codex review flagged every non-HLL call as an unknown method - that is expected against the repo's pinned released jar, not a defect. Multi-step sets (t-digest, home_prob_dts) are authored as self-contained per-STEP blocks rather than one method chain threaded through the STEP markers, because the clients-example shortcode extracts each step by line range and a threaded chain renders as broken mid-chain fragments. Learned: multi-step doctests must be self-contained per STEP block or the shortcode extracts broken mid-chain fragments Constraint: each STEP_START/STEP_END block must stand alone (its own async chain, or per-statement reactive blocks), never span a single threaded method chain Rejected: one async/reactive chain threaded through all STEP markers | shortcode extracts per-step line ranges, so a threaded chain yields uncompilable fragments Recheck: when Lettuce CMS (redis/lettuce#3821) and t-digest (#3823) merge and release - re-pin lettuce-core, re-run the doctest harness so the REMOVE-block asserts become real oracles, and re-diff the CMS/t-digest signatures Ticket: DOC-6827 Co-Authored-By: Claude Opus 4.8 (1M context) --- content/develop/clients/lettuce/prob.md | 229 +++++++++++ .../lettuce-async/BloomFilterExample.java | 97 +++++ .../lettuce-reactive/BloomFilterExample.java | 101 +++++ .../lettuce-async/CMSExample.java | 98 +++++ .../lettuce-reactive/CMSExample.java | 100 +++++ .../lettuce-async/CuckooFilterExample.java | 90 ++++ .../lettuce-reactive/CuckooFilterExample.java | 88 ++++ .../lettuce-async/HyperLogLogExample.java | 91 ++++ .../lettuce-reactive/HyperLogLogExample.java | 88 ++++ .../lettuce-async/HomeProbExample.java | 388 ++++++++++++++++++ .../lettuce-reactive/HomeProbExample.java | 346 ++++++++++++++++ .../lettuce-async/TDigestExample.java | 177 ++++++++ .../lettuce-reactive/TDigestExample.java | 138 +++++++ .../lettuce-async/TopKExample.java | 88 ++++ .../lettuce-reactive/TopKExample.java | 91 ++++ 15 files changed, 2210 insertions(+) create mode 100644 content/develop/clients/lettuce/prob.md create mode 100644 local_examples/bf_tutorial/lettuce-async/BloomFilterExample.java create mode 100644 local_examples/bf_tutorial/lettuce-reactive/BloomFilterExample.java create mode 100644 local_examples/cms_tutorial/lettuce-async/CMSExample.java create mode 100644 local_examples/cms_tutorial/lettuce-reactive/CMSExample.java create mode 100644 local_examples/cuckoo_tutorial/lettuce-async/CuckooFilterExample.java create mode 100644 local_examples/cuckoo_tutorial/lettuce-reactive/CuckooFilterExample.java create mode 100644 local_examples/hll_tutorial/lettuce-async/HyperLogLogExample.java create mode 100644 local_examples/hll_tutorial/lettuce-reactive/HyperLogLogExample.java create mode 100644 local_examples/home_prob_dts/lettuce-async/HomeProbExample.java create mode 100644 local_examples/home_prob_dts/lettuce-reactive/HomeProbExample.java create mode 100644 local_examples/tdigest_tutorial/lettuce-async/TDigestExample.java create mode 100644 local_examples/tdigest_tutorial/lettuce-reactive/TDigestExample.java create mode 100644 local_examples/topk_tutorial/lettuce-async/TopKExample.java create mode 100644 local_examples/topk_tutorial/lettuce-reactive/TopKExample.java diff --git a/content/develop/clients/lettuce/prob.md b/content/develop/clients/lettuce/prob.md new file mode 100644 index 0000000000..a16094002c --- /dev/null +++ b/content/develop/clients/lettuce/prob.md @@ -0,0 +1,229 @@ +--- +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +description: Learn how to use approximate calculations with Redis. +linkTitle: Probabilistic data types +title: Probabilistic data types +weight: 45 +--- + +Redis supports several +[probabilistic data types]({{< relref "/develop/data-types/probabilistic" >}}) +that let you calculate values approximately rather than exactly. +The types fall into two basic categories: + +- [Set operations](#set-operations): These types let you calculate (approximately) + the number of items in a set of distinct values, and whether or not a given value is + a member of a set. +- [Statistics](#statistics): These types give you an approximation of + statistics such as the quantiles, ranks, and frequencies of numeric data points in + a list. + +To see why these approximate calculations would be useful, consider the task of +counting the number of distinct IP addresses that access a website in one day. + +Assuming that you already have code that supplies you with each IP +address as a string, you could record the addresses in Redis using +a [set]({{< relref "/develop/data-types/sets" >}}): + +```java +commands.sadd("ip_tracker", newIpAddress); +``` + +The set can only contain each key once, so if the same address +appears again during the day, the new instance will not change +the set. At the end of the day, you could get the exact number of +distinct addresses using the `scard()` method: + +```java +RedisFuture numDistinctIps = commands.scard("ip_tracker"); +``` + +This approach is simple, effective, and precise but if your website +is very busy, the `ip_tracker` set could become very large and consume +a lot of memory. + +You would probably round the count of distinct IP addresses to the +nearest thousand or more to deliver the usage statistics, so +getting it exactly right is not important. It would be useful +if you could trade off some accuracy in exchange for lower memory +consumption. The probabilistic data types provide exactly this kind of +trade-off. Specifically, you can count the approximate number of items in a +set using the [HyperLogLog](#set-cardinality) data type, as described below. + +In general, the probabilistic data types let you perform approximations with a +bounded degree of error that have much lower memory consumption or execution +time than the equivalent precise calculations. + +The examples below use the asynchronous and reactive command APIs, but the +probabilistic commands are also available through the synchronous API. You can +access all of these commands directly from the standard Lettuce command +interfaces (for example, `RedisAsyncCommands` or `RedisReactiveCommands`), +just like the core Redis commands. + +## Set operations + +Redis supports the following approximate set operations: + +- [Membership](#set-membership): The + [Bloom filter]({{< relref "/develop/data-types/probabilistic/bloom-filter" >}}) and + [Cuckoo filter]({{< relref "/develop/data-types/probabilistic/cuckoo-filter" >}}) + data types let you track whether or not a given item is a member of a set. +- [Cardinality](#set-cardinality): The + [HyperLogLog]({{< relref "/develop/data-types/probabilistic/hyperloglogs" >}}) + data type gives you an approximate value for the number of items in a set, also + known as the *cardinality* of the set. + +The sections below describe these operations in more detail. + +### Set membership + +[Bloom filter]({{< relref "/develop/data-types/probabilistic/bloom-filter" >}}) and +[Cuckoo filter]({{< relref "/develop/data-types/probabilistic/cuckoo-filter" >}}) +objects provide a set membership operation that lets you track whether or not a +particular item has been added to a set. These two types provide different +trade-offs for memory usage and speed, so you can select the best one for your +use case. Note that for both types, there is an asymmetry between presence and +absence of items in the set. If an item is reported as absent, then it is definitely +absent, but if it is reported as present, then there is a small chance it may really be +absent. + +Instead of storing strings directly, like a [set]({{< relref "/develop/data-types/sets" >}}), +a Bloom filter records the presence or absence of the +[hash value](https://en.wikipedia.org/wiki/Hash_function) of a string. +This gives a very compact representation of the +set's membership with a fixed memory size, regardless of how many items you +add. The following example adds some names to a Bloom filter representing +a list of users and checks for the presence or absence of users in the list. + +{{< clients-example set="home_prob_dts" step="bloom" lang_filter="Java-Async,Java-Reactive" description="Foundational: Use Bloom filters for memory-efficient set membership testing with false positive possibility" difficulty="beginner" >}} +{{< /clients-example >}} + +A Cuckoo filter has similar features to a Bloom filter, but also supports +a deletion operation to remove hashes from a set, as shown in the example +below. + +{{< clients-example set="home_prob_dts" step="cuckoo" lang_filter="Java-Async,Java-Reactive" description="Foundational: Use Cuckoo filters for set membership testing with deletion support and faster lookups than Bloom filters" difficulty="beginner" >}} +{{< /clients-example >}} + +Which of these two data types you choose depends on your use case. +Bloom filters are generally faster than Cuckoo filters when adding new items, +and also have better memory usage. Cuckoo filters are generally faster +at checking membership and also support the delete operation. See the +[Bloom filter]({{< relref "/develop/data-types/probabilistic/bloom-filter" >}}) and +[Cuckoo filter]({{< relref "/develop/data-types/probabilistic/cuckoo-filter" >}}) +reference pages for more information and comparison between the two types. + +### Set cardinality + +A [HyperLogLog]({{< relref "/develop/data-types/probabilistic/hyperloglogs" >}}) +object calculates the cardinality of a set. As you add +items, the HyperLogLog tracks the number of distinct set members but +doesn't let you retrieve them or query which items have been added. +You can also merge two or more HyperLogLogs to find the cardinality of the +[union](https://en.wikipedia.org/wiki/Union_(set_theory)) of the sets they +represent. + +{{< clients-example set="home_prob_dts" step="hyperloglog" lang_filter="Java-Async,Java-Reactive" description="Foundational: Estimate set cardinality with HyperLogLog for memory-efficient counting of distinct items" difficulty="beginner" >}} +{{< /clients-example >}} + +The main benefit that HyperLogLogs offer is their very low +memory usage. They can count up to 2^64 items with less than +1% standard error using a maximum 12KB of memory. This makes +them very useful for counting things like the total of distinct +IP addresses that access a website or the total of distinct +bank card numbers that make purchases within a day. + +## Statistics + +Redis supports several approximate statistical calculations +on numeric data sets: + +- [Frequency](#frequency): The + [Count-min sketch]({{< relref "/develop/data-types/probabilistic/count-min-sketch" >}}) + data type lets you find the approximate frequency of a labeled item in a data stream. +- [Quantiles](#quantiles): The + [t-digest]({{< relref "/develop/data-types/probabilistic/t-digest" >}}) + data type estimates the quantile of a query value in a data stream. +- [Ranking](#ranking): The + [Top-K]({{< relref "/develop/data-types/probabilistic/top-k" >}}) data type + estimates the ranking of labeled items by frequency in a data stream. + +The sections below describe these operations in more detail. + +### Frequency + +A [Count-min sketch]({{< relref "/develop/data-types/probabilistic/count-min-sketch" >}}) +(CMS) object keeps count of a set of related items represented by +string labels. The count is approximate, but you can specify +how close you want to keep the count to the true value (as a fraction) +and the acceptable probability of failing to keep it in this +desired range. For example, you can request that the count should +stay within 0.1% of the true value and have a 0.05% probability +of going outside this limit. The example below shows how to create +a Count-min sketch object, add data to it, and then query it. + +{{< clients-example set="home_prob_dts" step="cms" lang_filter="Java-Async,Java-Reactive" description="Foundational: Track approximate item frequencies with Count-min sketch for memory-efficient statistics on data streams" difficulty="intermediate" >}} +{{< /clients-example >}} + +The advantage of using a CMS over keeping an exact count with a +[sorted set]({{< relref "/develop/data-types/sorted-sets" >}}) +is that that a CMS has very low and fixed memory usage, even for +large numbers of items. Use CMS objects to keep daily counts of +items sold, accesses to individual web pages on your site, and +other similar statistics. + +### Quantiles + +A [quantile](https://en.wikipedia.org/wiki/Quantile) is the value +below which a certain fraction of samples lie. For example, with +a set of measurements of people's heights, the quantile of 0.75 is +the value of height below which 75% of all people's heights lie. +[Percentiles](https://en.wikipedia.org/wiki/Percentile) are equivalent +to quantiles, except that the fraction is expressed as a percentage. + +A [t-digest]({{< relref "/develop/data-types/probabilistic/t-digest" >}}) +object can estimate quantiles from a set of values added to it +without having to store each value in the set explicitly. This can +save a lot of memory when you have a large number of samples. + +The example below shows how to add data samples to a t-digest +object and obtain some basic statistics, such as the minimum and +maximum values, the quantile of 0.75, and the +[cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function) +(CDF), which is effectively the inverse of the quantile function. It also +shows how to merge two or more t-digest objects to query the combined +data set. + +{{< clients-example set="home_prob_dts" step="tdigest" lang_filter="Java-Async,Java-Reactive" description="Foundational: Estimate quantiles and percentiles with t-digest for memory-efficient statistical analysis of large datasets" difficulty="intermediate" >}} +{{< /clients-example >}} + +A t-digest object also supports several other related commands, such +as querying by rank. See the +[t-digest]({{< relref "/develop/data-types/probabilistic/t-digest" >}}) +reference for more information. + +### Ranking + +A [Top-K]({{< relref "/develop/data-types/probabilistic/top-k" >}}) +object estimates the rankings of different labeled items in a data +stream according to frequency. For example, you could use this to +track the top ten most frequently-accessed pages on a website, or the +top five most popular items sold. + +The example below adds several different items to a Top-K object +that tracks the top three items (this is the second parameter to +the `topKReserve()` method). It also shows how to list the +top *k* items and query whether or not a given item is in the +list. + +{{< clients-example set="home_prob_dts" step="topk" lang_filter="Java-Async,Java-Reactive" description="Foundational: Track top K most frequent items in a data stream with Top-K for efficient ranking without storing all items" difficulty="intermediate" >}} +{{< /clients-example >}} diff --git a/local_examples/bf_tutorial/lettuce-async/BloomFilterExample.java b/local_examples/bf_tutorial/lettuce-async/BloomFilterExample.java new file mode 100644 index 0000000000..c3c121bd68 --- /dev/null +++ b/local_examples/bf_tutorial/lettuce-async/BloomFilterExample.java @@ -0,0 +1,97 @@ +// EXAMPLE: bf_tutorial +package io.redis.examples.async; + +// HIDE_START +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.async.RedisAsyncCommands; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +// REMOVE_START +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END +// HIDE_END + +public class BloomFilterExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisAsyncCommands asyncCommands = connection.async(); + // HIDE_END + + // STEP_START bloom + // REMOVE_START + asyncCommands.del("bikes:models").toCompletableFuture().join(); + // REMOVE_END + CompletableFuture bloomExample = asyncCommands + .bfReserve("bikes:models", 0.01, 1000) + .thenCompose(res1 -> { + System.out.println(res1); + // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.bfAdd("bikes:models", "Smoky Mountain Striker"); + }) + .thenCompose(res2 -> { + System.out.println(res2); + // >>> true + // REMOVE_START + assertThat(res2).isTrue(); + // REMOVE_END + return asyncCommands.bfExists("bikes:models", "Smoky Mountain Striker"); + }) + .thenCompose(res3 -> { + System.out.println(res3); + // >>> true + // REMOVE_START + assertThat(res3).isTrue(); + // REMOVE_END + return asyncCommands.bfMAdd("bikes:models", + "Rocky Mountain Racer", + "Cloudy City Cruiser", + "Windy City Wippet"); + }) + .thenCompose(res4 -> { + System.out.println(res4); + // >>> [true, true, true] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[true, true, true]"); + // REMOVE_END + return asyncCommands.bfMExists("bikes:models", + "Rocky Mountain Racer", + "Cloudy City Cruiser", + "Windy City Wippet"); + }) + .thenAccept(res5 -> { + System.out.println(res5); + // >>> [true, true, true] + // REMOVE_START + assertThat(res5.toString()).isEqualTo("[true, true, true]"); + // REMOVE_END + }) + .toCompletableFuture(); + + bloomExample.join(); + // STEP_END + + // REMOVE_START + asyncCommands.del("bikes:models").toCompletableFuture().join(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/bf_tutorial/lettuce-reactive/BloomFilterExample.java b/local_examples/bf_tutorial/lettuce-reactive/BloomFilterExample.java new file mode 100644 index 0000000000..d73bf7a625 --- /dev/null +++ b/local_examples/bf_tutorial/lettuce-reactive/BloomFilterExample.java @@ -0,0 +1,101 @@ +// EXAMPLE: bf_tutorial +// HIDE_START +package io.redis.examples.reactive; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.Value; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +// REMOVE_START +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END + +import reactor.core.publisher.Mono; + +import java.util.List; +import java.util.stream.Collectors; +// HIDE_END + +public class BloomFilterExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisReactiveCommands reactiveCommands = connection.reactive(); + // HIDE_END + + // STEP_START bloom + // REMOVE_START + reactiveCommands.del("bikes:models").block(); + // REMOVE_END + Mono bloomExample = reactiveCommands + .bfReserve("bikes:models", 0.01, 1000) + .doOnNext(res1 -> { + System.out.println(res1); + // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .then(reactiveCommands.bfAdd("bikes:models", "Smoky Mountain Striker")) + .doOnNext(res2 -> { + System.out.println(res2); + // >>> true + // REMOVE_START + assertThat(res2).isTrue(); + // REMOVE_END + }) + .then(reactiveCommands.bfExists("bikes:models", "Smoky Mountain Striker")) + .doOnNext(res3 -> { + System.out.println(res3); + // >>> true + // REMOVE_START + assertThat(res3).isTrue(); + // REMOVE_END + }) + .thenMany(reactiveCommands.bfMAdd("bikes:models", + "Rocky Mountain Racer", + "Cloudy City Cruiser", + "Windy City Wippet")) + .map(Value::getValue) + .collectList() + .doOnNext(res4 -> { + System.out.println(res4); + // >>> [true, true, true] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[true, true, true]"); + // REMOVE_END + }) + .thenMany(reactiveCommands.bfMExists("bikes:models", + "Rocky Mountain Racer", + "Cloudy City Cruiser", + "Windy City Wippet")) + .collectList() + .doOnNext(res5 -> { + System.out.println(res5); + // >>> [true, true, true] + // REMOVE_START + assertThat(res5.toString()).isEqualTo("[true, true, true]"); + // REMOVE_END + }) + .then(); + + bloomExample.block(); + // STEP_END + + // REMOVE_START + reactiveCommands.del("bikes:models").block(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/cms_tutorial/lettuce-async/CMSExample.java b/local_examples/cms_tutorial/lettuce-async/CMSExample.java new file mode 100644 index 0000000000..ae417966ab --- /dev/null +++ b/local_examples/cms_tutorial/lettuce-async/CMSExample.java @@ -0,0 +1,98 @@ +// EXAMPLE: cms_tutorial +package io.redis.examples.async; + +// HIDE_START +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.async.RedisAsyncCommands; +import io.lettuce.core.probabilistic.CMSInfoValue; +import io.lettuce.core.probabilistic.IncrementPair; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +// REMOVE_START +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END +// HIDE_END + +public class CMSExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisAsyncCommands asyncCommands = connection.async(); + // HIDE_END + + // STEP_START cms + // REMOVE_START + asyncCommands.del("bikes:profit").toCompletableFuture().join(); + // REMOVE_END + CompletableFuture cmsExample = asyncCommands + .cmsInitByProb("bikes:profit", 0.001, 0.002) + .thenCompose(res1 -> { + System.out.println(res1); + // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.cmsIncrBy("bikes:profit", + IncrementPair.of("Smoky Mountain Striker", 100L)); + }) + .thenCompose(res2 -> { + System.out.println(res2); + // >>> [100] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[100]"); + // REMOVE_END + return asyncCommands.cmsIncrBy("bikes:profit", + IncrementPair.of("Rocky Mountain Racer", 200L), + IncrementPair.of("Cloudy City Cruiser", 150L)); + }) + .thenCompose(res3 -> { + System.out.println(res3); + // >>> [200, 150] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[200, 150]"); + // REMOVE_END + return asyncCommands.cmsQuery("bikes:profit", "Smoky Mountain Striker"); + }) + .thenCompose(res4 -> { + System.out.println(res4); + // >>> [100] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[100]"); + // REMOVE_END + return asyncCommands.cmsInfo("bikes:profit"); + }) + .thenAccept(res5 -> { + System.out.println(res5.getWidth() + " " + res5.getDepth() + " " + res5.getCount()); + // >>> 2000 9 450 + // REMOVE_START + assertThat(res5.getWidth()).isEqualTo(2000L); + assertThat(res5.getDepth()).isEqualTo(9L); + assertThat(res5.getCount()).isEqualTo(450L); + // REMOVE_END + }) + .toCompletableFuture(); + + cmsExample.join(); + // STEP_END + + // REMOVE_START + asyncCommands.del("bikes:profit").toCompletableFuture().join(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/cms_tutorial/lettuce-reactive/CMSExample.java b/local_examples/cms_tutorial/lettuce-reactive/CMSExample.java new file mode 100644 index 0000000000..bab507679b --- /dev/null +++ b/local_examples/cms_tutorial/lettuce-reactive/CMSExample.java @@ -0,0 +1,100 @@ +// EXAMPLE: cms_tutorial +// HIDE_START +package io.redis.examples.reactive; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +import io.lettuce.core.probabilistic.CMSInfoValue; +import io.lettuce.core.probabilistic.IncrementPair; +// REMOVE_START +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END + +import reactor.core.publisher.Mono; + +import java.util.List; +// HIDE_END + +public class CMSExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisReactiveCommands reactiveCommands = connection.reactive(); + // HIDE_END + + // STEP_START cms + // REMOVE_START + reactiveCommands.del("bikes:profit").block(); + // REMOVE_END + Mono cmsExample = reactiveCommands + .cmsInitByProb("bikes:profit", 0.001, 0.002) + .doOnNext(res1 -> { + System.out.println(res1); + // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .thenMany(reactiveCommands.cmsIncrBy("bikes:profit", + IncrementPair.of("Smoky Mountain Striker", 100L))) + .collectList() + .doOnNext(res2 -> { + System.out.println(res2); + // >>> [100] + // REMOVE_START + assertThat(res2.toString()).isEqualTo("[100]"); + // REMOVE_END + }) + .thenMany(reactiveCommands.cmsIncrBy("bikes:profit", + IncrementPair.of("Rocky Mountain Racer", 200L), + IncrementPair.of("Cloudy City Cruiser", 150L))) + .collectList() + .doOnNext(res3 -> { + System.out.println(res3); + // >>> [200, 150] + // REMOVE_START + assertThat(res3.toString()).isEqualTo("[200, 150]"); + // REMOVE_END + }) + .thenMany(reactiveCommands.cmsQuery("bikes:profit", "Smoky Mountain Striker")) + .collectList() + .doOnNext(res4 -> { + System.out.println(res4); + // >>> [100] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[100]"); + // REMOVE_END + }) + .then(reactiveCommands.cmsInfo("bikes:profit")) + .doOnNext(res5 -> { + System.out.println(res5.getWidth() + " " + res5.getDepth() + " " + res5.getCount()); + // >>> 2000 9 450 + // REMOVE_START + assertThat(res5.getWidth()).isEqualTo(2000L); + assertThat(res5.getDepth()).isEqualTo(9L); + assertThat(res5.getCount()).isEqualTo(450L); + // REMOVE_END + }) + .then(); + + cmsExample.block(); + // STEP_END + + // REMOVE_START + reactiveCommands.del("bikes:profit").block(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/cuckoo_tutorial/lettuce-async/CuckooFilterExample.java b/local_examples/cuckoo_tutorial/lettuce-async/CuckooFilterExample.java new file mode 100644 index 0000000000..a44d157da4 --- /dev/null +++ b/local_examples/cuckoo_tutorial/lettuce-async/CuckooFilterExample.java @@ -0,0 +1,90 @@ +// EXAMPLE: cuckoo_tutorial +package io.redis.examples.async; + +// HIDE_START +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.async.RedisAsyncCommands; + +import java.util.concurrent.CompletableFuture; + +// REMOVE_START +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END +// HIDE_END + +public class CuckooFilterExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisAsyncCommands asyncCommands = connection.async(); + // HIDE_END + + // STEP_START cuckoo + // REMOVE_START + asyncCommands.del("bikes:models").toCompletableFuture().join(); + // REMOVE_END + CompletableFuture cuckooExample = asyncCommands + .cfReserve("bikes:models", 1000000L) + .thenCompose(res1 -> { + System.out.println(res1); + // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.cfAdd("bikes:models", "Smoky Mountain Striker"); + }) + .thenCompose(res2 -> { + System.out.println(res2); + // >>> true + // REMOVE_START + assertThat(res2).isTrue(); + // REMOVE_END + return asyncCommands.cfExists("bikes:models", "Smoky Mountain Striker"); + }) + .thenCompose(res3 -> { + System.out.println(res3); + // >>> true + // REMOVE_START + assertThat(res3).isTrue(); + // REMOVE_END + return asyncCommands.cfExists("bikes:models", "Terrible Bike Name"); + }) + .thenCompose(res4 -> { + System.out.println(res4); + // >>> false + // REMOVE_START + assertThat(res4).isFalse(); + // REMOVE_END + return asyncCommands.cfDel("bikes:models", "Smoky Mountain Striker"); + }) + .thenAccept(res5 -> { + System.out.println(res5); + // >>> true + // REMOVE_START + assertThat(res5).isTrue(); + // REMOVE_END + }) + .toCompletableFuture(); + + cuckooExample.join(); + // STEP_END + + // REMOVE_START + asyncCommands.del("bikes:models").toCompletableFuture().join(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/cuckoo_tutorial/lettuce-reactive/CuckooFilterExample.java b/local_examples/cuckoo_tutorial/lettuce-reactive/CuckooFilterExample.java new file mode 100644 index 0000000000..0aa0dea7d4 --- /dev/null +++ b/local_examples/cuckoo_tutorial/lettuce-reactive/CuckooFilterExample.java @@ -0,0 +1,88 @@ +// EXAMPLE: cuckoo_tutorial +// HIDE_START +package io.redis.examples.reactive; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +// REMOVE_START +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END + +import reactor.core.publisher.Mono; +// HIDE_END + +public class CuckooFilterExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisReactiveCommands reactiveCommands = connection.reactive(); + // HIDE_END + + // STEP_START cuckoo + // REMOVE_START + reactiveCommands.del("bikes:models").block(); + // REMOVE_END + Mono cuckooExample = reactiveCommands + .cfReserve("bikes:models", 1000000L) + .doOnNext(res1 -> { + System.out.println(res1); + // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .then(reactiveCommands.cfAdd("bikes:models", "Smoky Mountain Striker")) + .doOnNext(res2 -> { + System.out.println(res2); + // >>> true + // REMOVE_START + assertThat(res2).isTrue(); + // REMOVE_END + }) + .then(reactiveCommands.cfExists("bikes:models", "Smoky Mountain Striker")) + .doOnNext(res3 -> { + System.out.println(res3); + // >>> true + // REMOVE_START + assertThat(res3).isTrue(); + // REMOVE_END + }) + .then(reactiveCommands.cfExists("bikes:models", "Terrible Bike Name")) + .doOnNext(res4 -> { + System.out.println(res4); + // >>> false + // REMOVE_START + assertThat(res4).isFalse(); + // REMOVE_END + }) + .then(reactiveCommands.cfDel("bikes:models", "Smoky Mountain Striker")) + .doOnNext(res5 -> { + System.out.println(res5); + // >>> true + // REMOVE_START + assertThat(res5).isTrue(); + // REMOVE_END + }) + .then(); + + cuckooExample.block(); + // STEP_END + + // REMOVE_START + reactiveCommands.del("bikes:models").block(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/hll_tutorial/lettuce-async/HyperLogLogExample.java b/local_examples/hll_tutorial/lettuce-async/HyperLogLogExample.java new file mode 100644 index 0000000000..4d19fb2bb5 --- /dev/null +++ b/local_examples/hll_tutorial/lettuce-async/HyperLogLogExample.java @@ -0,0 +1,91 @@ +// EXAMPLE: hll_tutorial +package io.redis.examples.async; + +// HIDE_START +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.async.RedisAsyncCommands; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +// REMOVE_START +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END +// HIDE_END + +public class HyperLogLogExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisAsyncCommands asyncCommands = connection.async(); + // HIDE_END + + // STEP_START pfadd + // REMOVE_START + asyncCommands.del("bikes", "commuter_bikes", "all_bikes").toCompletableFuture().join(); + // REMOVE_END + CompletableFuture pfaddExample = asyncCommands + .pfadd("bikes", "Hyperion", "Deimos", "Phoebe", "Quaoar") + .thenCompose(res1 -> { + System.out.println(res1); + // >>> 1 + // REMOVE_START + assertThat(res1).isEqualTo(1L); + // REMOVE_END + return asyncCommands.pfcount("bikes"); + }) + .thenCompose(res2 -> { + System.out.println(res2); + // >>> 4 + // REMOVE_START + assertThat(res2).isEqualTo(4L); + // REMOVE_END + return asyncCommands.pfadd("commuter_bikes", "Salacia", "Mimas", "Quaoar"); + }) + .thenCompose(res3 -> { + System.out.println(res3); + // >>> 1 + // REMOVE_START + assertThat(res3).isEqualTo(1L); + // REMOVE_END + return asyncCommands.pfmerge("all_bikes", "bikes", "commuter_bikes"); + }) + .thenCompose(res4 -> { + System.out.println(res4); + // >>> OK + // REMOVE_START + assertThat(res4).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.pfcount("all_bikes"); + }) + .thenAccept(res5 -> { + System.out.println(res5); + // >>> 6 + // REMOVE_START + assertThat(res5).isEqualTo(6L); + // REMOVE_END + }) + .toCompletableFuture(); + + pfaddExample.join(); + // STEP_END + + // REMOVE_START + asyncCommands.del("bikes", "commuter_bikes", "all_bikes").toCompletableFuture().join(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/hll_tutorial/lettuce-reactive/HyperLogLogExample.java b/local_examples/hll_tutorial/lettuce-reactive/HyperLogLogExample.java new file mode 100644 index 0000000000..2a8c49cc37 --- /dev/null +++ b/local_examples/hll_tutorial/lettuce-reactive/HyperLogLogExample.java @@ -0,0 +1,88 @@ +// EXAMPLE: hll_tutorial +// HIDE_START +package io.redis.examples.reactive; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +// REMOVE_START +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END + +import reactor.core.publisher.Mono; +// HIDE_END + +public class HyperLogLogExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisReactiveCommands reactiveCommands = connection.reactive(); + // HIDE_END + + // STEP_START pfadd + // REMOVE_START + reactiveCommands.del("bikes", "commuter_bikes", "all_bikes").block(); + // REMOVE_END + Mono pfaddExample = reactiveCommands + .pfadd("bikes", "Hyperion", "Deimos", "Phoebe", "Quaoar") + .doOnNext(res1 -> { + System.out.println(res1); + // >>> 1 + // REMOVE_START + assertThat(res1).isEqualTo(1L); + // REMOVE_END + }) + .then(reactiveCommands.pfcount("bikes")) + .doOnNext(res2 -> { + System.out.println(res2); + // >>> 4 + // REMOVE_START + assertThat(res2).isEqualTo(4L); + // REMOVE_END + }) + .then(reactiveCommands.pfadd("commuter_bikes", "Salacia", "Mimas", "Quaoar")) + .doOnNext(res3 -> { + System.out.println(res3); + // >>> 1 + // REMOVE_START + assertThat(res3).isEqualTo(1L); + // REMOVE_END + }) + .then(reactiveCommands.pfmerge("all_bikes", "bikes", "commuter_bikes")) + .doOnNext(res4 -> { + System.out.println(res4); + // >>> OK + // REMOVE_START + assertThat(res4).isEqualTo("OK"); + // REMOVE_END + }) + .then(reactiveCommands.pfcount("all_bikes")) + .doOnNext(res5 -> { + System.out.println(res5); + // >>> 6 + // REMOVE_START + assertThat(res5).isEqualTo(6L); + // REMOVE_END + }) + .then(); + + pfaddExample.block(); + // STEP_END + + // REMOVE_START + reactiveCommands.del("bikes", "commuter_bikes", "all_bikes").block(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/home_prob_dts/lettuce-async/HomeProbExample.java b/local_examples/home_prob_dts/lettuce-async/HomeProbExample.java new file mode 100644 index 0000000000..6012eef0d8 --- /dev/null +++ b/local_examples/home_prob_dts/lettuce-async/HomeProbExample.java @@ -0,0 +1,388 @@ +// EXAMPLE: home_prob_dts +package io.redis.examples.async; + +// HIDE_START +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.async.RedisAsyncCommands; +import io.lettuce.core.probabilistic.IncrementPair; +import io.lettuce.core.probabilistic.arguments.TopKReserveArgs; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +// REMOVE_START +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END +// HIDE_END + +public class HomeProbExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisAsyncCommands asyncCommands = connection.async(); + // HIDE_END + + // REMOVE_START + asyncCommands.del("recorded_users", "other_users", + "group:1", "group:2", "both_groups", + "items_sold", "male_heights", "female_heights", "all_heights", + "top_3_songs").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START bloom + CompletableFuture bloomExample = asyncCommands + .bfMAdd("recorded_users", "andy", "cameron", "david", "michelle") + .thenCompose(res1 -> { + System.out.println(res1); + // >>> [true, true, true, true] + // REMOVE_START + assertThat(res1.toString()).isEqualTo("[true, true, true, true]"); + // REMOVE_END + return asyncCommands.bfExists("recorded_users", "cameron"); + }) + .thenCompose(res2 -> { + System.out.println(res2); + // >>> true + // REMOVE_START + assertThat(res2).isTrue(); + // REMOVE_END + return asyncCommands.bfExists("recorded_users", "kaitlyn"); + }) + .thenAccept(res3 -> { + System.out.println(res3); + // >>> false + // REMOVE_START + assertThat(res3).isFalse(); + // REMOVE_END + }) + .toCompletableFuture(); + + bloomExample.join(); + // STEP_END + + // STEP_START cuckoo + CompletableFuture cuckooExample = asyncCommands + .cfAdd("other_users", "paolo") + .thenCompose(res4 -> { + System.out.println(res4); + // >>> true + // REMOVE_START + assertThat(res4).isTrue(); + // REMOVE_END + return asyncCommands.cfAdd("other_users", "kaitlyn"); + }) + .thenCompose(res5 -> { + System.out.println(res5); + // >>> true + // REMOVE_START + assertThat(res5).isTrue(); + // REMOVE_END + return asyncCommands.cfAdd("other_users", "rachel"); + }) + .thenCompose(res6 -> { + System.out.println(res6); + // >>> true + // REMOVE_START + assertThat(res6).isTrue(); + // REMOVE_END + return asyncCommands.cfMExists("other_users", "paolo", "rachel", "andy"); + }) + .thenCompose(res7 -> { + System.out.println(res7); + // >>> [true, true, false] + // REMOVE_START + assertThat(res7.toString()).isEqualTo("[true, true, false]"); + // REMOVE_END + return asyncCommands.cfDel("other_users", "paolo"); + }) + .thenCompose(res8 -> { + System.out.println(res8); + // >>> true + // REMOVE_START + assertThat(res8).isTrue(); + // REMOVE_END + return asyncCommands.cfExists("other_users", "paolo"); + }) + .thenAccept(res9 -> { + System.out.println(res9); + // >>> false + // REMOVE_START + assertThat(res9).isFalse(); + // REMOVE_END + }) + .toCompletableFuture(); + + cuckooExample.join(); + // STEP_END + + // STEP_START hyperloglog + CompletableFuture hllExample = asyncCommands + .pfadd("group:1", "andy", "cameron", "david") + .thenCompose(res10 -> { + System.out.println(res10); + // >>> 1 + // REMOVE_START + assertThat(res10).isEqualTo(1L); + // REMOVE_END + return asyncCommands.pfcount("group:1"); + }) + .thenCompose(res11 -> { + System.out.println(res11); + // >>> 3 + // REMOVE_START + assertThat(res11).isEqualTo(3L); + // REMOVE_END + return asyncCommands.pfadd("group:2", "kaitlyn", "michelle", "paolo", "rachel"); + }) + .thenCompose(res12 -> { + System.out.println(res12); + // >>> 1 + // REMOVE_START + assertThat(res12).isEqualTo(1L); + // REMOVE_END + return asyncCommands.pfcount("group:2"); + }) + .thenCompose(res13 -> { + System.out.println(res13); + // >>> 4 + // REMOVE_START + assertThat(res13).isEqualTo(4L); + // REMOVE_END + return asyncCommands.pfmerge("both_groups", "group:1", "group:2"); + }) + .thenCompose(res14 -> { + System.out.println(res14); + // >>> OK + // REMOVE_START + assertThat(res14).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.pfcount("both_groups"); + }) + .thenAccept(res15 -> { + System.out.println(res15); + // >>> 7 + // REMOVE_START + assertThat(res15).isEqualTo(7L); + // REMOVE_END + }) + .toCompletableFuture(); + + hllExample.join(); + // STEP_END + + // STEP_START cms + CompletableFuture cmsExample = asyncCommands + // Specify that you want to keep the counts within 0.01 + // (1%) of the true value with a 0.005 (0.5%) chance + // of going outside this limit. + .cmsInitByProb("items_sold", 0.01, 0.005) + .thenCompose(res16 -> { + System.out.println(res16); + // >>> OK + // REMOVE_START + assertThat(res16).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.cmsIncrBy("items_sold", + IncrementPair.of("bread", 300L), + IncrementPair.of("tea", 200L), + IncrementPair.of("coffee", 200L), + IncrementPair.of("beer", 100L)); + }) + .thenCompose(res17 -> { + List sorted17 = new ArrayList<>(res17); + sorted17.sort(null); + System.out.println(sorted17); + // >>> [100, 200, 200, 300] + // REMOVE_START + assertThat(sorted17.toString()).isEqualTo("[100, 200, 200, 300]"); + // REMOVE_END + return asyncCommands.cmsIncrBy("items_sold", + IncrementPair.of("bread", 100L), + IncrementPair.of("coffee", 150L)); + }) + .thenCompose(res18 -> { + List sorted18 = new ArrayList<>(res18); + sorted18.sort(null); + System.out.println(sorted18); + // >>> [350, 400] + // REMOVE_START + assertThat(sorted18.toString()).isEqualTo("[350, 400]"); + // REMOVE_END + return asyncCommands.cmsQuery("items_sold", "bread", "tea", "coffee", "beer"); + }) + .thenAccept(res19 -> { + List sorted19 = new ArrayList<>(res19); + sorted19.sort(null); + System.out.println(sorted19); + // >>> [100, 200, 350, 400] + // REMOVE_START + assertThat(sorted19.toString()).isEqualTo("[100, 200, 350, 400]"); + // REMOVE_END + }) + .toCompletableFuture(); + + cmsExample.join(); + // STEP_END + + // STEP_START tdigest + CompletableFuture tdigestExample = asyncCommands + .tdigestCreate("male_heights") + .thenCompose(res20 -> { + System.out.println(res20); + // >>> OK + // REMOVE_START + assertThat(res20).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.tdigestAdd("male_heights", + "175.5", "181", "160.8", "152", "177", "196", "164"); + }) + .thenCompose(res21 -> { + System.out.println(res21); + // >>> OK + // REMOVE_START + assertThat(res21).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.tdigestMin("male_heights"); + }) + .thenCompose(res22 -> { + System.out.println(res22); + // >>> 152.0 + // REMOVE_START + assertThat(res22).isEqualTo(152.0); + // REMOVE_END + return asyncCommands.tdigestMax("male_heights"); + }) + .thenCompose(res23 -> { + System.out.println(res23); + // >>> 196.0 + // REMOVE_START + assertThat(res23).isEqualTo(196.0); + // REMOVE_END + return asyncCommands.tdigestQuantile("male_heights", 0.75); + }) + .thenCompose(res24 -> { + System.out.println(res24); + // >>> [181.0] + // REMOVE_START + assertThat(res24.toString()).isEqualTo("[181.0]"); + // REMOVE_END + // Note that the CDF value for 181 is not exactly 0.75. + // Both values are estimates. + return asyncCommands.tdigestCDF("male_heights", "181"); + }) + .thenCompose(res25 -> { + System.out.println(res25); + // >>> [0.7857142857142857] + return asyncCommands.tdigestCreate("female_heights"); + }) + .thenCompose(res26 -> { + System.out.println(res26); + // >>> OK + // REMOVE_START + assertThat(res26).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.tdigestAdd("female_heights", + "155.5", "161", "168.5", "170", "157.5", "163", "171"); + }) + .thenCompose(res27 -> { + System.out.println(res27); + // >>> OK + // REMOVE_START + assertThat(res27).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.tdigestQuantile("female_heights", 0.75); + }) + .thenCompose(res28 -> { + System.out.println(res28); + // >>> [170.0] + // REMOVE_START + assertThat(res28.toString()).isEqualTo("[170.0]"); + // REMOVE_END + return asyncCommands.tdigestMerge("all_heights", "male_heights", "female_heights"); + }) + .thenCompose(res29 -> { + System.out.println(res29); + // >>> OK + // REMOVE_START + assertThat(res29).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.tdigestQuantile("all_heights", 0.75); + }) + .thenAccept(res30 -> { + System.out.println(res30); + // >>> [175.5] + // REMOVE_START + assertThat(res30.toString()).isEqualTo("[175.5]"); + // REMOVE_END + }) + .toCompletableFuture(); + + tdigestExample.join(); + // STEP_END + + // STEP_START topk + CompletableFuture topkExample = asyncCommands + .topKReserve("top_3_songs", 3L, + TopKReserveArgs.Builder.width(2000L).depth(7L).decay(0.925)) + .thenCompose(res31 -> { + System.out.println(res31); + // >>> OK + // REMOVE_START + assertThat(res31).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.topKIncrBy("top_3_songs", + IncrementPair.of("Starfish Trooper", 3000L), + IncrementPair.of("Only one more time", 1850L), + IncrementPair.of("Rock me, Handel", 1325L), + IncrementPair.of("How will anyone know?", 3890L), + IncrementPair.of("Average lover", 4098L), + IncrementPair.of("Road to everywhere", 770L)); + }) + .thenCompose(res32 -> { + System.out.println(res32); + // >>> [null, null, null, null, null, Rock me, Handel] + return asyncCommands.topKList("top_3_songs"); + }) + .thenCompose(res33 -> { + System.out.println(res33); + // >>> [Average lover, How will anyone know?, Starfish Trooper] + // REMOVE_START + assertThat(res33).contains("Average lover", "How will anyone know?", "Starfish Trooper"); + // REMOVE_END + return asyncCommands.topKQuery("top_3_songs", "Starfish Trooper", "Road to everywhere"); + }) + .thenAccept(res34 -> { + System.out.println(res34); + // >>> [true, false] + // REMOVE_START + assertThat(res34.toString()).isEqualTo("[true, false]"); + // REMOVE_END + }) + .toCompletableFuture(); + + topkExample.join(); + // STEP_END + + // REMOVE_START + asyncCommands.del("recorded_users", "other_users", + "group:1", "group:2", "both_groups", + "items_sold", "male_heights", "female_heights", "all_heights", + "top_3_songs").toCompletableFuture().join(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/home_prob_dts/lettuce-reactive/HomeProbExample.java b/local_examples/home_prob_dts/lettuce-reactive/HomeProbExample.java new file mode 100644 index 0000000000..5747f4506d --- /dev/null +++ b/local_examples/home_prob_dts/lettuce-reactive/HomeProbExample.java @@ -0,0 +1,346 @@ +// EXAMPLE: home_prob_dts +// HIDE_START +package io.redis.examples.reactive; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.Value; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +import io.lettuce.core.probabilistic.IncrementPair; +import io.lettuce.core.probabilistic.arguments.TopKReserveArgs; +// REMOVE_START +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END + +import java.util.ArrayList; +import java.util.List; +// HIDE_END + +public class HomeProbExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisReactiveCommands reactiveCommands = connection.reactive(); + // HIDE_END + + // REMOVE_START + reactiveCommands.del("recorded_users", "other_users", + "group:1", "group:2", "both_groups", + "items_sold", "male_heights", "female_heights", "all_heights", + "top_3_songs").block(); + // REMOVE_END + + // STEP_START bloom + List res1 = reactiveCommands + .bfMAdd("recorded_users", "andy", "cameron", "david", "michelle") + .map(Value::getValue) + .collectList() + .block(); + System.out.println(res1); + // >>> [true, true, true, true] + // REMOVE_START + assertThat(res1.toString()).isEqualTo("[true, true, true, true]"); + // REMOVE_END + + Boolean res2 = reactiveCommands.bfExists("recorded_users", "cameron").block(); + System.out.println(res2); + // >>> true + // REMOVE_START + assertThat(res2).isTrue(); + // REMOVE_END + + Boolean res3 = reactiveCommands.bfExists("recorded_users", "kaitlyn").block(); + System.out.println(res3); + // >>> false + // REMOVE_START + assertThat(res3).isFalse(); + // REMOVE_END + // STEP_END + + // STEP_START cuckoo + Boolean res4 = reactiveCommands.cfAdd("other_users", "paolo").block(); + System.out.println(res4); + // >>> true + // REMOVE_START + assertThat(res4).isTrue(); + // REMOVE_END + + Boolean res5 = reactiveCommands.cfAdd("other_users", "kaitlyn").block(); + System.out.println(res5); + // >>> true + // REMOVE_START + assertThat(res5).isTrue(); + // REMOVE_END + + Boolean res6 = reactiveCommands.cfAdd("other_users", "rachel").block(); + System.out.println(res6); + // >>> true + // REMOVE_START + assertThat(res6).isTrue(); + // REMOVE_END + + List res7 = reactiveCommands + .cfMExists("other_users", "paolo", "rachel", "andy") + .collectList() + .block(); + System.out.println(res7); + // >>> [true, true, false] + // REMOVE_START + assertThat(res7.toString()).isEqualTo("[true, true, false]"); + // REMOVE_END + + Boolean res8 = reactiveCommands.cfDel("other_users", "paolo").block(); + System.out.println(res8); + // >>> true + // REMOVE_START + assertThat(res8).isTrue(); + // REMOVE_END + + Boolean res9 = reactiveCommands.cfExists("other_users", "paolo").block(); + System.out.println(res9); + // >>> false + // REMOVE_START + assertThat(res9).isFalse(); + // REMOVE_END + // STEP_END + + // STEP_START hyperloglog + Long res10 = reactiveCommands.pfadd("group:1", "andy", "cameron", "david").block(); + System.out.println(res10); + // >>> 1 + // REMOVE_START + assertThat(res10).isEqualTo(1L); + // REMOVE_END + + Long res11 = reactiveCommands.pfcount("group:1").block(); + System.out.println(res11); + // >>> 3 + // REMOVE_START + assertThat(res11).isEqualTo(3L); + // REMOVE_END + + Long res12 = reactiveCommands.pfadd("group:2", "kaitlyn", "michelle", "paolo", "rachel").block(); + System.out.println(res12); + // >>> 1 + // REMOVE_START + assertThat(res12).isEqualTo(1L); + // REMOVE_END + + Long res13 = reactiveCommands.pfcount("group:2").block(); + System.out.println(res13); + // >>> 4 + // REMOVE_START + assertThat(res13).isEqualTo(4L); + // REMOVE_END + + String res14 = reactiveCommands.pfmerge("both_groups", "group:1", "group:2").block(); + System.out.println(res14); + // >>> OK + // REMOVE_START + assertThat(res14).isEqualTo("OK"); + // REMOVE_END + + Long res15 = reactiveCommands.pfcount("both_groups").block(); + System.out.println(res15); + // >>> 7 + // REMOVE_START + assertThat(res15).isEqualTo(7L); + // REMOVE_END + // STEP_END + + // STEP_START cms + // Specify that you want to keep the counts within 0.01 + // (1%) of the true value with a 0.005 (0.5%) chance + // of going outside this limit. + String res16 = reactiveCommands.cmsInitByProb("items_sold", 0.01, 0.005).block(); + System.out.println(res16); + // >>> OK + // REMOVE_START + assertThat(res16).isEqualTo("OK"); + // REMOVE_END + + List res17 = new ArrayList<>(reactiveCommands + .cmsIncrBy("items_sold", + IncrementPair.of("bread", 300L), + IncrementPair.of("tea", 200L), + IncrementPair.of("coffee", 200L), + IncrementPair.of("beer", 100L)) + .collectList() + .block()); + res17.sort(null); + System.out.println(res17); + // >>> [100, 200, 200, 300] + // REMOVE_START + assertThat(res17.toString()).isEqualTo("[100, 200, 200, 300]"); + // REMOVE_END + + List res18 = new ArrayList<>(reactiveCommands + .cmsIncrBy("items_sold", + IncrementPair.of("bread", 100L), + IncrementPair.of("coffee", 150L)) + .collectList() + .block()); + res18.sort(null); + System.out.println(res18); + // >>> [350, 400] + // REMOVE_START + assertThat(res18.toString()).isEqualTo("[350, 400]"); + // REMOVE_END + + List res19 = new ArrayList<>(reactiveCommands + .cmsQuery("items_sold", "bread", "tea", "coffee", "beer") + .collectList() + .block()); + res19.sort(null); + System.out.println(res19); + // >>> [100, 200, 350, 400] + // REMOVE_START + assertThat(res19.toString()).isEqualTo("[100, 200, 350, 400]"); + // REMOVE_END + // STEP_END + + // STEP_START tdigest + String res20 = reactiveCommands.tdigestCreate("male_heights").block(); + System.out.println(res20); + // >>> OK + // REMOVE_START + assertThat(res20).isEqualTo("OK"); + // REMOVE_END + + String res21 = reactiveCommands.tdigestAdd("male_heights", + "175.5", "181", "160.8", "152", "177", "196", "164").block(); + System.out.println(res21); + // >>> OK + // REMOVE_START + assertThat(res21).isEqualTo("OK"); + // REMOVE_END + + Double res22 = reactiveCommands.tdigestMin("male_heights").block(); + System.out.println(res22); + // >>> 152.0 + // REMOVE_START + assertThat(res22).isEqualTo(152.0); + // REMOVE_END + + Double res23 = reactiveCommands.tdigestMax("male_heights").block(); + System.out.println(res23); + // >>> 196.0 + // REMOVE_START + assertThat(res23).isEqualTo(196.0); + // REMOVE_END + + List res24 = reactiveCommands.tdigestQuantile("male_heights", 0.75) + .collectList().block(); + System.out.println(res24); + // >>> [181.0] + // REMOVE_START + assertThat(res24.toString()).isEqualTo("[181.0]"); + // REMOVE_END + + // Note that the CDF value for 181 is not exactly 0.75. + // Both values are estimates. + List res25 = reactiveCommands.tdigestCDF("male_heights", "181") + .collectList().block(); + System.out.println(res25); + // >>> [0.7857142857142857] + + String res26 = reactiveCommands.tdigestCreate("female_heights").block(); + System.out.println(res26); + // >>> OK + // REMOVE_START + assertThat(res26).isEqualTo("OK"); + // REMOVE_END + + String res27 = reactiveCommands.tdigestAdd("female_heights", + "155.5", "161", "168.5", "170", "157.5", "163", "171").block(); + System.out.println(res27); + // >>> OK + // REMOVE_START + assertThat(res27).isEqualTo("OK"); + // REMOVE_END + + List res28 = reactiveCommands.tdigestQuantile("female_heights", 0.75) + .collectList().block(); + System.out.println(res28); + // >>> [170.0] + // REMOVE_START + assertThat(res28.toString()).isEqualTo("[170.0]"); + // REMOVE_END + + String res29 = reactiveCommands.tdigestMerge("all_heights", "male_heights", "female_heights").block(); + System.out.println(res29); + // >>> OK + // REMOVE_START + assertThat(res29).isEqualTo("OK"); + // REMOVE_END + + List res30 = reactiveCommands.tdigestQuantile("all_heights", 0.75) + .collectList().block(); + System.out.println(res30); + // >>> [175.5] + // REMOVE_START + assertThat(res30.toString()).isEqualTo("[175.5]"); + // REMOVE_END + // STEP_END + + // STEP_START topk + String res31 = reactiveCommands.topKReserve("top_3_songs", 3L, + TopKReserveArgs.Builder.width(2000L).depth(7L).decay(0.925)).block(); + System.out.println(res31); + // >>> OK + // REMOVE_START + assertThat(res31).isEqualTo("OK"); + // REMOVE_END + + List res32 = reactiveCommands + .topKIncrBy("top_3_songs", + IncrementPair.of("Starfish Trooper", 3000L), + IncrementPair.of("Only one more time", 1850L), + IncrementPair.of("Rock me, Handel", 1325L), + IncrementPair.of("How will anyone know?", 3890L), + IncrementPair.of("Average lover", 4098L), + IncrementPair.of("Road to everywhere", 770L)) + .map(v -> v.getValueOrElse(null)) + .collectList() + .block(); + System.out.println(res32); + // >>> [null, null, null, null, null, Rock me, Handel] + + List res33 = reactiveCommands.topKList("top_3_songs").collectList().block(); + System.out.println(res33); + // >>> [Average lover, How will anyone know?, Starfish Trooper] + // REMOVE_START + assertThat(res33).contains("Average lover", "How will anyone know?", "Starfish Trooper"); + // REMOVE_END + + List res34 = reactiveCommands + .topKQuery("top_3_songs", "Starfish Trooper", "Road to everywhere") + .collectList() + .block(); + System.out.println(res34); + // >>> [true, false] + // REMOVE_START + assertThat(res34.toString()).isEqualTo("[true, false]"); + // REMOVE_END + // STEP_END + + // REMOVE_START + reactiveCommands.del("recorded_users", "other_users", + "group:1", "group:2", "both_groups", + "items_sold", "male_heights", "female_heights", "all_heights", + "top_3_songs").block(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/tdigest_tutorial/lettuce-async/TDigestExample.java b/local_examples/tdigest_tutorial/lettuce-async/TDigestExample.java new file mode 100644 index 0000000000..b7f3581225 --- /dev/null +++ b/local_examples/tdigest_tutorial/lettuce-async/TDigestExample.java @@ -0,0 +1,177 @@ +// EXAMPLE: tdigest_tutorial +package io.redis.examples.async; + +// HIDE_START +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.async.RedisAsyncCommands; + +import java.util.concurrent.CompletableFuture; + +// REMOVE_START +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END +// HIDE_END + +public class TDigestExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisAsyncCommands asyncCommands = connection.async(); + // HIDE_END + + // REMOVE_START + asyncCommands.del("bikes:sales", "racer_ages").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START tdig_start + CompletableFuture tdigStartExample = asyncCommands + .tdigestCreate("bikes:sales", 100L) + .thenCompose(res1 -> { + System.out.println(res1); + // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.tdigestAdd("bikes:sales", "21"); + }) + .thenCompose(res2 -> { + System.out.println(res2); + // >>> OK + // REMOVE_START + assertThat(res2).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.tdigestAdd("bikes:sales", "150", "95", "75", "34"); + }) + .thenAccept(res3 -> { + System.out.println(res3); + // >>> OK + // REMOVE_START + assertThat(res3).isEqualTo("OK"); + // REMOVE_END + }) + .toCompletableFuture(); + + tdigStartExample.join(); + // STEP_END + + // STEP_START tdig_cdf + CompletableFuture tdigCdfExample = asyncCommands + .tdigestCreate("racer_ages") + .thenCompose(res4 -> { + System.out.println(res4); + // >>> OK + // REMOVE_START + assertThat(res4).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.tdigestAdd("racer_ages", + "45.88", "44.2", "58.03", "19.76", "39.84", "69.28", + "50.97", "25.41", "19.27", "85.71", "42.63"); + }) + .thenCompose(res5 -> { + System.out.println(res5); + // >>> OK + // REMOVE_START + assertThat(res5).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.tdigestRank("racer_ages", "50"); + }) + .thenCompose(res6 -> { + System.out.println(res6); + // >>> [7] + // REMOVE_START + assertThat(res6.toString()).isEqualTo("[7]"); + // REMOVE_END + return asyncCommands.tdigestRank("racer_ages", "50", "40"); + }) + .thenAccept(res7 -> { + System.out.println(res7); + // >>> [7, 4] + // REMOVE_START + assertThat(res7.toString()).isEqualTo("[7, 4]"); + // REMOVE_END + }) + .toCompletableFuture(); + + tdigCdfExample.join(); + // STEP_END + + // STEP_START tdig_quant + CompletableFuture tdigQuantExample = asyncCommands + .tdigestQuantile("racer_ages", 0.5) + .thenCompose(res8 -> { + System.out.println(res8); + // >>> [44.2] + // REMOVE_START + assertThat(res8.toString()).isEqualTo("[44.2]"); + // REMOVE_END + return asyncCommands.tdigestByRank("racer_ages", 4L); + }) + .thenAccept(res9 -> { + System.out.println(res9); + // >>> [42.63] + // REMOVE_START + assertThat(res9.toString()).isEqualTo("[42.63]"); + // REMOVE_END + }) + .toCompletableFuture(); + + tdigQuantExample.join(); + // STEP_END + + // STEP_START tdig_min + CompletableFuture tdigMinExample = asyncCommands + .tdigestMin("racer_ages") + .thenCompose(res10 -> { + System.out.println(res10); + // >>> 19.27 + // REMOVE_START + assertThat(res10).isEqualTo(19.27); + // REMOVE_END + return asyncCommands.tdigestMax("racer_ages"); + }) + .thenAccept(res11 -> { + System.out.println(res11); + // >>> 85.71 + // REMOVE_START + assertThat(res11).isEqualTo(85.71); + // REMOVE_END + }) + .toCompletableFuture(); + + tdigMinExample.join(); + // STEP_END + + // STEP_START tdig_reset + CompletableFuture tdigResetExample = asyncCommands + .tdigestReset("racer_ages") + .thenAccept(res12 -> { + System.out.println(res12); + // >>> OK + // REMOVE_START + assertThat(res12).isEqualTo("OK"); + // REMOVE_END + }) + .toCompletableFuture(); + + tdigResetExample.join(); + // STEP_END + + // REMOVE_START + asyncCommands.del("bikes:sales", "racer_ages").toCompletableFuture().join(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/tdigest_tutorial/lettuce-reactive/TDigestExample.java b/local_examples/tdigest_tutorial/lettuce-reactive/TDigestExample.java new file mode 100644 index 0000000000..b4fcc11144 --- /dev/null +++ b/local_examples/tdigest_tutorial/lettuce-reactive/TDigestExample.java @@ -0,0 +1,138 @@ +// EXAMPLE: tdigest_tutorial +// HIDE_START +package io.redis.examples.reactive; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +// REMOVE_START +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END + +import java.util.List; +// HIDE_END + +public class TDigestExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisReactiveCommands reactiveCommands = connection.reactive(); + // HIDE_END + + // REMOVE_START + reactiveCommands.del("bikes:sales", "racer_ages").block(); + // REMOVE_END + + // STEP_START tdig_start + String res1 = reactiveCommands.tdigestCreate("bikes:sales", 100L).block(); + System.out.println(res1); + // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + + String res2 = reactiveCommands.tdigestAdd("bikes:sales", "21").block(); + System.out.println(res2); + // >>> OK + // REMOVE_START + assertThat(res2).isEqualTo("OK"); + // REMOVE_END + + String res3 = reactiveCommands.tdigestAdd("bikes:sales", "150", "95", "75", "34").block(); + System.out.println(res3); + // >>> OK + // REMOVE_START + assertThat(res3).isEqualTo("OK"); + // REMOVE_END + // STEP_END + + // STEP_START tdig_cdf + String res4 = reactiveCommands.tdigestCreate("racer_ages").block(); + System.out.println(res4); + // >>> OK + // REMOVE_START + assertThat(res4).isEqualTo("OK"); + // REMOVE_END + + String res5 = reactiveCommands.tdigestAdd("racer_ages", + "45.88", "44.2", "58.03", "19.76", "39.84", "69.28", + "50.97", "25.41", "19.27", "85.71", "42.63").block(); + System.out.println(res5); + // >>> OK + // REMOVE_START + assertThat(res5).isEqualTo("OK"); + // REMOVE_END + + List res6 = reactiveCommands.tdigestRank("racer_ages", "50").collectList().block(); + System.out.println(res6); + // >>> [7] + // REMOVE_START + assertThat(res6.toString()).isEqualTo("[7]"); + // REMOVE_END + + List res7 = reactiveCommands.tdigestRank("racer_ages", "50", "40").collectList().block(); + System.out.println(res7); + // >>> [7, 4] + // REMOVE_START + assertThat(res7.toString()).isEqualTo("[7, 4]"); + // REMOVE_END + // STEP_END + + // STEP_START tdig_quant + List res8 = reactiveCommands.tdigestQuantile("racer_ages", 0.5).collectList().block(); + System.out.println(res8); + // >>> [44.2] + // REMOVE_START + assertThat(res8.toString()).isEqualTo("[44.2]"); + // REMOVE_END + + List res9 = reactiveCommands.tdigestByRank("racer_ages", 4L).collectList().block(); + System.out.println(res9); + // >>> [42.63] + // REMOVE_START + assertThat(res9.toString()).isEqualTo("[42.63]"); + // REMOVE_END + // STEP_END + + // STEP_START tdig_min + Double res10 = reactiveCommands.tdigestMin("racer_ages").block(); + System.out.println(res10); + // >>> 19.27 + // REMOVE_START + assertThat(res10).isEqualTo(19.27); + // REMOVE_END + + Double res11 = reactiveCommands.tdigestMax("racer_ages").block(); + System.out.println(res11); + // >>> 85.71 + // REMOVE_START + assertThat(res11).isEqualTo(85.71); + // REMOVE_END + // STEP_END + + // STEP_START tdig_reset + String res12 = reactiveCommands.tdigestReset("racer_ages").block(); + System.out.println(res12); + // >>> OK + // REMOVE_START + assertThat(res12).isEqualTo("OK"); + // REMOVE_END + // STEP_END + + // REMOVE_START + reactiveCommands.del("bikes:sales", "racer_ages").block(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/topk_tutorial/lettuce-async/TopKExample.java b/local_examples/topk_tutorial/lettuce-async/TopKExample.java new file mode 100644 index 0000000000..6302ef9155 --- /dev/null +++ b/local_examples/topk_tutorial/lettuce-async/TopKExample.java @@ -0,0 +1,88 @@ +// EXAMPLE: topk_tutorial +package io.redis.examples.async; + +// HIDE_START +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.async.RedisAsyncCommands; +import io.lettuce.core.probabilistic.arguments.TopKReserveArgs; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +// REMOVE_START +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END +// HIDE_END + +public class TopKExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisAsyncCommands asyncCommands = connection.async(); + // HIDE_END + + // STEP_START topk + // REMOVE_START + asyncCommands.del("bikes:keywords").toCompletableFuture().join(); + // REMOVE_END + CompletableFuture topKExample = asyncCommands + .topKReserve("bikes:keywords", 5L, + TopKReserveArgs.Builder.width(2000L).depth(7L).decay(0.925)) + .thenCompose(res1 -> { + System.out.println(res1); + // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + return asyncCommands.topKAdd("bikes:keywords", + "store", "seat", "handlebars", "handles", "pedals", + "tires", "store", "seat"); + }) + .thenCompose(res2 -> { + System.out.println(res2); + // >>> [null, null, null, null, null, handlebars, null, null] + // REMOVE_START + assertThat(res2.size()).isEqualTo(8); + // REMOVE_END + return asyncCommands.topKList("bikes:keywords"); + }) + .thenCompose(res3 -> { + System.out.println(res3); + // >>> [store, seat, pedals, tires, handles] + // REMOVE_START + assertThat(res3.size()).isEqualTo(5); + assertThat(res3).contains("store", "seat"); + // REMOVE_END + return asyncCommands.topKQuery("bikes:keywords", "store", "handlebars"); + }) + .thenAccept(res4 -> { + System.out.println(res4); + // >>> [true, false] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[true, false]"); + // REMOVE_END + }) + .toCompletableFuture(); + + topKExample.join(); + // STEP_END + + // REMOVE_START + asyncCommands.del("bikes:keywords").toCompletableFuture().join(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} diff --git a/local_examples/topk_tutorial/lettuce-reactive/TopKExample.java b/local_examples/topk_tutorial/lettuce-reactive/TopKExample.java new file mode 100644 index 0000000000..52aa011b92 --- /dev/null +++ b/local_examples/topk_tutorial/lettuce-reactive/TopKExample.java @@ -0,0 +1,91 @@ +// EXAMPLE: topk_tutorial +// HIDE_START +package io.redis.examples.reactive; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +import io.lettuce.core.probabilistic.arguments.TopKReserveArgs; +// REMOVE_START +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END + +import reactor.core.publisher.Mono; + +import java.util.List; +// HIDE_END + +public class TopKExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + // HIDE_START + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect()) { + RedisReactiveCommands reactiveCommands = connection.reactive(); + // HIDE_END + + // STEP_START topk + // REMOVE_START + reactiveCommands.del("bikes:keywords").block(); + // REMOVE_END + Mono topKExample = reactiveCommands + .topKReserve("bikes:keywords", 5L, + TopKReserveArgs.Builder.width(2000L).depth(7L).decay(0.925)) + .doOnNext(res1 -> { + System.out.println(res1); + // >>> OK + // REMOVE_START + assertThat(res1).isEqualTo("OK"); + // REMOVE_END + }) + .thenMany(reactiveCommands.topKAdd("bikes:keywords", + "store", "seat", "handlebars", "handles", "pedals", + "tires", "store", "seat")) + .map(v -> v.getValueOrElse(null)) + .collectList() + .doOnNext(res2 -> { + System.out.println(res2); + // >>> [null, null, null, null, null, handlebars, null, null] + // REMOVE_START + assertThat(res2.size()).isEqualTo(8); + // REMOVE_END + }) + .thenMany(reactiveCommands.topKList("bikes:keywords")) + .collectList() + .doOnNext(res3 -> { + System.out.println(res3); + // >>> [store, seat, pedals, tires, handles] + // REMOVE_START + assertThat(res3.size()).isEqualTo(5); + assertThat(res3).contains("store", "seat"); + // REMOVE_END + }) + .thenMany(reactiveCommands.topKQuery("bikes:keywords", "store", "handlebars")) + .collectList() + .doOnNext(res4 -> { + System.out.println(res4); + // >>> [true, false] + // REMOVE_START + assertThat(res4.toString()).isEqualTo("[true, false]"); + // REMOVE_END + }) + .then(); + + topKExample.block(); + // STEP_END + + // REMOVE_START + reactiveCommands.del("bikes:keywords").block(); + // REMOVE_END + // HIDE_START + } finally { + redisClient.shutdown(); + } + // HIDE_END + } +} From 6901edcdc9762fe5a232680df290992cda99caeb Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Tue, 25 Aug 2026 10:09:53 +0100 Subject: [PATCH 2/3] DOC-6827: reconcile the Lettuce prob examples against released 7.7.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lettuce 7.7.0.RELEASE (2026-08-18) ships all six probabilistic families, so the park trigger fired and the examples could finally be compiled and run rather than reasoned about. Three defects surfaced that five rounds of signature analysis had all passed as clean. t-digest took `double`, not `V`. 32 compile errors across the four t-digest-touching files: `tdigestAdd`, `tdigestRank` and `tdigestCDF` declare `(K, double...)`, and the examples passed quoted strings ("45.88", "50"). Every park-time note checked method name, arity and return type — never the parameter type — and `(K, V...)` vs `(K, double...)` are identical on all three. The 08-10 note's own shorthand `tdigestAdd(k, v)` encoded the wrong assumption and it was carried forward through four re-verifications. Fixed by unquoting 68 numeric literals, scoped to the t-digest call spans so the BF/CF/CMS sibling calls in the same files kept their string values. Reactor forbids a null from map(). The two reactive examples did `.map(v -> v.getValueOrElse(null))` on Top-K replies, which throws NullPointerException at runtime — so those files had never once been executed. Lettuce returns `Flux>` reactively but `RedisFuture>` async, precisely because a Flux cannot carry null while a List can. The examples had copied the async expected output and forced nulls through a mapper to match it. Dropping the mapper lets the real `Value.empty` / `Value[handlebars]` shape surface, which is the honest reactive idiom rather than a fiction that matches the async tab. One expected output was simply wrong, in the async file too. Top-K eviction for home_prob_dts printed [null, null, null, Rock me, Handel, Only one more time, null] — two evictions at positions 4 and 5 — where the comment claimed five nulls then a single "Rock me, Handel". It survived because that println had no assertion at all; the reactive Top-K one had only a .size() check. Added content assertions at all three sites so the >>> values are now guarded, and verified the eviction order is stable across three fresh runs before baking it in. Verified: all 14 compile against 7.7.0 and all 14 pass against Redis 8.8.0, with 132 of 132 expected-output comments matching observed stdout (mechanically diffed, not spot-checked). Negative control run: perturbing one new assertion does fail the build. Learned: a tripwire guarding a false conclusion preserves the falsehood perfectly — all four pinned blob SHAs matched at the release tag, which correctly proved nothing had moved and told us nothing about whether the claim was ever true. Recheck: prob.md needed no edit — no bannerText, no version-gating prose. Recheck: 7.7.0 has neither the reactive() deprecation nor the commands(factory) replacement, though #3731's diff read "@deprecated since 7.7"; #3781 landed and was reworked away before the tag. Do not document commands(factory) as a migration path yet. Gaps: tdigestByRevRank, tdigestRevRank, tdigestTrimmedMean, tdigestInfo, cmsInitByDim and cmsMerge remain uncovered by these examples. --- .../lettuce-async/HomeProbExample.java | 12 ++++++++---- .../lettuce-reactive/HomeProbExample.java | 15 +++++++++------ .../lettuce-async/TDigestExample.java | 12 ++++++------ .../lettuce-reactive/TDigestExample.java | 12 ++++++------ .../lettuce-reactive/TopKExample.java | 5 +++-- 5 files changed, 32 insertions(+), 24 deletions(-) diff --git a/local_examples/home_prob_dts/lettuce-async/HomeProbExample.java b/local_examples/home_prob_dts/lettuce-async/HomeProbExample.java index 6012eef0d8..ef49ebcb46 100644 --- a/local_examples/home_prob_dts/lettuce-async/HomeProbExample.java +++ b/local_examples/home_prob_dts/lettuce-async/HomeProbExample.java @@ -244,7 +244,7 @@ public void run() { assertThat(res20).isEqualTo("OK"); // REMOVE_END return asyncCommands.tdigestAdd("male_heights", - "175.5", "181", "160.8", "152", "177", "196", "164"); + 175.5, 181, 160.8, 152, 177, 196, 164); }) .thenCompose(res21 -> { System.out.println(res21); @@ -278,7 +278,7 @@ public void run() { // REMOVE_END // Note that the CDF value for 181 is not exactly 0.75. // Both values are estimates. - return asyncCommands.tdigestCDF("male_heights", "181"); + return asyncCommands.tdigestCDF("male_heights", 181); }) .thenCompose(res25 -> { System.out.println(res25); @@ -292,7 +292,7 @@ public void run() { assertThat(res26).isEqualTo("OK"); // REMOVE_END return asyncCommands.tdigestAdd("female_heights", - "155.5", "161", "168.5", "170", "157.5", "163", "171"); + 155.5, 161, 168.5, 170, 157.5, 163, 171); }) .thenCompose(res27 -> { System.out.println(res27); @@ -350,7 +350,11 @@ public void run() { }) .thenCompose(res32 -> { System.out.println(res32); - // >>> [null, null, null, null, null, Rock me, Handel] + // >>> [null, null, null, Rock me, Handel, Only one more time, null] + // REMOVE_START + assertThat(res32).containsExactly(null, null, null, "Rock me, Handel", + "Only one more time", null); + // REMOVE_END return asyncCommands.topKList("top_3_songs"); }) .thenCompose(res33 -> { diff --git a/local_examples/home_prob_dts/lettuce-reactive/HomeProbExample.java b/local_examples/home_prob_dts/lettuce-reactive/HomeProbExample.java index 5747f4506d..e3bc40c445 100644 --- a/local_examples/home_prob_dts/lettuce-reactive/HomeProbExample.java +++ b/local_examples/home_prob_dts/lettuce-reactive/HomeProbExample.java @@ -215,7 +215,7 @@ public void run() { // REMOVE_END String res21 = reactiveCommands.tdigestAdd("male_heights", - "175.5", "181", "160.8", "152", "177", "196", "164").block(); + 175.5, 181, 160.8, 152, 177, 196, 164).block(); System.out.println(res21); // >>> OK // REMOVE_START @@ -246,7 +246,7 @@ public void run() { // Note that the CDF value for 181 is not exactly 0.75. // Both values are estimates. - List res25 = reactiveCommands.tdigestCDF("male_heights", "181") + List res25 = reactiveCommands.tdigestCDF("male_heights", 181) .collectList().block(); System.out.println(res25); // >>> [0.7857142857142857] @@ -259,7 +259,7 @@ public void run() { // REMOVE_END String res27 = reactiveCommands.tdigestAdd("female_heights", - "155.5", "161", "168.5", "170", "157.5", "163", "171").block(); + 155.5, 161, 168.5, 170, 157.5, 163, 171).block(); System.out.println(res27); // >>> OK // REMOVE_START @@ -299,7 +299,7 @@ public void run() { assertThat(res31).isEqualTo("OK"); // REMOVE_END - List res32 = reactiveCommands + List> res32 = reactiveCommands .topKIncrBy("top_3_songs", IncrementPair.of("Starfish Trooper", 3000L), IncrementPair.of("Only one more time", 1850L), @@ -307,11 +307,14 @@ public void run() { IncrementPair.of("How will anyone know?", 3890L), IncrementPair.of("Average lover", 4098L), IncrementPair.of("Road to everywhere", 770L)) - .map(v -> v.getValueOrElse(null)) .collectList() .block(); System.out.println(res32); - // >>> [null, null, null, null, null, Rock me, Handel] + // >>> [Value.empty, Value.empty, Value.empty, Value[Rock me, Handel], Value[Only one more time], Value.empty] + // REMOVE_START + assertThat(res32.toString()).isEqualTo( + "[Value.empty, Value.empty, Value.empty, Value[Rock me, Handel], Value[Only one more time], Value.empty]"); + // REMOVE_END List res33 = reactiveCommands.topKList("top_3_songs").collectList().block(); System.out.println(res33); diff --git a/local_examples/tdigest_tutorial/lettuce-async/TDigestExample.java b/local_examples/tdigest_tutorial/lettuce-async/TDigestExample.java index b7f3581225..13088a42f5 100644 --- a/local_examples/tdigest_tutorial/lettuce-async/TDigestExample.java +++ b/local_examples/tdigest_tutorial/lettuce-async/TDigestExample.java @@ -41,7 +41,7 @@ public void run() { // REMOVE_START assertThat(res1).isEqualTo("OK"); // REMOVE_END - return asyncCommands.tdigestAdd("bikes:sales", "21"); + return asyncCommands.tdigestAdd("bikes:sales", 21); }) .thenCompose(res2 -> { System.out.println(res2); @@ -49,7 +49,7 @@ public void run() { // REMOVE_START assertThat(res2).isEqualTo("OK"); // REMOVE_END - return asyncCommands.tdigestAdd("bikes:sales", "150", "95", "75", "34"); + return asyncCommands.tdigestAdd("bikes:sales", 150, 95, 75, 34); }) .thenAccept(res3 -> { System.out.println(res3); @@ -73,8 +73,8 @@ public void run() { assertThat(res4).isEqualTo("OK"); // REMOVE_END return asyncCommands.tdigestAdd("racer_ages", - "45.88", "44.2", "58.03", "19.76", "39.84", "69.28", - "50.97", "25.41", "19.27", "85.71", "42.63"); + 45.88, 44.2, 58.03, 19.76, 39.84, 69.28, + 50.97, 25.41, 19.27, 85.71, 42.63); }) .thenCompose(res5 -> { System.out.println(res5); @@ -82,7 +82,7 @@ public void run() { // REMOVE_START assertThat(res5).isEqualTo("OK"); // REMOVE_END - return asyncCommands.tdigestRank("racer_ages", "50"); + return asyncCommands.tdigestRank("racer_ages", 50); }) .thenCompose(res6 -> { System.out.println(res6); @@ -90,7 +90,7 @@ public void run() { // REMOVE_START assertThat(res6.toString()).isEqualTo("[7]"); // REMOVE_END - return asyncCommands.tdigestRank("racer_ages", "50", "40"); + return asyncCommands.tdigestRank("racer_ages", 50, 40); }) .thenAccept(res7 -> { System.out.println(res7); diff --git a/local_examples/tdigest_tutorial/lettuce-reactive/TDigestExample.java b/local_examples/tdigest_tutorial/lettuce-reactive/TDigestExample.java index b4fcc11144..125019217f 100644 --- a/local_examples/tdigest_tutorial/lettuce-reactive/TDigestExample.java +++ b/local_examples/tdigest_tutorial/lettuce-reactive/TDigestExample.java @@ -38,14 +38,14 @@ public void run() { assertThat(res1).isEqualTo("OK"); // REMOVE_END - String res2 = reactiveCommands.tdigestAdd("bikes:sales", "21").block(); + String res2 = reactiveCommands.tdigestAdd("bikes:sales", 21).block(); System.out.println(res2); // >>> OK // REMOVE_START assertThat(res2).isEqualTo("OK"); // REMOVE_END - String res3 = reactiveCommands.tdigestAdd("bikes:sales", "150", "95", "75", "34").block(); + String res3 = reactiveCommands.tdigestAdd("bikes:sales", 150, 95, 75, 34).block(); System.out.println(res3); // >>> OK // REMOVE_START @@ -62,22 +62,22 @@ public void run() { // REMOVE_END String res5 = reactiveCommands.tdigestAdd("racer_ages", - "45.88", "44.2", "58.03", "19.76", "39.84", "69.28", - "50.97", "25.41", "19.27", "85.71", "42.63").block(); + 45.88, 44.2, 58.03, 19.76, 39.84, 69.28, + 50.97, 25.41, 19.27, 85.71, 42.63).block(); System.out.println(res5); // >>> OK // REMOVE_START assertThat(res5).isEqualTo("OK"); // REMOVE_END - List res6 = reactiveCommands.tdigestRank("racer_ages", "50").collectList().block(); + List res6 = reactiveCommands.tdigestRank("racer_ages", 50).collectList().block(); System.out.println(res6); // >>> [7] // REMOVE_START assertThat(res6.toString()).isEqualTo("[7]"); // REMOVE_END - List res7 = reactiveCommands.tdigestRank("racer_ages", "50", "40").collectList().block(); + List res7 = reactiveCommands.tdigestRank("racer_ages", 50, 40).collectList().block(); System.out.println(res7); // >>> [7, 4] // REMOVE_START diff --git a/local_examples/topk_tutorial/lettuce-reactive/TopKExample.java b/local_examples/topk_tutorial/lettuce-reactive/TopKExample.java index 52aa011b92..348c48164d 100644 --- a/local_examples/topk_tutorial/lettuce-reactive/TopKExample.java +++ b/local_examples/topk_tutorial/lettuce-reactive/TopKExample.java @@ -46,13 +46,14 @@ public void run() { .thenMany(reactiveCommands.topKAdd("bikes:keywords", "store", "seat", "handlebars", "handles", "pedals", "tires", "store", "seat")) - .map(v -> v.getValueOrElse(null)) .collectList() .doOnNext(res2 -> { System.out.println(res2); - // >>> [null, null, null, null, null, handlebars, null, null] + // >>> [Value.empty, Value.empty, Value.empty, Value.empty, Value.empty, Value[handlebars], Value.empty, Value.empty] // REMOVE_START assertThat(res2.size()).isEqualTo(8); + assertThat(res2.toString()).isEqualTo( + "[Value.empty, Value.empty, Value.empty, Value.empty, Value.empty, Value[handlebars], Value.empty, Value.empty]"); // REMOVE_END }) .thenMany(reactiveCommands.topKList("bikes:keywords")) From 512ba424da669b6430c5fbf087d56b123ee6d4b8 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Tue, 25 Aug 2026 10:12:48 +0100 Subject: [PATCH 3/3] DOC-6827: re-pin the lettuce harness to 7.7.0.RELEASE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The harness pinned lettuce-core 6.5.5.RELEASE, which cannot compile the repo's own Lettuce examples: the same 43 non-DJL example files give 200 compile errors at 6.5.5 and 0 at 7.7.0. Only 104 of those errors are this PR's probabilistic files — the other 96 are pre-existing arrays, JSON and query examples that had already outgrown the pin. So this is a fix to a stale pin, not a bump taken for this PR's convenience. Verified through the real harness rather than a throwaway project: all 7 probabilistic sets pass in both async and reactive, and so do cmds_hash (incl. sync), set_and_get, cmds_string, cmds_sorted_set, cmds_generic and arrays_tutorial. Nothing regressed. Learned: run.sh:407 seeds a toolchain pom with `[ -f "$d/pom.xml" ] || cp ...`, so editing a tracked pom-*.xml has no effect while a cached work//pom.xml exists. The first run after this bump reported FAIL with every tdigest method "cannot find symbol" — which reads exactly like a missing-API problem rather than a stale toolchain, and my throwaway maven project had already passed on the same sources, which is what exposed the contradiction. Delete work//pom.xml after changing a client version. Recheck: the four DJL-dependent vec examples (HomeQueryVecExample, HomeVecSetsExample) cannot compile under any harness pom — none carries ai.djl.* — so they are outside this verification either way, not a regression from the bump. --- build/example-test-harness/pom-lettuce-async.xml | 2 +- build/example-test-harness/pom-lettuce-reactive.xml | 2 +- build/example-test-harness/pom-lettuce-sync.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/example-test-harness/pom-lettuce-async.xml b/build/example-test-harness/pom-lettuce-async.xml index b488bebbf5..6448dd4f98 100644 --- a/build/example-test-harness/pom-lettuce-async.xml +++ b/build/example-test-harness/pom-lettuce-async.xml @@ -4,7 +4,7 @@ io.redislettuce-async1.0 17UTF-8 - io.lettucelettuce-core6.5.5.RELEASE + io.lettucelettuce-core7.7.0.RELEASE org.junit.jupiterjunit-jupiter5.10.2test org.assertjassertj-core3.25.3test diff --git a/build/example-test-harness/pom-lettuce-reactive.xml b/build/example-test-harness/pom-lettuce-reactive.xml index e3f67e7892..ab2a6d38df 100644 --- a/build/example-test-harness/pom-lettuce-reactive.xml +++ b/build/example-test-harness/pom-lettuce-reactive.xml @@ -4,7 +4,7 @@ io.redislettuce-reactive1.0 17UTF-8 - io.lettucelettuce-core6.5.5.RELEASE + io.lettucelettuce-core7.7.0.RELEASE org.junit.jupiterjunit-jupiter5.10.2test org.assertjassertj-core3.25.3test diff --git a/build/example-test-harness/pom-lettuce-sync.xml b/build/example-test-harness/pom-lettuce-sync.xml index 35796e7140..3b053ef119 100644 --- a/build/example-test-harness/pom-lettuce-sync.xml +++ b/build/example-test-harness/pom-lettuce-sync.xml @@ -4,7 +4,7 @@ io.redislettuce-sync1.0 17UTF-8 - io.lettucelettuce-core6.5.5.RELEASE + io.lettucelettuce-core7.7.0.RELEASE org.junit.jupiterjunit-jupiter5.10.2test org.assertjassertj-core3.25.3test