diff --git a/content/develop/clients/dotnet/queryjson.md b/content/develop/clients/dotnet/queryjson.md index d1d8e1a8a..865b09440 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/content/develop/clients/jedis/queryjson.md b/content/develop/clients/jedis/queryjson.md index b3d790334..0e52a17d9 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 >}} diff --git a/content/develop/clients/nodejs/queryjson.md b/content/develop/clients/nodejs/queryjson.md index fad354bce..b7c0b1b45 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 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 000000000..b40884779 --- /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 + } +} diff --git a/local_examples/client-specific/jedis/HomeJsonExample.java b/local_examples/client-specific/jedis/HomeJsonExample.java new file mode 100644 index 000000000..36265229a --- /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 + } +} + 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 000000000..031bb0578 --- /dev/null +++ b/local_examples/client-specific/nodejs/home-query.js @@ -0,0 +1,204 @@ +// EXAMPLE: 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