From e7830709c0fa58be108a23a30acb865a24c33679 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Wed, 22 Oct 2025 13:27:02 +0100 Subject: [PATCH 1/6] DOC-5840 add C#-sync index/query notebook --- content/develop/clients/dotnet/queryjson.md | 12 + .../dotnet-sync/HomeJsonExample.cs | 241 ++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 local_examples/client-specific/dotnet-sync/HomeJsonExample.cs diff --git a/content/develop/clients/dotnet/queryjson.md b/content/develop/clients/dotnet/queryjson.md index d1d8e1a8a2..865b09440f 100644 --- a/content/develop/clients/dotnet/queryjson.md +++ b/content/develop/clients/dotnet/queryjson.md @@ -60,6 +60,11 @@ to learn more about the available connection options. {{< clients-example cs_home_json connect >}} {{< /clients-example >}} +Delete any existing index called `idx:users` and any keys that start with `user:`. + +{{< clients-example cs_home_json cleanup_json >}} +{{< /clients-example >}} + Create an index. In this example, only JSON documents with the key prefix `user:` are indexed. For more information, see [Query syntax]({{< relref "/develop/ai/search-and-query/query/" >}}). {{< clients-example cs_home_json make_index >}} @@ -109,6 +114,13 @@ in the `FTCreateParams` object when you create the index. The code below shows these changes with a new index called `hash-idx:users`, which is otherwise the same as the `idx:users` index used for JSON documents in the previous examples. +First, delete any existing index called `hash-idx:users` and any keys that start with `huser:`. + +{{< clients-example cs_home_json cleanup_hash >}} +{{< /clients-example >}} + +Now create the new index: + {{< clients-example cs_home_json make_hash_index >}} {{< /clients-example >}} diff --git a/local_examples/client-specific/dotnet-sync/HomeJsonExample.cs b/local_examples/client-specific/dotnet-sync/HomeJsonExample.cs new file mode 100644 index 0000000000..b408847796 --- /dev/null +++ b/local_examples/client-specific/dotnet-sync/HomeJsonExample.cs @@ -0,0 +1,241 @@ +// EXAMPLE: cs_home_json +// BINDER_ID netsync-cs_home_json +// STEP_START import +using NRedisStack.RedisStackCommands; +using NRedisStack.Search; +using NRedisStack.Search.Aggregation; +using NRedisStack.Search.Literals.Enums; +using StackExchange.Redis; +// STEP_END + +// REMOVE_START +using NRedisStack.Tests; + +namespace Doc; +[Collection("DocsTests")] +// REMOVE_END + +public class HomeJsonExample +// REMOVE_START +: AbstractNRedisStackTest, IDisposable +// REMOVE_END +{ + // REMOVE_START + public HomeJsonExample(EndpointsFixture fixture) : base(fixture) { } + + [SkippableFact] + // REMOVE_END + public void Run() + { + //REMOVE_START + // This is needed because we're constructing ConfigurationOptions in the test before calling GetConnection + SkipIfTargetConnectionDoesNotExist(EndpointsFixture.Env.Standalone); + var _ = GetCleanDatabase(EndpointsFixture.Env.Standalone); + //REMOVE_END + + // STEP_START connect + var muxer = ConnectionMultiplexer.Connect("localhost:6379"); + var db = muxer.GetDatabase(); + // STEP_END + + // STEP_START cleanup_json + db.KeyDelete(["user:1", "user:2", "user:3"]); + try { db.FT().DropIndex("idx:users"); } catch { } + // STEP_END + + // STEP_START create_data + var user1 = new + { + name = "Paul John", + email = "paul.john@example.com", + age = 42, + city = "London" + }; + + var user2 = new + { + name = "Eden Zamir", + email = "eden.zamir@example.com", + age = 29, + city = "Tel Aviv" + }; + + var user3 = new + { + name = "Paul Zamir", + email = "paul.zamir@example.com", + age = 35, + city = "Tel Aviv" + }; + // STEP_END + + // STEP_START make_index + var schema = new Schema() + .AddTextField(new FieldName("$.name", "name")) + .AddTagField(new FieldName("$.city", "city")) + .AddNumericField(new FieldName("$.age", "age")); + + bool indexCreated = db.FT().Create( + "idx:users", + new FTCreateParams() + .On(IndexDataType.JSON) + .Prefix("user:"), + schema + ); + // STEP_END + // REMOVE_START + Assert.True(indexCreated); + // REMOVE_END + + + // STEP_START add_data + bool user1Set = db.JSON().Set("user:1", "$", user1); + bool user2Set = db.JSON().Set("user:2", "$", user2); + bool user3Set = db.JSON().Set("user:3", "$", user3); + // STEP_END + // REMOVE_START + Assert.True(user1Set); + Assert.True(user2Set); + Assert.True(user3Set); + // REMOVE_END + + + // STEP_START query1 + SearchResult findPaulResult = db.FT().Search( + "idx:users", + new("Paul @age:[30 40]") + ); + Console.WriteLine(string.Join( + ", ", + findPaulResult.Documents.Select(x => x["json"]) + )); + // >>> {"name":"Paul Zamir","email":"paul.zamir@example.com", ... + // STEP_END + // REMOVE_START + Assert.Equal( + "{\"name\":\"Paul Zamir\",\"email\":\"paul.zamir@example.com\",\"age\":35,\"city\":\"Tel Aviv\"}", + string.Join(", ", findPaulResult.Documents.Select(x => x["json"])) + ); + // REMOVE_END + + + // STEP_START query2 + var citiesResult = db.FT().Search( + "idx:users", + new Query("Paul") + .ReturnFields(new FieldName("$.city", "city")) + ); + Console.WriteLine(string.Join( + ", ", + citiesResult.Documents.Select(x => x["city"]).OrderBy(x => x) + )); + // >>> London, Tel Aviv + // STEP_END + // REMOVE_START + Assert.Equal( + "London, Tel Aviv", + string.Join(", ", citiesResult.Documents.Select(x => x["city"]).OrderBy(x => x)) + ); + // REMOVE_END + + + // STEP_START query3 + AggregationRequest aggRequest = new AggregationRequest("*") + .GroupBy("@city", Reducers.Count().As("count")); + + AggregationResult aggResult = db.FT().Aggregate("idx:users", aggRequest); + IReadOnlyList> resultsList = + aggResult.GetResults(); + + for (var i = 0; i < resultsList.Count; i++) + { + Dictionary item = resultsList.ElementAt(i); + Console.WriteLine($"{item["city"]} - {item["count"]}"); + } + // >>> London - 1 + // >>> Tel Aviv - 2 + // STEP_END + // REMOVE_START + Assert.Equal(2, resultsList.Count); + + var sortedResults = resultsList.OrderBy(x => x["city"]); + Dictionary testItem = sortedResults.ElementAt(0); + Assert.Equal("London", testItem["city"]); + Assert.Equal(1, testItem["count"]); + + testItem = sortedResults.ElementAt(1); + Assert.Equal("Tel Aviv", testItem["city"]); + Assert.Equal(2, testItem["count"]); + // REMOVE_END + + // STEP_START cleanup_hash + db.KeyDelete(["huser:1", "huser:2", "huser:3"]); + try { db.FT().DropIndex("hash-idx:users"); } catch { } + // STEP_END + + // STEP_START make_hash_index + var hashSchema = new Schema() + .AddTextField("name") + .AddTagField("city") + .AddNumericField("age"); + + bool hashIndexCreated = db.FT().Create( + "hash-idx:users", + new FTCreateParams() + .On(IndexDataType.HASH) + .Prefix("huser:"), + hashSchema + ); + // STEP_END + // REMOVE_START + Assert.True(hashIndexCreated); + // REMOVE_END + + // STEP_START add_hash_data + db.HashSet("huser:1", [ + new("name", "Paul John"), + new("email", "paul.john@example.com"), + new("age", 42), + new("city", "London") + ]); + + db.HashSet("huser:2", [ + new("name", "Eden Zamir"), + new("email", "eden.zamir@example.com"), + new("age", 29), + new("city", "Tel Aviv") + ]); + + db.HashSet("huser:3", [ + new("name", "Paul Zamir"), + new("email", "paul.zamir@example.com"), + new("age", 35), + new("city", "Tel Aviv") + ]); + // STEP_END + + // STEP_START query1_hash + SearchResult findPaulHashResult = db.FT().Search( + "hash-idx:users", + new("Paul @age:[30 40]") + ); + + foreach (Document doc in findPaulHashResult.Documents) + { + Console.WriteLine( + $"Name: {doc["name"]}, email: {doc["email"]}, " + + $"age: {doc["age"]}, city:{doc["city"]}" + ); + } + // >>> Name: Paul Zamir, email: paul.zamir@example.com, age: 35, ... + // STEP_END + // REMOVE_START + Document d = findPaulHashResult.Documents[0]; + Assert.Equal( + "Name: Paul Zamir, email: paul.zamir@example.com, age: 35, city:Tel Aviv", + $"Name: {d["name"]}, email: {d["email"]}, " + + $"age: {d["age"]}, city:{d["city"]}" + ); + // REMOVE_END + } +} From 7b8a356c97ef93d54a9b915407a5e89765b60dc0 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Wed, 22 Oct 2025 14:29:17 +0100 Subject: [PATCH 2/6] DOC-5842 modify index/query page for notebook content --- content/develop/clients/jedis/queryjson.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/content/develop/clients/jedis/queryjson.md b/content/develop/clients/jedis/queryjson.md index b3d7903344..0e52a17d9f 100644 --- a/content/develop/clients/jedis/queryjson.md +++ b/content/develop/clients/jedis/queryjson.md @@ -63,6 +63,11 @@ to learn more about the available connection options. {{< clients-example java_home_json connect >}} {{< /clients-example >}} +Delete any existing index called `idx:users` and any keys that start with `user:`. + +{{< clients-example java_home_json cleanup_json >}} +{{< /clients-example >}} + Create an index. In this example, only JSON documents with the key prefix `user:` are indexed. For more information, see [Query syntax]({{< relref "/develop/ai/search-and-query/query/" >}}). {{< clients-example java_home_json make_index >}} @@ -112,6 +117,13 @@ option of `FTCreateParams` when you create the index. The code below shows these changes with a new index called `hash-idx:users`, which is otherwise the same as the `idx:users` index used for JSON documents in the previous examples. +First, delete any existing index called `hash-idx:users` and any keys that start with `huser:`. + +{{< clients-example java_home_json cleanup_hash >}} +{{< /clients-example >}} + +Now create the new index: + {{< clients-example java_home_json make_hash_index >}} {{< /clients-example >}} From 131ba7f3bd0aad089420928dcbffcb5b124049f9 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Wed, 22 Oct 2025 15:44:55 +0100 Subject: [PATCH 3/6] DOC-5842 updated code for Jedis index/query example --- .../jedis/HomeJsonExample.java | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 local_examples/client-specific/jedis/HomeJsonExample.java diff --git a/local_examples/client-specific/jedis/HomeJsonExample.java b/local_examples/client-specific/jedis/HomeJsonExample.java new file mode 100644 index 0000000000..36265229a0 --- /dev/null +++ b/local_examples/client-specific/jedis/HomeJsonExample.java @@ -0,0 +1,234 @@ +// EXAMPLE: java_home_json +// BINDER_ID jedis-java_home_json +// REMOVE_START +package io.redis.examples; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +// REMOVE_END +// STEP_START import +import redis.clients.jedis.UnifiedJedis; +import redis.clients.jedis.exceptions.JedisDataException; +import redis.clients.jedis.json.Path2; +import redis.clients.jedis.search.*; +import redis.clients.jedis.search.aggr.*; +import redis.clients.jedis.search.schemafields.*; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +// STEP_END + +public class HomeJsonExample { + + @Test + public void run() { + + // STEP_START create_data + JSONObject user1 = new JSONObject() + .put("name", "Paul John") + .put("email", "paul.john@example.com") + .put("age", 42) + .put("city", "London"); + + JSONObject user2 = new JSONObject() + .put("name", "Eden Zamir") + .put("email", "eden.zamir@example.com") + .put("age", 29) + .put("city", "Tel Aviv"); + + JSONObject user3 = new JSONObject() + .put("name", "Paul Zamir") + .put("email", "paul.zamir@example.com") + .put("age", 35) + .put("city", "Tel Aviv"); + // STEP_END + + // STEP_START connect + UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379"); + // STEP_END + + // STEP_START cleanup_json + try {jedis.ftDropIndex("idx:users");} catch (JedisDataException j){} + jedis.del("user:1", "user:2", "user:3"); + // STEP_END + + // STEP_START make_index + SchemaField[] schema = { + TextField.of("$.name").as("name"), + TextField.of("$.city").as("city"), + NumericField.of("$.age").as("age") + }; + + String createResult = jedis.ftCreate("idx:users", + FTCreateParams.createParams() + .on(IndexDataType.JSON) + .addPrefix("user:"), + schema + ); + + System.out.println(createResult); // >>> OK + // STEP_END + // REMOVE_START + assertEquals("OK", createResult); + // REMOVE_END + + // STEP_START add_data + String user1Set = jedis.jsonSet("user:1", new Path2("$"), user1); + String user2Set = jedis.jsonSet("user:2", new Path2("$"), user2); + String user3Set = jedis.jsonSet("user:3", new Path2("$"), user3); + // STEP_END + // REMOVE_START + assertEquals("OK", user1Set); + assertEquals("OK", user2Set); + assertEquals("OK", user3Set); + // REMOVE_END + + // STEP_START query1 + SearchResult findPaulResult = jedis.ftSearch("idx:users", + "Paul @age:[30 40]" + ); + + System.out.println(findPaulResult.getTotalResults()); // >>> 1 + + List paulDocs = findPaulResult.getDocuments(); + + for (Document doc: paulDocs) { + System.out.println(doc.getId()); + } + // >>> user:3 + // STEP_END + // REMOVE_START + assertEquals("user:3", paulDocs.get(0).getId()); + // REMOVE_END + + // STEP_START query2 + SearchResult citiesResult = jedis.ftSearch("idx:users", + "Paul", + FTSearchParams.searchParams() + .returnFields("city") + ); + + System.out.println(citiesResult.getTotalResults()); // >>> 2 + + for (Document doc: citiesResult.getDocuments()) { + System.out.println(doc.getId()); + } + // >>> user:1 + // >>> user:3 + // STEP_END + // REMOVE_START + assertArrayEquals( + new String[] {"user:1", "user:3"}, + citiesResult.getDocuments().stream().map(Document::getId).sorted().toArray() + ); + // REMOVE_END + + // STEP_START query3 + AggregationResult aggResult = jedis.ftAggregate("idx:users", + new AggregationBuilder("*") + .groupBy("@city", Reducers.count().as("count")) + ); + + System.out.println(aggResult.getTotalResults()); // >>> 2 + + for (Row cityRow: aggResult.getRows()) { + System.out.printf("%s - %d%n", + cityRow.getString("city"), cityRow.getLong("count")); + } + // >>> London - 1 + // >>> Tel Aviv - 2 + // STEP_END + // REMOVE_START + assertArrayEquals( + new String[] {"London - 1", "Tel Aviv - 2"}, + aggResult.getRows().stream() + .map(r -> r.getString("city") + " - " + r.getString("count")) + .sorted().toArray()); + // REMOVE_END + + // STEP_START cleanup_hash + try {jedis.ftDropIndex("hash-idx:users");} catch (JedisDataException j){} + jedis.del("huser:1", "huser:2", "huser:3"); + // STEP_END + + // STEP_START make_hash_index + SchemaField[] hashSchema = { + TextField.of("name"), + TextField.of("city"), + NumericField.of("age") + }; + + String hashCreateResult = jedis.ftCreate("hash-idx:users", + FTCreateParams.createParams() + .on(IndexDataType.HASH) + .addPrefix("huser:"), + hashSchema + ); + + System.out.println(hashCreateResult); // >>> OK + // STEP_END + // REMOVE_START + assertEquals("OK", hashCreateResult); + // REMOVE_END + + // STEP_START add_hash_data + Map user1Info = new HashMap<>(); + user1Info.put("name", "Paul John"); + user1Info.put("email", "paul.john@example.com"); + user1Info.put("age", "42"); + user1Info.put("city", "London"); + long huser1Set = jedis.hset("huser:1", user1Info); + + System.out.println(huser1Set); // >>> 4 + + Map user2Info = new HashMap<>(); + user2Info.put("name", "Eden Zamir"); + user2Info.put("email", "eden.zamir@example.com"); + user2Info.put("age", "29"); + user2Info.put("city", "Tel Aviv"); + long huser2Set = jedis.hset("huser:2", user2Info); + + System.out.println(huser2Set); // >>> 4 + + Map user3Info = new HashMap<>(); + user3Info.put("name", "Paul Zamir"); + user3Info.put("email", "paul.zamir@example.com"); + user3Info.put("age", "35"); + user3Info.put("city", "Tel Aviv"); + long huser3Set = jedis.hset("huser:3", user3Info); + + System.out.println(huser3Set); // >>> 4 + // STEP_END + // REMOVE_START + assertEquals(4, huser1Set); + assertEquals(4, huser2Set); + assertEquals(4, huser3Set); + // REMOVE_END + + // STEP_START query1_hash + SearchResult findPaulHashResult = jedis.ftSearch("hash-idx:users", + "Paul @age:[30 40]" + ); + + System.out.println(findPaulHashResult.getTotalResults()); // >>> 1 + + List paulHashDocs = findPaulHashResult.getDocuments(); + + for (Document doc: paulHashDocs) { + System.out.println(doc.getId()); + } + // >>> user:3 + // STEP_END + // REMOVE_START + assertEquals("huser:3", paulHashDocs.get(0).getId()); + // REMOVE_END + + // STEP_START close + jedis.close(); + // STEP_END + } +} + From 3d11b9cb35b0ab43cfbd7e3cc22783f2274f1b38 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Fri, 24 Oct 2025 13:56:38 +0100 Subject: [PATCH 4/6] DOC-5841 updated node-redis index/query page for notebook --- content/develop/clients/nodejs/queryjson.md | 176 ++++---------------- 1 file changed, 37 insertions(+), 139 deletions(-) diff --git a/content/develop/clients/nodejs/queryjson.md b/content/develop/clients/nodejs/queryjson.md index fad354bce8..b7c0b1b456 100644 --- a/content/develop/clients/nodejs/queryjson.md +++ b/content/develop/clients/nodejs/queryjson.md @@ -40,42 +40,16 @@ haven't already done so. Add the following dependencies: -```js -import { - createClient, - SCHEMA_FIELD_TYPE, - FT_AGGREGATE_GROUP_BY_REDUCERS, - FT_AGGREGATE_STEPS, -} from 'redis'; -``` +{{< clients-example set="js_home_query" step="import" lang_filter="Node.js" >}} +{{< /clients-example >}} ## Create data Create some test data to add to your database. The example data shown below is compatible with both JSON and hash objects. -```js -const user1 = { - name: 'Paul John', - email: 'paul.john@example.com', - age: 42, - city: 'London' -}; - -const user2 = { - name: 'Eden Zamir', - email: 'eden.zamir@example.com', - age: 29, - city: 'Tel Aviv' -}; - -const user3 = { - name: 'Paul Zamir', - email: 'paul.zamir@example.com', - age: 35, - city: 'Tel Aviv' -}; -``` +{{< clients-example set="js_home_query" step="create_data" lang_filter="Node.js" >}} +{{< /clients-example >}} ## Add the index @@ -84,32 +58,21 @@ basic connection but see [Connect to the server]({{< relref "/develop/clients/nodejs/connect" >}}) to learn more about the available connection options. -```js -const client = await createClient(); -await client.connect(); -``` +{{< clients-example set="js_home_query" step="connect" lang_filter="Node.js" >}} +{{< /clients-example >}} Create an index. In this example, only JSON documents with the key prefix `user:` are indexed. For more information, see [Query syntax]({{< relref "/develop/ai/search-and-query/query/" >}}). -```js -await client.ft.create('idx:users', { - '$.name': { - type: SchemaFieldTypes.TEXT, - AS: 'name' - }, - '$.city': { - type: SchemaFieldTypes.TEXT, - AS: 'city' - }, - '$.age': { - type: SchemaFieldTypes.NUMERIC, - AS: 'age' - } -}, { - ON: 'JSON', - PREFIX: 'user:' -}); -``` +First, drop any existing index to avoid a collision. (The callback is required +to avoid an error if the index doesn't already exist.) + +{{< clients-example set="js_home_query" step="cleanup_json" lang_filter="Node.js" >}} +{{< /clients-example >}} + +Then create the index: + +{{< clients-example set="js_home_query" step="create_index" lang_filter="Node.js" >}} +{{< /clients-example >}} ## Add the data @@ -121,13 +84,8 @@ the commands in a `Promise.all()` call is an easy way to create a [pipeline]({{< relref "/develop/clients/nodejs/transpipe" >}}), which is more efficient than sending the commands individually. -```js -const [user1Reply, user2Reply, user3Reply] = await Promise.all([ - client.json.set('user:1', '$', user1), - client.json.set('user:2', '$', user2), - client.json.set('user:3', '$', user3) -]); -``` +{{< clients-example set="js_home_query" step="add_data" lang_filter="Node.js" >}} +{{< /clients-example >}} ## Query the data @@ -136,58 +94,20 @@ You can now use the index to search the JSON objects. The below searches for objects that have the text "Paul" in any field and have an `age` value in the range 30 to 40: -```js -let findPaulResult = await client.ft.search('idx:users', 'Paul @age:[30 40]'); - -console.log(findPaulResult.total); // >>> 1 - -findPaulResult.documents.forEach(doc => { - console.log(`ID: ${doc.id}, name: ${doc.value.name}, age: ${doc.value.age}`); -}); -``` +{{< clients-example set="js_home_query" step="query1" lang_filter="Node.js" >}} +{{< /clients-example >}} Specify query options to return only the `city` field: -```js -let citiesResult = await client.ft.search('idx:users', '*',{ - RETURN: 'city' -}); - -console.log(citiesResult.total); // >>> 3 - -citiesResult.documents.forEach(cityDoc => { - console.log(cityDoc.value); -}); -``` +{{< clients-example set="js_home_query" step="query2" lang_filter="Node.js" >}} +{{< /clients-example >}} Use an [aggregation query]({{< relref "/develop/ai/search-and-query/query/aggregation" >}}) to count all users in each city. -```js -let aggResult = await client.ft.aggregate('idx:users', '*', { - STEPS: [{ - type: AggregateSteps.GROUPBY, - properties: '@city', - REDUCE: [{ - type: AggregateGroupByReducers.COUNT, - AS: 'count' - }] - }] -}); - -console.log(aggResult.total); // >>> 2 - -aggResult.results.forEach(result => { - console.log(`${result.city} - ${result.count}`); -}); -``` - -Finally, close the connection to Redis. - -```js -await client.quit(); -``` +{{< clients-example set="js_home_query" step="query3" lang_filter="Node.js" >}} +{{< /clients-example >}} ## Differences with hash documents @@ -201,52 +121,30 @@ when you create the index. The code below shows these changes with a new index called `hash-idx:users`, which is otherwise the same as the `idx:users` index used for JSON documents in the previous examples. -```js -await client.ft.create('hash-idx:users', { - 'name': { - type: SchemaFieldTypes.TEXT - }, - 'city': { - type: SchemaFieldTypes.TEXT - }, - 'age': { - type: SchemaFieldTypes.NUMERIC - } -}, { - ON: 'HASH', - PREFIX: 'huser:' -}); -``` +First, drop any existing index to avoid a collision. + +{{< clients-example set="js_home_query" step="cleanup_hash" lang_filter="Node.js" >}} +{{< /clients-example >}} + +Then create the new index: + +{{< clients-example set="js_home_query" step="create_hash_index" lang_filter="Node.js" >}} +{{< /clients-example >}} You use [`hSet()`]({{< relref "/commands/hset" >}}) to add the hash documents instead of [`json.set()`]({{< relref "/commands/json.set" >}}), but the same flat `userX` objects work equally well with either hash or JSON: -```js -const [huser1Reply, huser2Reply, huser3Reply] = await Promise.all([ - client.hSet('huser:1', user1), - client.hSet('huser:2', user2), - client.hSet('huser:3', user3) -]); -``` +{{< clients-example set="js_home_query" step="add_hash_data" lang_filter="Node.js" >}} +{{< /clients-example >}} The query commands work the same here for hash as they do for JSON (but the name of the hash index is different). The format of the result is also the same: -```js -let findPaulHashResult = await client.ft.search( - 'hash-idx:users', 'Paul @age:[30 40]' -); - -console.log(findPaulHashResult.total); // >>> 1 - -findPaulHashResult.documents.forEach(doc => { - console.log(`ID: ${doc.id}, name: ${doc.value.name}, age: ${doc.value.age}`); -}); -// >>> ID: huser:3, name: Paul Zamir, age: 35 -``` +{{< clients-example set="js_home_query" step="query1_hash" lang_filter="Node.js" >}} +{{< /clients-example >}} ## More information From cd2a868aab8528b25959305f46fe7e0d6ff1c041 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Fri, 24 Oct 2025 14:26:12 +0100 Subject: [PATCH 5/6] DOC-5839 added JS source file for index/query examples --- .../client-specific/nodejs/home-query.js | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 local_examples/client-specific/nodejs/home-query.js diff --git a/local_examples/client-specific/nodejs/home-query.js b/local_examples/client-specific/nodejs/home-query.js new file mode 100644 index 0000000000..b5b5bd5ba5 --- /dev/null +++ b/local_examples/client-specific/nodejs/home-query.js @@ -0,0 +1,205 @@ +// EXAMPLE: js_home_query +// BINDER_ID nodejs-js_home_query +// REMOVE_START +import assert from "node:assert"; +// REMOVE_END +// STEP_START import +import { + createClient, + SCHEMA_FIELD_TYPE, + FT_AGGREGATE_GROUP_BY_REDUCERS, + FT_AGGREGATE_STEPS, +} from 'redis'; +// STEP_END + +// STEP_START create_data +const user1 = { + name: 'Paul John', + email: 'paul.john@example.com', + age: 42, + city: 'London' +}; + +const user2 = { + name: 'Eden Zamir', + email: 'eden.zamir@example.com', + age: 29, + city: 'Tel Aviv' +}; + +const user3 = { + name: 'Paul Zamir', + email: 'paul.zamir@example.com', + age: 35, + city: 'Tel Aviv' +}; +// STEP_END + +// STEP_START connect +const client = await createClient(); +await client.connect(); +// STEP_END + +// STEP_START cleanup_json +await client.ft.dropIndex('idx:users', { DD: true }).then(() => {}, () => {}); +// STEP_END + +// STEP_START create_index +await client.ft.create('idx:users', { + '$.name': { + type: SCHEMA_FIELD_TYPE.TEXT, + AS: 'name' + }, + '$.city': { + type: SCHEMA_FIELD_TYPE.TEXT, + AS: 'city' + }, + '$.age': { + type: SCHEMA_FIELD_TYPE.NUMERIC, + AS: 'age' + } +}, { + ON: 'JSON', + PREFIX: 'user:' +}); +// STEP_END + +// STEP_START add_data +const [user1Reply, user2Reply, user3Reply] = await Promise.all([ + client.json.set('user:1', '$', user1), + client.json.set('user:2', '$', user2), + client.json.set('user:3', '$', user3) +]); +// STEP_END +// REMOVE_START +assert.equal(user1Reply, 'OK'); +assert.equal(user2Reply, 'OK'); +assert.equal(user3Reply, 'OK'); +// REMOVE_END + +// STEP_START query1 +let findPaulResult = await client.ft.search('idx:users', 'Paul @age:[30 40]'); + +console.log(findPaulResult.total); // >>> 1 + +findPaulResult.documents.forEach(doc => { + console.log(`ID: ${doc.id}, name: ${doc.value.name}, age: ${doc.value.age}`); +}); +// >>> ID: user:3, name: Paul Zamir, age: 35 +// STEP_END +// REMOVE_START +assert.equal(findPaulResult.total, 1); +assert.equal(findPaulResult.documents[0].id, 'user:3'); +// REMOVE_END + +// STEP_START query2 +let citiesResult = await client.ft.search('idx:users', '*',{ + RETURN: 'city' +}); + +console.log(citiesResult.total); // >>> 3 + +citiesResult.documents.forEach(cityDoc => { + console.log(cityDoc.value); +}); +// >>> { city: 'London' } +// >>> { city: 'Tel Aviv' } +// >>> { city: 'Tel Aviv' } +// STEP_END +// REMOVE_START +assert.equal(citiesResult.total, 3); +citiesResult.documents.sort((a, b) => a.value.city.localeCompare(b.value.city)); +assert.deepEqual(citiesResult.documents.map(doc => doc.value.city), [ + 'London', + 'Tel Aviv', + 'Tel Aviv' +]); +// REMOVE_END + +// STEP_START query3 +let aggResult = await client.ft.aggregate('idx:users', '*', { + STEPS: [{ + type: FT_AGGREGATE_STEPS.GROUPBY, + properties: '@city', + REDUCE: [{ + type: FT_AGGREGATE_GROUP_BY_REDUCERS.COUNT, + AS: 'count' + }] + }] +}); + +console.log(aggResult.total); // >>> 2 + +aggResult.results.forEach(result => { + console.log(`${result.city} - ${result.count}`); +}); +// >>> London - 1 +// >>> Tel Aviv - 2 +// STEP_END +// REMOVE_START +assert.equal(aggResult.total, 2); +aggResult.results.sort((a, b) => a.city.localeCompare(b.city)); +assert.deepEqual(aggResult.results.map(result => result.city), [ + 'London', + 'Tel Aviv' +]); +assert.deepEqual(aggResult.results.map(result => result.count), [ + 1, + 2 +]); +// REMOVE_END + +// STEP_START cleanup_hash +await client.ft.dropIndex('hash-idx:users', { DD: true }).then(() => {}, () => {}); +// STEP_END + +// STEP_START create_hash_index +await client.ft.create('hash-idx:users', { + 'name': { + type: SCHEMA_FIELD_TYPE.TEXT + }, + 'city': { + type: SCHEMA_FIELD_TYPE.TEXT + }, + 'age': { + type: SCHEMA_FIELD_TYPE.NUMERIC + } +}, { + ON: 'HASH', + PREFIX: 'huser:' +}); +// STEP_END + +// STEP_START add_hash_data +const [huser1Reply, huser2Reply, huser3Reply] = await Promise.all([ + client.hSet('huser:1', user1), + client.hSet('huser:2', user2), + client.hSet('huser:3', user3) +]); +// STEP_END +// REMOVE_START +assert.equal(huser1Reply, 4); +assert.equal(huser2Reply, 4); +assert.equal(huser3Reply, 4); +// REMOVE_END + +// STEP_START query1_hash +let findPaulHashResult = await client.ft.search( + 'hash-idx:users', 'Paul @age:[30 40]' +); + +console.log(findPaulHashResult.total); // >>> 1 + +findPaulHashResult.documents.forEach(doc => { + console.log(`ID: ${doc.id}, name: ${doc.value.name}, age: ${doc.value.age}`); +}); +// >>> ID: huser:3, name: Paul Zamir, age: 35 +// STEP_END +// REMOVE_START +assert.equal(findPaulHashResult.total, 1); +assert.equal(findPaulHashResult.documents[0].id, 'huser:3'); +// REMOVE_END + +// STEP_START close +await client.quit(); +// STEP_END From 49111fcec851e4584100a8610e7b9928dd81df4b Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Wed, 29 Oct 2025 13:08:32 +0000 Subject: [PATCH 6/6] DOC-5839 removed JS notebook link --- local_examples/client-specific/nodejs/home-query.js | 1 - 1 file changed, 1 deletion(-) diff --git a/local_examples/client-specific/nodejs/home-query.js b/local_examples/client-specific/nodejs/home-query.js index b5b5bd5ba5..031bb05788 100644 --- a/local_examples/client-specific/nodejs/home-query.js +++ b/local_examples/client-specific/nodejs/home-query.js @@ -1,5 +1,4 @@ // EXAMPLE: js_home_query -// BINDER_ID nodejs-js_home_query // REMOVE_START import assert from "node:assert"; // REMOVE_END