From e9bd841b357d6264d7b2297aa2560743285922f2 Mon Sep 17 00:00:00 2001 From: Shoham Elias Date: Tue, 20 Aug 2024 19:17:58 +0000 Subject: [PATCH 1/3] Add NOSCORES option to ZSCAN & NOVALUES option to HSCAN Signed-off-by: Shoham Elias --- .../glide/api/commands/HashBaseCommands.java | 6 +- .../api/commands/SortedSetBaseCommands.java | 6 +- .../glide/api/models/BaseTransaction.java | 7 +- .../models/commands/scan/HScanOptions.java | 54 ++++++++- .../commands/scan/HScanOptionsBinary.java | 50 +++++++- .../models/commands/scan/ZScanOptions.java | 54 ++++++++- .../commands/scan/ZScanOptionsBinary.java | 50 +++++++- .../test/java/glide/SharedCommandTests.java | 65 ++++++++++- .../java/glide/TransactionTestUtilities.java | 107 +++++++++++++----- node/src/BaseClient.ts | 47 ++++++-- node/src/Commands.ts | 41 ++++++- node/src/Transaction.ts | 17 ++- node/tests/SharedTests.ts | 40 ++++++- node/tests/TestUtilities.ts | 37 ++++++ python/python/glide/async_commands/core.py | 52 ++++++++- .../glide/async_commands/transaction.py | 22 +++- python/python/tests/test_async_client.py | 20 ++++ python/python/tests/test_transaction.py | 6 + 18 files changed, 602 insertions(+), 79 deletions(-) diff --git a/java/client/src/main/java/glide/api/commands/HashBaseCommands.java b/java/client/src/main/java/glide/api/commands/HashBaseCommands.java index 9140932fc0..58a9814559 100644 --- a/java/client/src/main/java/glide/api/commands/HashBaseCommands.java +++ b/java/client/src/main/java/glide/api/commands/HashBaseCommands.java @@ -696,7 +696,8 @@ public interface HashBaseCommands { * returned on the last iteration of the result. The second element is always an * Array of the subset of the hash held in key. The array in the * second element is always a flattened series of String pairs, where the key is - * at even indices and the value is at odd indices. + * at even indices and the value is at odd indices. If options.noValues is set to true + * , the second element will only contain the fields without the values. * @example *
{@code
      * // Assume key contains a set with 200 member-score pairs
@@ -732,7 +733,8 @@ public interface HashBaseCommands {
      *      returned on the last iteration of the result. The second element is always an
      *     Array of the subset of the hash held in key. The array in the
      *     second element is always a flattened series of String pairs, where the key is
-     *     at even indices and the value is at odd indices.
+     *     at even indices and the value is at odd indices. If options.noValues is set to true
+     *     , the second element will only contain the fields without the values.
      * @example
      *     
{@code
      * // Assume key contains a set with 200 member-score pairs
diff --git a/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java b/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java
index ce6d4c5973..2238b2c627 100644
--- a/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java
+++ b/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java
@@ -2790,7 +2790,8 @@ CompletableFuture> zinterWithScores(
      *     
      *     Array of the subset of the sorted set held in key. The array in the
      *     second element is always a flattened series of String pairs, where the value
-     *     is at even indices and the score is at odd indices.
+     *     is at even indices and the score is at odd indices. If options.noScores is to true
+     *     , the second element will only contain the members without scores.
      * @example
      *     
{@code
      * // Assume key contains a set with 200 member-score pairs
@@ -2827,7 +2828,8 @@ CompletableFuture> zinterWithScores(
      *     
      *     Array of the subset of the sorted set held in key. The array in the
      *     second element is always a flattened series of String pairs, where the value
-     *     is at even indices and the score is at odd indices.
+     *     is at even indices and the score is at odd indices. If options.noScores is to true
+     *     , the second element will only contain the members without scores.
      * @example
      *     
{@code
      * // Assume key contains a set with 200 member-score pairs
diff --git a/java/client/src/main/java/glide/api/models/BaseTransaction.java b/java/client/src/main/java/glide/api/models/BaseTransaction.java
index 94a2ab59a8..6ccab2c274 100644
--- a/java/client/src/main/java/glide/api/models/BaseTransaction.java
+++ b/java/client/src/main/java/glide/api/models/BaseTransaction.java
@@ -7055,7 +7055,9 @@ public  T zscan(@NonNull ArgType key, @NonNull ArgType cursor) {
      *     the cursor returned on the last iteration of the sorted set. The second
      *     element is always an Array of the subset of the sorted set held in key
      *     . The array in the second element is always a flattened series of String
-     *      pairs, where the value is at even indices and the score is at odd indices.
+     *      pairs, where the value is at even indices and the score is at odd indices. If
+     *     options.noScores is to true, the second element will only contain the members
+     *     without scores.
      */
     public  T zscan(
             @NonNull ArgType key, @NonNull ArgType cursor, @NonNull ZScanOptions zScanOptions) {
@@ -7102,7 +7104,8 @@ public  T hscan(@NonNull ArgType key, @NonNull ArgType cursor) {
      *     the cursor returned on the last iteration of the result. The second element is
      *     always an Array of the subset of the hash held in key. The array
      *     in the second element is always a flattened series of String pairs, where the
-     *     key is at even indices and the value is at odd indices.
+     *     key is at even indices and the value is at odd indices. If options.noValues is set to
+     *     true, the second element will only contain the fields without the values.
      */
     public  T hscan(
             @NonNull ArgType key, @NonNull ArgType cursor, @NonNull HScanOptions hScanOptions) {
diff --git a/java/client/src/main/java/glide/api/models/commands/scan/HScanOptions.java b/java/client/src/main/java/glide/api/models/commands/scan/HScanOptions.java
index 1f03a00e6d..38ac80136a 100644
--- a/java/client/src/main/java/glide/api/models/commands/scan/HScanOptions.java
+++ b/java/client/src/main/java/glide/api/models/commands/scan/HScanOptions.java
@@ -2,6 +2,11 @@
 package glide.api.models.commands.scan;
 
 import glide.api.commands.HashBaseCommands;
+import glide.api.models.GlideString;
+import glide.utils.ArgsBuilder;
+import java.util.Arrays;
+import java.util.Objects;
+import lombok.Builder;
 import lombok.experimental.SuperBuilder;
 
 /**
@@ -10,4 +15,51 @@
  * @see valkey.io
  */
 @SuperBuilder
-public class HScanOptions extends BaseScanOptions {}
+public class HScanOptions extends BaseScanOptions {
+
+    /** Option string to include in the HSCAN command when values are not included. */
+    public static final String NO_VALUES_API = "NOVALUES";
+
+    /**
+     * When set to true, the command will not include values in the results. This option is available
+     * from Redis version 8.0.0 and above.
+     */
+    @Builder.Default protected boolean noValues = false;
+
+    @Override
+    public String[] toArgs() {
+        return Arrays.stream(toGlideStringArgs()).map(GlideString::getString).toArray(String[]::new);
+    }
+
+    /**
+     * Creates the arguments to be used in HSCAN commands.
+     *
+     * @return a GlideString array that holds the options and their arguments.
+     */
+    @Override
+    public GlideString[] toGlideStringArgs() {
+        ArgsBuilder builder = new ArgsBuilder();
+
+        // Add options from the superclass
+        GlideString[] superArgs = super.toGlideStringArgs();
+        for (GlideString arg : superArgs) {
+            builder.add(arg);
+        }
+
+        // Add the noValues option if applicable
+        if (noValues) {
+            builder.add(NO_VALUES_API);
+        }
+
+        return builder.toArray();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof HScanOptions)) return false;
+        if (!super.equals(o)) return false;
+        HScanOptions that = (HScanOptions) o;
+        return Objects.equals(noValues, that.noValues);
+    }
+}
diff --git a/java/client/src/main/java/glide/api/models/commands/scan/HScanOptionsBinary.java b/java/client/src/main/java/glide/api/models/commands/scan/HScanOptionsBinary.java
index b68506c9c4..5bd9b4b7d2 100644
--- a/java/client/src/main/java/glide/api/models/commands/scan/HScanOptionsBinary.java
+++ b/java/client/src/main/java/glide/api/models/commands/scan/HScanOptionsBinary.java
@@ -1,7 +1,14 @@
 /** Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 */
 package glide.api.models.commands.scan;
 
+import static glide.api.models.GlideString.gs;
+
 import glide.api.commands.HashBaseCommands;
+import glide.api.models.GlideString;
+import glide.utils.ArgsBuilder;
+import java.util.Arrays;
+import java.util.Objects;
+import lombok.Builder;
 import lombok.experimental.SuperBuilder;
 
 /**
@@ -11,4 +18,45 @@
  * @see valkey.io
  */
 @SuperBuilder
-public class HScanOptionsBinary extends BaseScanOptionsBinary {}
+public class HScanOptionsBinary extends BaseScanOptionsBinary {
+    /** Option string to include in the HSCAN command when values are not included. */
+    public static final GlideString NO_VALUES_API = gs("NOVALUES");
+
+    /**
+     * When set to true, the command will not include values in the results. This option is available
+     * from Redis version 8.0.0 and above.
+     */
+    @Builder.Default protected boolean noValues = false;
+
+    /**
+     * Creates the arguments to be used in ZSCAN commands.
+     *
+     * @return a String array that holds the options and their arguments.
+     */
+    @Override
+    public String[] toArgs() {
+        ArgsBuilder builder = new ArgsBuilder();
+
+        // Add options from the superclass
+        String[] superArgs = super.toArgs();
+        for (String arg : superArgs) {
+            builder.add(arg);
+        }
+
+        // Add the noValues option if applicable
+        if (noValues) {
+            builder.add(NO_VALUES_API.toString());
+        }
+
+        return Arrays.stream(builder.toArray()).map(GlideString::getString).toArray(String[]::new);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ZScanOptionsBinary)) return false;
+        if (!super.equals(o)) return false;
+        HScanOptionsBinary that = (HScanOptionsBinary) o;
+        return Objects.equals(noValues, that.noValues);
+    }
+}
diff --git a/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptions.java b/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptions.java
index dbda89106b..e50838808f 100644
--- a/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptions.java
+++ b/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptions.java
@@ -2,6 +2,11 @@
 package glide.api.models.commands.scan;
 
 import glide.api.commands.SortedSetBaseCommands;
+import glide.api.models.GlideString;
+import glide.utils.ArgsBuilder;
+import java.util.Arrays;
+import java.util.Objects;
+import lombok.Builder;
 import lombok.experimental.SuperBuilder;
 
 /**
@@ -10,4 +15,51 @@
  * @see valkey.io
  */
 @SuperBuilder
-public class ZScanOptions extends BaseScanOptions {}
+public class ZScanOptions extends BaseScanOptions {
+
+    /** Option string to include in the ZSCAN command when scores are not included. */
+    public static final String NO_SCORES_API = "NOSCORES";
+
+    /**
+     * When set to true, the command will not include scores in the results. This option is available
+     * from Redis version 8.0.0 and above.
+     */
+    @Builder.Default protected boolean noScores = false;
+
+    @Override
+    public String[] toArgs() {
+        return Arrays.stream(toGlideStringArgs()).map(GlideString::getString).toArray(String[]::new);
+    }
+
+    /**
+     * Creates the arguments to be used in ZSCAN commands.
+     *
+     * @return a GlideString array that holds the options and their arguments.
+     */
+    @Override
+    public GlideString[] toGlideStringArgs() {
+        ArgsBuilder builder = new ArgsBuilder();
+
+        // Add options from the superclass
+        GlideString[] superArgs = super.toGlideStringArgs();
+        for (GlideString arg : superArgs) {
+            builder.add(arg);
+        }
+
+        // Add the noScores option if applicable
+        if (noScores) {
+            builder.add(NO_SCORES_API);
+        }
+
+        return builder.toArray();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ZScanOptions)) return false;
+        if (!super.equals(o)) return false;
+        ZScanOptions that = (ZScanOptions) o;
+        return Objects.equals(noScores, that.noScores);
+    }
+}
diff --git a/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptionsBinary.java b/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptionsBinary.java
index 288c75e4e7..75020bc375 100644
--- a/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptionsBinary.java
+++ b/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptionsBinary.java
@@ -1,7 +1,14 @@
 /** Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 */
 package glide.api.models.commands.scan;
 
+import static glide.api.models.GlideString.gs;
+
 import glide.api.commands.SortedSetBaseCommands;
+import glide.api.models.GlideString;
+import glide.utils.ArgsBuilder;
+import java.util.Arrays;
+import java.util.Objects;
+import lombok.Builder;
 import lombok.experimental.SuperBuilder;
 
 /**
@@ -11,4 +18,45 @@
  * @see valkey.io
  */
 @SuperBuilder
-public class ZScanOptionsBinary extends BaseScanOptionsBinary {}
+public class ZScanOptionsBinary extends BaseScanOptionsBinary {
+    /** Option string to include in the ZSCAN command when scores are not included. */
+    public static final GlideString NO_SCORES_API = gs("NOSCORES");
+
+    /**
+     * When set to true, the command will not include scores in the results. This option is available
+     * from Redis version 8.0.0 and above.
+     */
+    @Builder.Default protected boolean noScores = false;
+
+    /**
+     * Creates the arguments to be used in ZSCAN commands.
+     *
+     * @return a String array that holds the options and their arguments.
+     */
+    @Override
+    public String[] toArgs() {
+        ArgsBuilder builder = new ArgsBuilder();
+
+        // Add options from the superclass
+        String[] superArgs = super.toArgs();
+        for (String arg : superArgs) {
+            builder.add(arg);
+        }
+
+        // Add the noScores option if applicable
+        if (noScores) {
+            builder.add(NO_SCORES_API.toString());
+        }
+
+        return Arrays.stream(builder.toArray()).map(GlideString::getString).toArray(String[]::new);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ZScanOptionsBinary)) return false;
+        if (!super.equals(o)) return false;
+        ZScanOptionsBinary that = (ZScanOptionsBinary) o;
+        return Objects.equals(noScores, that.noScores);
+    }
+}
diff --git a/java/integTest/src/test/java/glide/SharedCommandTests.java b/java/integTest/src/test/java/glide/SharedCommandTests.java
index aab628e7de..59cc0f6e64 100644
--- a/java/integTest/src/test/java/glide/SharedCommandTests.java
+++ b/java/integTest/src/test/java/glide/SharedCommandTests.java
@@ -117,6 +117,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import lombok.Getter;
 import lombok.SneakyThrows;
 import org.apache.commons.lang3.ArrayUtils;
@@ -14055,7 +14056,7 @@ public void zscan(BaseClient client) {
         // Setup test data - use a large number of entries to force an iterative cursor.
         Map numberMap = new HashMap<>();
         for (Double i = 0.0; i < 50000; i++) {
-            numberMap.put(String.valueOf(i), i);
+            numberMap.put("member" + String.valueOf(i), i);
         }
         String[] charMembers = new String[] {"a", "b", "c", "d", "e"};
         Map charMap = new HashMap<>();
@@ -14186,11 +14187,27 @@ public void zscan(BaseClient client) {
         result =
                 client
                         .zscan(
-                                key1, initialCursor, ZScanOptions.builder().matchPattern("1*").count(20L).build())
+                                key1,
+                                initialCursor,
+                                ZScanOptions.builder().matchPattern("member1*").count(20L).build())
                         .get();
         assertTrue(Long.parseLong(result[resultCursorIndex].toString()) >= 0);
         assertTrue(ArrayUtils.getLength(result[resultCollectionIndex]) >= 0);
 
+        if (SERVER_VERSION.isGreaterThanOrEqualTo("7.9.0")) {
+            result =
+                    client.zscan(key1, initialCursor, ZScanOptions.builder().noScores(true).build()).get();
+            assertTrue(Long.parseLong(result[resultCursorIndex].toString()) >= 0);
+            // Cast the result collection to a String array
+            Object[] fieldsArray = (Object[]) result[resultCollectionIndex];
+            System.out.println(Arrays.toString(fieldsArray));
+            // Convert Object array to Stream for processing
+            Stream stream = Arrays.stream(fieldsArray);
+
+            // Check if all fields start with "member"
+            assertTrue(stream.allMatch(field -> ((String) field).startsWith("member")));
+        }
+
         // Exceptions
         // Non-set key
         assertEquals(OK, client.set(key2, "test").get());
@@ -14384,6 +14401,22 @@ public void zscan_binary(BaseClient client) {
         assertTrue(Long.parseLong(result[resultCursorIndex].toString()) >= 0);
         assertTrue(ArrayUtils.getLength(result[resultCollectionIndex]) >= 0);
 
+        if (SERVER_VERSION.isGreaterThanOrEqualTo("7.9.0")) {
+            result =
+                    client
+                            .zscan(key1, initialCursor, ZScanOptionsBinary.builder().noScores(true).build())
+                            .get();
+            assertTrue(Long.parseLong(result[resultCursorIndex].toString()) >= 0);
+            // Cast the result collection to a String array
+            Object[] fieldsArray = (Object[]) result[resultCollectionIndex];
+            System.out.println(Arrays.toString(fieldsArray));
+            // Convert Object array to Stream for processing
+            Stream stream = Arrays.stream(fieldsArray);
+
+            // Check if all fields start with "member"
+            assertTrue(stream.allMatch(field -> ((GlideString) field).toString().startsWith("member")));
+        }
+
         // Exceptions
         // Non-set key
         assertEquals(OK, client.set(key2, gs("test")).get());
@@ -14556,6 +14589,19 @@ public void hscan(BaseClient client) {
         assertTrue(Long.parseLong(result[resultCursorIndex].toString()) >= 0);
         assertTrue(ArrayUtils.getLength(result[resultCollectionIndex]) >= 0);
 
+        if (SERVER_VERSION.isGreaterThanOrEqualTo("7.9.0")) {
+            result =
+                    client.hscan(key1, initialCursor, HScanOptions.builder().noValues(true).build()).get();
+            assertTrue(Long.parseLong(result[resultCursorIndex].toString()) >= 0);
+            // Cast the result collection to a String array
+            Object[] fieldsArray = (Object[]) result[resultCollectionIndex];
+            // Convert Object array to Stream for processing
+            Stream stream = Arrays.stream(fieldsArray);
+
+            // Check if all fields dont start with "num"
+            assertTrue(stream.allMatch(field -> !((String) field).startsWith("num")));
+        }
+
         // Exceptions
         // Non-hash key
         assertEquals(OK, client.set(key2, "test").get());
@@ -14732,6 +14778,21 @@ public void hscan_binary(BaseClient client) {
         assertTrue(Long.parseLong(result[resultCursorIndex].toString()) >= 0);
         assertTrue(ArrayUtils.getLength(result[resultCollectionIndex]) >= 0);
 
+        if (SERVER_VERSION.isGreaterThanOrEqualTo("7.9.0")) {
+            result =
+                    client
+                            .hscan(key1, initialCursor, HScanOptionsBinary.builder().noValues(true).build())
+                            .get();
+            assertTrue(Long.parseLong(result[resultCursorIndex].toString()) >= 0);
+            // Cast the result collection to a String array
+            Object[] fieldsArray = (Object[]) result[resultCollectionIndex];
+            // Convert Object array to Stream for processing
+            Stream stream = Arrays.stream(fieldsArray);
+
+            // Check if all fields dont start with "num"
+            assertTrue(stream.allMatch(field -> !((GlideString) field).toString().startsWith("num")));
+        }
+
         // Exceptions
         // Non-hash key
         assertEquals(OK, client.set(key2, gs("test")).get());
diff --git a/java/integTest/src/test/java/glide/TransactionTestUtilities.java b/java/integTest/src/test/java/glide/TransactionTestUtilities.java
index c874dbb3b1..4e41fb2751 100644
--- a/java/integTest/src/test/java/glide/TransactionTestUtilities.java
+++ b/java/integTest/src/test/java/glide/TransactionTestUtilities.java
@@ -382,33 +382,54 @@ private static Object[] hashCommands(BaseTransaction transaction) {
                 .hscan(hashKey2, "0")
                 .hscan(hashKey2, "0", HScanOptions.builder().count(20L).build());
 
-        return new Object[] {
-            2L, // hset(hashKey1, Map.of(field1, value1, field2, value2))
-            value1, // hget(hashKey1, field1)
-            2L, // hlen(hashKey1)
-            true, // hexists(hashKey1, field2)
-            false, // hsetnx(hashKey1, field1, value1)
-            new String[] {value1, null, value2}, // hmget(hashKey1, new String[] {...})
-            Map.of(field1, value1, field2, value2), // hgetall(hashKey1)
-            1L, // hdel(hashKey1, new String[] {field1})
-            new String[] {value2}, // hvals(hashKey1)
-            field2, // hrandfield(hashKey1)
-            new String[] {field2}, // hrandfieldWithCount(hashKey1, 2)
-            new String[] {field2, field2}, // hrandfieldWithCount(hashKey1, -2)
-            new String[][] {{field2, value2}}, // hrandfieldWithCountWithValues(hashKey1, 2)
-            new String[][] {
-                {field2, value2}, {field2, value2}
-            }, // hrandfieldWithCountWithValues(hashKey1, -2)
-            5L, // hincrBy(hashKey1, field3, 5)
-            10.5, // hincrByFloat(hashKey1, field3, 5.5)
-            new String[] {field2, field3}, // hkeys(hashKey1)
-            (long) value2.length(), // hstrlen(hashKey1, field2)
-            1L, // hset(hashKey2, Map.of(field1, value1))
-            new Object[] {"0", new Object[] {field1, value1}}, // hscan(hashKey2, "0")
-            new Object[] {
-                "0", new Object[] {field1, value1}
-            }, // hscan(hashKey2, "0", HScanOptions.builder().count(20L).build());
-        };
+        if (SERVER_VERSION.isGreaterThanOrEqualTo("7.9.0")) {
+            transaction
+                    .hscan(hashKey2, "0", HScanOptions.builder().count(20L).noValues(false).build())
+                    .hscan(hashKey2, "0", HScanOptions.builder().count(20L).noValues(true).build());
+        }
+
+        var result =
+                new Object[] {
+                    2L, // hset(hashKey1, Map.of(field1, value1, field2, value2))
+                    value1, // hget(hashKey1, field1)
+                    2L, // hlen(hashKey1)
+                    true, // hexists(hashKey1, field2)
+                    false, // hsetnx(hashKey1, field1, value1)
+                    new String[] {value1, null, value2}, // hmget(hashKey1, new String[] {...})
+                    Map.of(field1, value1, field2, value2), // hgetall(hashKey1)
+                    1L, // hdel(hashKey1, new String[] {field1})
+                    new String[] {value2}, // hvals(hashKey1)
+                    field2, // hrandfield(hashKey1)
+                    new String[] {field2}, // hrandfieldWithCount(hashKey1, 2)
+                    new String[] {field2, field2}, // hrandfieldWithCount(hashKey1, -2)
+                    new String[][] {{field2, value2}}, // hrandfieldWithCountWithValues(hashKey1, 2)
+                    new String[][] {
+                        {field2, value2}, {field2, value2}
+                    }, // hrandfieldWithCountWithValues(hashKey1, -2)
+                    5L, // hincrBy(hashKey1, field3, 5)
+                    10.5, // hincrByFloat(hashKey1, field3, 5.5)
+                    new String[] {field2, field3}, // hkeys(hashKey1)
+                    (long) value2.length(), // hstrlen(hashKey1, field2)
+                    1L, // hset(hashKey2, Map.of(field1, value1))
+                    new Object[] {"0", new Object[] {field1, value1}}, // hscan(hashKey2, "0")
+                    new Object[] {
+                        "0", new Object[] {field1, value1}
+                    }, // hscan(hashKey2, "0", HScanOptions.builder().count(20L).build());
+                };
+
+        if (SERVER_VERSION.isGreaterThanOrEqualTo("7.9.0")) {
+            result =
+                    concatenateArrays(
+                            result,
+                            new Object[] {
+                                new Object[] {"0", new Object[] {field1, value1}}, // hscan(hashKey2, "0",
+                                // HScanOptions.builder().count(20L).noValues(false).build());
+                                new Object[] {"0", new Object[] {field1}}, // hscan(hashKey2, "0",
+                                // HScanOptions.builder().count(20L).noValues(true).build());
+                            });
+        }
+
+        return result;
     }
 
     private static Object[] listCommands(BaseTransaction transaction) {
@@ -648,8 +669,14 @@ private static Object[] sortedSetCommands(BaseTransaction transaction) {
                 .zrandmemberWithCount(zSetKey2, 1)
                 .zrandmemberWithCountWithScores(zSetKey2, 1)
                 .zscan(zSetKey2, "0")
-                .zscan(zSetKey2, "0", ZScanOptions.builder().count(20L).build())
-                .bzpopmin(new String[] {zSetKey2}, .1);
+                .zscan(zSetKey2, "0", ZScanOptions.builder().count(20L).build());
+        if (SERVER_VERSION.isGreaterThanOrEqualTo("7.9.0")) {
+            transaction
+                    .zscan(zSetKey2, 0, ZScanOptions.builder().count(20L).noScores(false).build())
+                    .zscan(zSetKey2, 0, ZScanOptions.builder().count(20L).noScores(true).build());
+        }
+
+        transaction.bzpopmin(new String[] {zSetKey2}, .1);
         // zSetKey2 is now empty
 
         if (SERVER_VERSION.isGreaterThanOrEqualTo("6.2.0")) {
@@ -712,9 +739,28 @@ private static Object[] sortedSetCommands(BaseTransaction transaction) {
                     new Object[] {
                         "0", new String[] {"one", "1"}
                     }, // zscan(zSetKey2, 0, ZScanOptions.builder().count(20L).build())
-                    new Object[] {zSetKey2, "one", 1.0}, // bzpopmin(new String[] { zsetKey2 }, .1)
                 };
 
+        if (SERVER_VERSION.isGreaterThanOrEqualTo("7.9.0")) {
+            expectedResults =
+                    concatenateArrays(
+                            expectedResults,
+                            new Object[] {
+                                new Object[] {
+                                    "0", new String[] {"one", "1"}
+                                }, // zscan(zSetKey2, 0, ZScanOptions.builder().count(20L).noScores(false).build())
+                                new Object[] {
+                                    "0", new String[] {"one"}
+                                }, // zscan(zSetKey2, 0, ZScanOptions.builder().count(20L).noScores(true).build())
+                            });
+        }
+
+        expectedResults =
+                concatenateArrays(
+                        expectedResults,
+                        new Object[] {
+                            new Object[] {zSetKey2, "one", 1.0}, // bzpopmin(new String[] { zsetKey2 }, .1)
+                        });
         if (SERVER_VERSION.isGreaterThanOrEqualTo("6.2.0")) {
             expectedResults =
                     concatenateArrays(
@@ -751,6 +797,7 @@ private static Object[] sortedSetCommands(BaseTransaction transaction) {
                                 2L, // zintercard(new String[] {zSetKey4, zSetKey3}, 2)
                             });
         }
+
         return expectedResults;
     }
 
diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts
index b96be60469..019d2db556 100644
--- a/node/src/BaseClient.ts
+++ b/node/src/BaseClient.ts
@@ -33,6 +33,7 @@ import {
     GeoSearchStoreResultOptions,
     GeoUnit,
     GeospatialData,
+    HScanOptions,
     InsertPosition,
     KeyWeight,
     LPosOptions,
@@ -2049,12 +2050,13 @@ export class BaseClient {
      *
      * @param key - The key of the set.
      * @param cursor - The cursor that points to the next iteration of results. A value of `"0"` indicates the start of the search.
-     * @param options - (Optional) The {@link BaseScanOptions}.
+     * @param options - (Optional) The {@link HScanOptions}.
      * @returns An array of the `cursor` and the subset of the hash held by `key`.
      * The first element is always the `cursor` for the next iteration of results. `"0"` will be the `cursor`
      * returned on the last iteration of the hash. The second element is always an array of the subset of the
      * hash held in `key`. The array in the second element is always a flattened series of string pairs,
      * where the value is at even indices and the value is at odd indices.
+     * If options.noValues is set to `true`, the second element will only contain the fields without the values.
      *
      * @example
      * ```typescript
@@ -2076,13 +2078,36 @@ export class BaseClient {
      * // Cursor:  39
      * // Members:  ['field 63', 'value 63', 'field 293', 'value 293', 'field 162', 'value 162']
      * // Cursor:  0
-     * // Members:  ['value 55', '55', 'value 24', '24', 'value 90', '90', 'value 113', '113']
+     * // Members:  ['field 55', 'value 55', 'field 24', 'value 24', 'field 90', 'value 90', 'field 113', 'value 113']
+     * ```
+     * @example
+     * ```typescript
+     * // Hscan with noValues
+     * let newCursor = "0";
+     * let result = [];
+     * do {
+     *      result = await client.hscan(key1, newCursor, {
+     *          match: "*",
+     *          count: 3,
+     *          noValues: true,
+     *      });
+     *      newCursor = result[0];
+     *      console.log("Cursor: ", newCursor);
+     *      console.log("Members: ", result[1]);
+     * } while (newCursor !== "0");
+     * // The output of the code above is something similar to:
+     * // Cursor:  31
+     * // Members:  ['field 79', 'field 20', 'field 115']
+     * // Cursor:  39
+     * // Members:  ['field 63', 'field 293', 'field 162']
+     * // Cursor:  0
+     * // Members:  ['field 55', 'field 24', 'field 90', 'field 113']
      * ```
      */
     public async hscan(
         key: string,
         cursor: string,
-        options?: BaseScanOptions,
+        options?: HScanOptions,
     ): Promise<[string, string[]]> {
         return this.createWritePromise(createHScan(key, cursor, options));
     }
@@ -6442,21 +6467,25 @@ export class BaseClient {
      * @param key - The key of the sorted set.
      * @param cursor - The cursor that points to the next iteration of results. A value of `"0"` indicates the start of
      *      the search.
-     * @param options - (Optional) The `zscan` options - see {@link BaseScanOptions} and {@link DecoderOption}.
+     * @param options - (Optional) The `zscan` options - see {@link ZScanOptions} and {@link DecoderOption}.
      * @returns An `Array` of the `cursor` and the subset of the sorted set held by `key`.
      *      The first element is always the `cursor` for the next iteration of results. `0` will be the `cursor`
      *      returned on the last iteration of the sorted set. The second element is always an `Array` of the subset
      *      of the sorted set held in `key`. The `Array` in the second element is always a flattened series of
      *      `string` pairs, where the value is at even indices and the score is at odd indices.
+     *      If options.noScores is to `true`, the second element will only contain the members without scores.
      *
      * @example
      * ```typescript
-     * // Assume "key1" contains a sorted set with multiple members
-     * let cursor = "0";
+     * // Assume "key" contains a sorted set with multiple members
+     * let newCursor = "0";
+     * let result = [];
+     *
      * do {
      *      const result = await client.zscan(key1, cursor, {
      *          match: "*",
      *          count: 5,
+     *          noScores: true,
      *      });
      *      cursor = result[0];
      *      console.log("Cursor: ", cursor);
@@ -6464,9 +6493,9 @@ export class BaseClient {
      * } while (cursor !== "0");
      * // The output of the code above is something similar to:
      * // Cursor:  123
-     * // Members:  ['value 163', '163', 'value 114', '114', 'value 25', '25', 'value 82', '82', 'value 64', '64']
+     * // Members:  ['value 163', 'value 114', 'value 25', 'value 82', 'value 64']
      * // Cursor:  47
-     * // Members:  ['value 39', '39', 'value 127', '127', 'value 43', '43', 'value 139', '139', 'value 211', '211']
+     * // Members:  ['value 39', 'value 127', 'value 43', 'value 139', 'value 211']
      * // Cursor:  0
      * // Members:  ['value 55', '55', 'value 24', '24', 'value 90', '90', 'value 113', '113']
      * ```
@@ -6474,7 +6503,7 @@ export class BaseClient {
     public async zscan(
         key: GlideString,
         cursor: string,
-        options?: BaseScanOptions & DecoderOption,
+        options?: ZScanOptions & DecoderOption,
     ): Promise<[string, GlideString[]]> {
         return this.createWritePromise<[GlideString, GlideString[]]>(
             createZScan(key, cursor, options),
diff --git a/node/src/Commands.ts b/node/src/Commands.ts
index e7be32de14..2d26178332 100644
--- a/node/src/Commands.ts
+++ b/node/src/Commands.ts
@@ -3669,12 +3669,16 @@ export function createHRandField(
 export function createHScan(
     key: string,
     cursor: string,
-    options?: BaseScanOptions,
+    options?: HScanOptions,
 ): command_request.Command {
     let args: GlideString[] = [key, cursor];
 
     if (options) {
         args = args.concat(convertBaseScanOptionsToArgsArray(options));
+
+        if (options.noValues) {
+            args.push("NOVALUES");
+        }
     }
 
     return createCommand(RequestType.HScan, args);
@@ -3764,9 +3768,8 @@ export function createWait(
 }
 
 /**
- * This base class represents the common set of optional arguments for the `SCAN` family of commands.
- * Concrete implementations of this class are tied to specific SCAN commands (`SCAN`, `HSCAN`, `SSCAN`,
- * and `ZSCAN`).
+ * This base class represents the common set of optional arguments for the SCAN family of commands.
+ * Concrete implementations of this class are tied to specific SCAN commands (`SCAN`, `SSCAN`).
  */
 export type BaseScanOptions = {
     /**
@@ -3782,7 +3785,29 @@ export type BaseScanOptions = {
      * sorted set. `COUNT` could be ignored until the sorted set is large enough for the `SCAN` commands to
      * represent the results as compact single-allocation packed encoding.
      */
-    count?: number;
+    readonly count?: number;
+};
+
+/**
+ * Options specific to the ZSCAN command, extending from the base scan options.
+ */
+export type ZScanOptions = BaseScanOptions & {
+    /**
+     * If true, the scores are not included in the results.
+     * Supported from Valkey 8.0.0 and above.
+     */
+    readonly noScores?: boolean;
+};
+
+/**
+ * Options specific to the HSCAN command, extending from the base scan options.
+ */
+export type HScanOptions = BaseScanOptions & {
+    /**
+     * If true, the values of the fields are not included in the results.
+     * Supported from Valkey 8.0.0 and above.
+     */
+    readonly noValues?: boolean;
 };
 
 /**
@@ -3810,12 +3835,16 @@ function convertBaseScanOptionsToArgsArray(
 export function createZScan(
     key: GlideString,
     cursor: string,
-    options?: BaseScanOptions,
+    options?: ZScanOptions,
 ): command_request.Command {
     let args = [key, cursor];
 
     if (options) {
         args = args.concat(convertBaseScanOptionsToArgsArray(options));
+
+        if (options.noScores) {
+            args.push("NOSCORES");
+        }
     }
 
     return createCommand(RequestType.ZScan, args);
diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts
index 9503adbe0a..2f02f65b71 100644
--- a/node/src/Transaction.ts
+++ b/node/src/Transaction.ts
@@ -41,6 +41,7 @@ import {
     GeoSearchStoreResultOptions,
     GeoUnit,
     GeospatialData,
+    HScanOptions,
     InfoOptions,
     InsertPosition,
     KeyWeight,
@@ -996,15 +997,16 @@ export class BaseTransaction> {
      *
      * @param key - The key of the set.
      * @param cursor - The cursor that points to the next iteration of results. A value of `"0"` indicates the start of the search.
-     * @param options - (Optional) The {@link BaseScanOptions}.
+     * @param options - (Optional) The {@link HScanOptions}.
      *
-     * Command Response -  An array of the `cursor` and the subset of the hash held by `key`.
+     * Command Response - An array of the `cursor` and the subset of the hash held by `key`.
      * The first element is always the `cursor` for the next iteration of results. `"0"` will be the `cursor`
      * returned on the last iteration of the hash. The second element is always an array of the subset of the
      * hash held in `key`. The array in the second element is always a flattened series of string pairs,
      * where the value is at even indices and the value is at odd indices.
+     * If options.noValues is set to `true`, the second element will only contain the fields without the values.
      */
-    public hscan(key: string, cursor: string, options?: BaseScanOptions): T {
+    public hscan(key: string, cursor: string, options?: HScanOptions): T {
         return this.addAndReturn(createHScan(key, cursor, options));
     }
 
@@ -3675,19 +3677,16 @@ export class BaseTransaction> {
      * @param key - The key of the sorted set.
      * @param cursor - The cursor that points to the next iteration of results. A value of `"0"` indicates the start of
      *      the search.
-     * @param options - (Optional) The `zscan` options - see {@link BaseScanOptions}
+     * @param options - (Optional) The `zscan` options - see {@link ZScanOptions}
      *
      * Command Response - An `Array` of the `cursor` and the subset of the sorted set held by `key`.
      *      The first element is always the `cursor` for the next iteration of results. `0` will be the `cursor`
      *      returned on the last iteration of the sorted set. The second element is always an `Array` of the subset
      *      of the sorted set held in `key`. The `Array` in the second element is always a flattened series of
      *      `String` pairs, where the value is at even indices and the score is at odd indices.
+     *      If options.noScores is to `true`, the second element will only contain the members without scores.
      */
-    public zscan(
-        key: GlideString,
-        cursor: string,
-        options?: BaseScanOptions,
-    ): T {
+    public zscan(key: GlideString, cursor: string, options?: ZScanOptions): T {
         return this.addAndReturn(createZScan(key, cursor, options));
     }
 
diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts
index 89987d1c92..28cd31d95b 100644
--- a/node/tests/SharedTests.ts
+++ b/node/tests/SharedTests.ts
@@ -1415,7 +1415,7 @@ export function runBaseTests(config: {
     it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
         `hscan test_%p`,
         async (protocol) => {
-            await runTest(async (client: BaseClient) => {
+            await runTest(async (client: BaseClient, cluster) => {
                 const key1 = "{key}-1" + uuidv4();
                 const initialCursor = "0";
                 const defaultCount = 20;
@@ -1556,6 +1556,22 @@ export function runBaseTests(config: {
                 });
                 expect(result[resultCursorIndex]).not.toEqual(initialCursor);
                 expect(result[resultCollectionIndex].length).toBeGreaterThan(0);
+
+                if (!cluster.checkIfServerVersionLessThan("7.9.0")) {
+                    const result = await client.hscan(key1, initialCursor, {
+                        noValues: true,
+                    });
+                    const resultCursor = result[resultCursorIndex];
+                    const fieldsArray = result[
+                        resultCollectionIndex
+                    ] as string[];
+
+                    // Verify that the cursor is not "0" and values are not included
+                    expect(resultCursor).not.toEqual("0");
+                    expect(
+                        fieldsArray.every((field) => !field.startsWith("num")),
+                    ).toBeTruthy();
+                }
             }, protocol);
         },
         config.timeout,
@@ -9256,7 +9272,7 @@ export function runBaseTests(config: {
                 const numberMap: Record = {};
 
                 for (let i = 0; i < 50000; i++) {
-                    numberMap[i.toString()] = i;
+                    numberMap["member" + i.toString()] = i;
                 }
 
                 const charMembers = ["a", "b", "c", "d", "e"];
@@ -9369,12 +9385,30 @@ export function runBaseTests(config: {
 
                 // Test count with match returns a non-empty list
                 result = await client.zscan(key1, initialCursor, {
-                    match: "1*",
+                    match: "member1*",
                     count: 20,
                 });
                 expect(result[resultCursorIndex]).not.toEqual("0");
                 expect(result[resultCollectionIndex].length).toBeGreaterThan(0);
 
+                if (!cluster.checkIfServerVersionLessThan("7.9.0")) {
+                    const result = await client.zscan(key1, initialCursor, {
+                        noScores: true,
+                    });
+                    const resultCursor = result[resultCursorIndex];
+                    const fieldsArray = result[
+                        resultCollectionIndex
+                    ] as string[];
+
+                    // Verify that the cursor is not "0" and values are not included
+                    expect(resultCursor).not.toEqual("0");
+                    expect(
+                        fieldsArray.every((field) =>
+                            field.startsWith("member"),
+                        ),
+                    ).toBeTruthy();
+                }
+
                 // Exceptions
                 // Non-set key
                 expect(await client.set(key2, "test")).toEqual("OK");
diff --git a/node/tests/TestUtilities.ts b/node/tests/TestUtilities.ts
index 7136c043ab..5bfbae0013 100644
--- a/node/tests/TestUtilities.ts
+++ b/node/tests/TestUtilities.ts
@@ -766,6 +766,24 @@ export async function transactionTest(
     responseData.push(["hset(key4, { [field]: value })", 1]);
     baseTransaction.hscan(key4, "0");
     responseData.push(['hscan(key4, "0")', ["0", [field, value]]]);
+
+    if (gte(version, "7.9.0")) {
+        baseTransaction.hscan(key4, "0", { noValues: false });
+        responseData.push([
+            'hscan(key4, "0", {noValues: false})',
+            ["0", [field, value]],
+        ]);
+        baseTransaction.hscan(key4, "0", {
+            match: "*",
+            count: 20,
+            noValues: true,
+        });
+        responseData.push([
+            'hscan(key4, "0", {match: "*", count: 20, noValues:true})',
+            ["0", [field]],
+        ]);
+    }
+
     baseTransaction.hscan(key4, "0", { match: "*", count: 20 });
     responseData.push([
         'hscan(key4, "0", {match: "*", count: 20})',
@@ -1009,6 +1027,25 @@ export async function transactionTest(
     responseData.push(["zadd(key12, { one: 1, two: 2 })", 2]);
     baseTransaction.zscan(key12, "0");
     responseData.push(['zscan(key12, "0")', ["0", ["one", "1", "two", "2"]]]);
+
+    if (gte(version, "7.9.0")) {
+        baseTransaction.zscan(key12, "0", { noScores: false });
+        responseData.push([
+            'zscan(key12, "0", {noScores: false})',
+            ["0", ["one", "1", "two", "2"]],
+        ]);
+
+        baseTransaction.zscan(key12, "0", {
+            match: "*",
+            count: 20,
+            noScores: true,
+        });
+        responseData.push([
+            'zscan(key12, "0", {match: "*", count: 20, noScores:true})',
+            ["0", ["one", "two"]],
+        ]);
+    }
+
     baseTransaction.zscan(key12, "0", { match: "*", count: 20 });
     responseData.push([
         'zscan(key12, "0", {match: "*", count: 20})',
diff --git a/python/python/glide/async_commands/core.py b/python/python/glide/async_commands/core.py
index c3bb4e1aea..87c2c9b593 100644
--- a/python/python/glide/async_commands/core.py
+++ b/python/python/glide/async_commands/core.py
@@ -6117,6 +6117,7 @@ async def zscan(
         cursor: TEncodable,
         match: Optional[TEncodable] = None,
         count: Optional[int] = None,
+        no_scores: bool = False,
     ) -> List[Union[bytes, List[bytes]]]:
         """
         Iterates incrementally over a sorted set.
@@ -6135,13 +6136,15 @@ async def zscan(
             count (Optional[int]): `COUNT` is a just a hint for the command for how many elements to fetch from the
                 sorted set. `COUNT` could be ignored until the sorted set is large enough for the `SCAN` commands to
                 represent the results as compact single-allocation packed encoding.
+            no_scores (bool): If `True`, the command will not return scores associated with the members. Since Valkey "8.0.0".
 
         Returns:
             List[Union[bytes, List[bytes]]]: An `Array` of the `cursor` and the subset of the sorted set held by `key`.
-                The first element is always the `cursor` for the next iteration of results. `0` will be the `cursor`
-                returned on the last iteration of the sorted set. The second element is always an `Array` of the subset
-                of the sorted set held in `key`. The `Array` in the second element is always a flattened series of
-                `String` pairs, where the value is at even indices and the score is at odd indices.
+            The first element is always the `cursor` for the next iteration of results. `0` will be the `cursor`
+            returned on the last iteration of the sorted set. The second element is always an `Array` of the subset
+            of the sorted set held in `key`. The `Array` in the second element is always a flattened series of
+            `String` pairs, where the value is at even indices and the score is at odd indices.
+            If `no_scores` is set to`True`, the second element will only contain the members without scores.
 
         Examples:
             # Assume "key" contains a sorted set with multiple members
@@ -6160,12 +6163,31 @@ async def zscan(
             Members: [b'value 39', b'39', b'value 127', b'127', b'value 43', b'43', b'value 139', b'139', b'value 211', b'211']
             Cursor: 0
             Members: [b'value 55', b'55', b'value 24', b'24', b'value 90', b'90', b'value 113', b'113']
+
+            # Using no-score
+            >>> result_cursor = "0"
+            >>> while True:
+            ...     result = await client.zscan("key", "0", match="*", count=5, no_scores=True)
+            ...     new_cursor = str(result[0])
+            ...     print("Cursor: ", new_cursor)
+            ...     print("Members: ", result[1])
+            ...     if new_cursor == "0":
+            ...         break
+            ...     result_cursor = new_cursor
+            Cursor: 123
+            Members: [b'value 163', b'value 114', b'value 25', b'value 82', b'value 64']
+            Cursor: 47
+            Members: [b'value 39', b'value 127', b'value 43', b'value 139', b'value 211']
+            Cursor: 0
+            Members: [b'value 55', b'value 24', b'value 90', b'value 113']
         """
         args: List[TEncodable] = [key, cursor]
         if match is not None:
             args += ["MATCH", match]
         if count is not None:
             args += ["COUNT", str(count)]
+        if no_scores:
+            args.append("NOSCORES")
 
         return cast(
             List[Union[bytes, List[bytes]]],
@@ -6178,6 +6200,7 @@ async def hscan(
         cursor: TEncodable,
         match: Optional[TEncodable] = None,
         count: Optional[int] = None,
+        no_values: bool = False,
     ) -> List[Union[bytes, List[bytes]]]:
         """
         Iterates incrementally over a hash.
@@ -6196,6 +6219,7 @@ async def hscan(
             count (Optional[int]): `COUNT` is a just a hint for the command for how many elements to fetch from the hash.
                 `COUNT` could be ignored until the hash is large enough for the `SCAN` commands to represent the results
                 as compact single-allocation packed encoding.
+            no_values (bool): If `True`, the command will not return values the fields in the hash. Since Valkey "8.0.0".
 
         Returns:
             List[Union[bytes, List[bytes]]]: An `Array` of the `cursor` and the subset of the hash held by `key`.
@@ -6203,6 +6227,7 @@ async def hscan(
                 returned on the last iteration of the hash. The second element is always an `Array` of the subset of the
                 hash held in `key`. The `Array` in the second element is always a flattened series of `String` pairs,
                 where the value is at even indices and the score is at odd indices.
+                If `no_values` is set to `True`, the second element will only contain the fields without the values.
 
         Examples:
             # Assume "key" contains a hash with multiple members
@@ -6221,12 +6246,31 @@ async def hscan(
             Members: [b'field 63', b'value 63', b'field 293', b'value 293', b'field 162', b'value 162']
             Cursor: 0
             Members: [b'field 420', b'value 420', b'field 221', b'value 221']
+
+            # Use no-values
+            >>> result_cursor = "0"
+            >>> while True:
+            ...     result = await client.hscan("key", "0", match="*", count=3, no_values=True)
+            ...     new_cursor = str(result [0])
+            ...     print("Cursor: ", new_cursor)
+            ...     print("Members: ", result[1])
+            ...     if new_cursor == "0":
+            ...         break
+            ...     result_cursor = new_cursor
+            Cursor: 1
+            Members: [b'field 79',b'field 20', b'field 115']
+            Cursor: 39
+            Members: [b'field 63', b'field 293', b'field 162']
+            Cursor: 0
+            Members: [b'field 420', b'field 221']
         """
         args: List[TEncodable] = [key, cursor]
         if match is not None:
             args += ["MATCH", match]
         if count is not None:
             args += ["COUNT", str(count)]
+        if no_values:
+            args.append("NOVALUES")
 
         return cast(
             List[Union[bytes, List[bytes]]],
diff --git a/python/python/glide/async_commands/transaction.py b/python/python/glide/async_commands/transaction.py
index 8aa0f0fa1d..3906c17126 100644
--- a/python/python/glide/async_commands/transaction.py
+++ b/python/python/glide/async_commands/transaction.py
@@ -4463,6 +4463,7 @@ def zscan(
         cursor: TEncodable,
         match: Optional[TEncodable] = None,
         count: Optional[int] = None,
+        no_scores: bool = False,
     ) -> TTransaction:
         """
         Iterates incrementally over a sorted set.
@@ -4474,26 +4475,30 @@ def zscan(
             cursor (TEncodable): The cursor that points to the next iteration of results. A value of "0" indicates the start of
                 the search.
             match (Optional[TEncodable]): The match filter is applied to the result of the command and will only include
-                strings or byte string that match the pattern specified. If the sorted set is large enough for scan commands to return
+                strings or byte strings that match the pattern specified. If the sorted set is large enough for scan commands to return
                 only a subset of the sorted set then there could be a case where the result is empty although there are
                 items that match the pattern specified. This is due to the default `COUNT` being `10` which indicates
                 that it will only fetch and match `10` items from the list.
             count (Optional[int]): `COUNT` is a just a hint for the command for how many elements to fetch from the
                 sorted set. `COUNT` could be ignored until the sorted set is large enough for the `SCAN` commands to
                 represent the results as compact single-allocation packed encoding.
+            no_scores (bool): If `True`, the command will not return scores associated with the members. Since Valkey "8.0.0".
 
         Returns:
             List[Union[bytes, List[bytes]]]: An `Array` of the `cursor` and the subset of the sorted set held by `key`.
-                The first element is always the `cursor` for the next iteration of results. `0` will be the `cursor`
-                returned on the last iteration of the sorted set. The second element is always an `Array` of the subset
-                of the sorted set held in `key`. The `Array` in the second element is always a flattened series of
-                `String` pairs, where the value is at even indices and the score is at odd indices.
+            The first element is always the `cursor` for the next iteration of results. `0` will be the `cursor`
+            returned on the last iteration of the sorted set. The second element is always an `Array` of the subset
+            of the sorted set held in `key`. The `Array` in the second element is always a flattened series of
+            `String` pairs, where the value is at even indices and the score is at odd indices.
+            If `no_scores` is set to`True`, the second element will only contain the members without scores.
         """
         args = [key, cursor]
         if match is not None:
             args += ["MATCH", match]
         if count is not None:
             args += ["COUNT", str(count)]
+        if no_scores:
+            args.append("NOSCORES")
 
         return self.append_command(RequestType.ZScan, args)
 
@@ -4503,6 +4508,7 @@ def hscan(
         cursor: TEncodable,
         match: Optional[TEncodable] = None,
         count: Optional[int] = None,
+        no_values: bool = False,
     ) -> TTransaction:
         """
         Iterates incrementally over a hash.
@@ -4514,13 +4520,14 @@ def hscan(
             cursor (TEncodable): The cursor that points to the next iteration of results. A value of "0" indicates the start of
                 the search.
             match (Optional[TEncodable]): The match filter is applied to the result of the command and will only include
-                strings or bytes strings that match the pattern specified. If the hash is large enough for scan commands to return only a
+                strings or byte strings that match the pattern specified. If the hash is large enough for scan commands to return only a
                 subset of the hash then there could be a case where the result is empty although there are items that
                 match the pattern specified. This is due to the default `COUNT` being `10` which indicates that it will
                 only fetch and match `10` items from the list.
             count (Optional[int]): `COUNT` is a just a hint for the command for how many elements to fetch from the hash.
                 `COUNT` could be ignored until the hash is large enough for the `SCAN` commands to represent the results
                 as compact single-allocation packed encoding.
+            no_values (bool): If `True`, the command will not return values the fields in the hash. Since Valkey "8.0.0".
 
         Returns:
             List[Union[bytes, List[bytes]]]: An `Array` of the `cursor` and the subset of the hash held by `key`.
@@ -4528,12 +4535,15 @@ def hscan(
                 returned on the last iteration of the hash. The second element is always an `Array` of the subset of the
                 hash held in `key`. The `Array` in the second element is always a flattened series of `String` pairs,
                 where the value is at even indices and the score is at odd indices.
+                If `no_values` is set to `True`, the second element will only contain the fields without the values.
         """
         args = [key, cursor]
         if match is not None:
             args += ["MATCH", match]
         if count is not None:
             args += ["COUNT", str(count)]
+        if no_values:
+            args.append("NOVALUES")
 
         return self.append_command(RequestType.HScan, args)
 
diff --git a/python/python/tests/test_async_client.py b/python/python/tests/test_async_client.py
index a5026e70dc..9e3664ba08 100644
--- a/python/python/tests/test_async_client.py
+++ b/python/python/tests/test_async_client.py
@@ -10020,6 +10020,16 @@ async def test_zscan(self, glide_client: GlideClusterClient):
         assert result[result_cursor_index] != b"0"
         assert len(result[result_collection_index]) >= 0
 
+        # Test no_scores option
+        if not await check_if_server_version_lt(glide_client, "7.9.0"):
+            result = await glide_client.zscan(key1, initial_cursor, no_scores=True)
+            assert result[result_cursor_index] != b"0"
+            values_array = cast(List[bytes], result[result_collection_index])
+            # Verify that scores are not included
+            assert all(
+                item.startswith(b"value") and item.isascii() for item in values_array
+            )
+
         # Exceptions
         # Non-set key
         assert await glide_client.set(key2, "test") == OK
@@ -10137,6 +10147,16 @@ async def test_hscan(self, glide_client: GlideClusterClient):
         assert result[result_cursor_index] != b"0"
         assert len(result[result_collection_index]) >= 0
 
+        # Test no_values option
+        if not await check_if_server_version_lt(glide_client, "7.9.0"):
+            result = await glide_client.hscan(key1, initial_cursor, no_values=True)
+            assert result[result_cursor_index] != b"0"
+            values_array = cast(List[bytes], result[result_collection_index])
+            # Verify that values are not included
+            assert all(
+                item.startswith(b"field") and item.isascii() for item in values_array
+            )
+
         # Exceptions
         # Non-hash key
         assert await glide_client.set(key2, "test") == OK
diff --git a/python/python/tests/test_transaction.py b/python/python/tests/test_transaction.py
index a279b76f87..964b1309a9 100644
--- a/python/python/tests/test_transaction.py
+++ b/python/python/tests/test_transaction.py
@@ -306,6 +306,9 @@ async def transaction_test(
     args.append([b"0", [key3.encode(), b"10.5"]])
     transaction.hscan(key4, "0", match="*", count=10)
     args.append([b"0", [key3.encode(), b"10.5"]])
+    if not await check_if_server_version_lt(glide_client, "7.9.0"):
+        transaction.hscan(key4, "0", match="*", count=10, no_values=True)
+        args.append([b"0", [key3.encode()]])
     transaction.hrandfield(key4)
     args.append(key3_bytes)
     transaction.hrandfield_count(key4, 1)
@@ -458,6 +461,9 @@ async def transaction_test(
     args.append([b"0", [b"three", b"3"]])
     transaction.zscan(key8, "0", match="*", count=20)
     args.append([b"0", [b"three", b"3"]])
+    if not await check_if_server_version_lt(glide_client, "7.9.0"):
+        transaction.zscan(key8, "0", match="*", count=20, no_scores=True)
+        args.append([b"0", [b"three"]])
     transaction.zpopmax(key8)
     args.append({b"three": 3.0})
     transaction.zpopmin(key8)

From b1cdc0b3c8ae3cfc94934542a50869526ae5599a Mon Sep 17 00:00:00 2001
From: Shoham Elias 
Date: Tue, 20 Aug 2024 20:31:18 +0000
Subject: [PATCH 2/3] fix test

Signed-off-by: Shoham Elias 
---
 java/integTest/src/test/java/glide/SharedCommandTests.java | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/java/integTest/src/test/java/glide/SharedCommandTests.java b/java/integTest/src/test/java/glide/SharedCommandTests.java
index 59cc0f6e64..06be6cea3c 100644
--- a/java/integTest/src/test/java/glide/SharedCommandTests.java
+++ b/java/integTest/src/test/java/glide/SharedCommandTests.java
@@ -14249,11 +14249,11 @@ public void zscan_binary(BaseClient client) {
         // Setup test data - use a large number of entries to force an iterative cursor.
         Map numberMap = new HashMap<>();
         for (Double i = 0.0; i < 50000; i++) {
-            numberMap.put(gs(String.valueOf(i)), i);
+            numberMap.put(gs("member" + String.valueOf(i)), i);
         }
         Map numberMap_strings = new HashMap<>();
         for (Double i = 0.0; i < 50000; i++) {
-            numberMap_strings.put(String.valueOf(i), i);
+            numberMap_strings.put("member" + String.valueOf(i), i);
         }
 
         GlideString[] charMembers = new GlideString[] {gs("a"), gs("b"), gs("c"), gs("d"), gs("e")};
@@ -14396,7 +14396,7 @@ public void zscan_binary(BaseClient client) {
                         .zscan(
                                 key1,
                                 initialCursor,
-                                ZScanOptionsBinary.builder().matchPattern(gs("1*")).count(20L).build())
+                                ZScanOptionsBinary.builder().matchPattern(gs("member1*")).count(20L).build())
                         .get();
         assertTrue(Long.parseLong(result[resultCursorIndex].toString()) >= 0);
         assertTrue(ArrayUtils.getLength(result[resultCollectionIndex]) >= 0);
@@ -14409,7 +14409,6 @@ public void zscan_binary(BaseClient client) {
             assertTrue(Long.parseLong(result[resultCursorIndex].toString()) >= 0);
             // Cast the result collection to a String array
             Object[] fieldsArray = (Object[]) result[resultCollectionIndex];
-            System.out.println(Arrays.toString(fieldsArray));
             // Convert Object array to Stream for processing
             Stream stream = Arrays.stream(fieldsArray);
 

From d66f394f4cfea844c5cd2ff3f0967211cde60051 Mon Sep 17 00:00:00 2001
From: Shoham Elias 
Date: Thu, 5 Sep 2024 13:51:17 +0000
Subject: [PATCH 3/3] pr comments

Signed-off-by: Shoham Elias 
---
 .../glide/api/commands/HashBaseCommands.java  | 12 ++++--
 .../api/commands/SortedSetBaseCommands.java   | 12 ++++--
 .../glide/api/models/BaseTransaction.java     | 15 ++++---
 .../models/commands/scan/HScanOptions.java    | 14 ++----
 .../commands/scan/HScanOptionsBinary.java     | 12 +-----
 .../models/commands/scan/ZScanOptions.java    | 14 ++----
 .../commands/scan/ZScanOptionsBinary.java     | 12 +-----
 node/npm/glide/index.ts                       |  4 ++
 node/src/BaseClient.ts                        | 43 ++++++++++++++-----
 node/src/Transaction.ts                       |  9 ++--
 python/python/glide/async_commands/core.py    |  4 +-
 .../glide/async_commands/transaction.py       |  4 +-
 12 files changed, 80 insertions(+), 75 deletions(-)

diff --git a/java/client/src/main/java/glide/api/commands/HashBaseCommands.java b/java/client/src/main/java/glide/api/commands/HashBaseCommands.java
index 58a9814559..e732833223 100644
--- a/java/client/src/main/java/glide/api/commands/HashBaseCommands.java
+++ b/java/client/src/main/java/glide/api/commands/HashBaseCommands.java
@@ -3,7 +3,9 @@
 
 import glide.api.models.GlideString;
 import glide.api.models.commands.scan.HScanOptions;
+import glide.api.models.commands.scan.HScanOptions.HScanOptionsBuilder;
 import glide.api.models.commands.scan.HScanOptionsBinary;
+import glide.api.models.commands.scan.HScanOptionsBinary.HScanOptionsBinaryBuilder;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 
@@ -695,8 +697,9 @@ public interface HashBaseCommands {
      *     cursor for the next iteration of results. "0" will be the cursor
      *      returned on the last iteration of the result. The second element is always an
      *     Array of the subset of the hash held in key. The array in the
-     *     second element is always a flattened series of String pairs, where the key is
-     *     at even indices and the value is at odd indices. If options.noValues is set to true
+     *     second element is a flattened series of String pairs, where the key is at even
+     *     indices and the value is at odd indices. If {@link HScanOptionsBuilder#noValues} is set to
+     *     true
      *     , the second element will only contain the fields without the values.
      * @example
      *     
{@code
@@ -732,8 +735,9 @@ public interface HashBaseCommands {
      *     cursor for the next iteration of results. "0" will be the cursor
      *      returned on the last iteration of the result. The second element is always an
      *     Array of the subset of the hash held in key. The array in the
-     *     second element is always a flattened series of String pairs, where the key is
-     *     at even indices and the value is at odd indices. If options.noValues is set to true
+     *     second element is a flattened series of String pairs, where the key is at even
+     *     indices and the value is at odd indices. If {@link HScanOptionsBinaryBuilder#noValues} is
+     *     set to true
      *     , the second element will only contain the fields without the values.
      * @example
      *     
{@code
diff --git a/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java b/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java
index 2238b2c627..814a41cc99 100644
--- a/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java
+++ b/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java
@@ -22,7 +22,9 @@
 import glide.api.models.commands.WeightAggregateOptions.WeightedKeys;
 import glide.api.models.commands.ZAddOptions;
 import glide.api.models.commands.scan.ZScanOptions;
+import glide.api.models.commands.scan.ZScanOptions.ZScanOptionsBuilder;
 import glide.api.models.commands.scan.ZScanOptionsBinary;
+import glide.api.models.commands.scan.ZScanOptionsBinary.ZScanOptionsBinaryBuilder;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 
@@ -2789,8 +2791,9 @@ CompletableFuture> zinterWithScores(
      *      returned on the last iteration of the sorted set. The second element is always an
      *     
      *     Array of the subset of the sorted set held in key. The array in the
-     *     second element is always a flattened series of String pairs, where the value
-     *     is at even indices and the score is at odd indices. If options.noScores is to true
+     *     second element is a flattened series of String pairs, where the value is at
+     *     even indices and the score is at odd indices. If {@link ZScanOptionsBuilder#noScores} is to
+     *     true
      *     , the second element will only contain the members without scores.
      * @example
      *     
{@code
@@ -2827,8 +2830,9 @@ CompletableFuture> zinterWithScores(
      *      returned on the last iteration of the sorted set. The second element is always an
      *     
      *     Array of the subset of the sorted set held in key. The array in the
-     *     second element is always a flattened series of String pairs, where the value
-     *     is at even indices and the score is at odd indices. If options.noScores is to true
+     *     second element is a flattened series of String pairs, where the value is at
+     *     even indices and the score is at odd indices. If {@link ZScanOptionsBinaryBuilder#noScores}
+     *     is to true
      *     , the second element will only contain the members without scores.
      * @example
      *     
{@code
diff --git a/java/client/src/main/java/glide/api/models/BaseTransaction.java b/java/client/src/main/java/glide/api/models/BaseTransaction.java
index 6ccab2c274..ecbb9fac70 100644
--- a/java/client/src/main/java/glide/api/models/BaseTransaction.java
+++ b/java/client/src/main/java/glide/api/models/BaseTransaction.java
@@ -282,8 +282,10 @@
 import glide.api.models.commands.geospatial.GeoUnit;
 import glide.api.models.commands.geospatial.GeospatialData;
 import glide.api.models.commands.scan.HScanOptions;
+import glide.api.models.commands.scan.HScanOptions.HScanOptionsBuilder;
 import glide.api.models.commands.scan.SScanOptions;
 import glide.api.models.commands.scan.ZScanOptions;
+import glide.api.models.commands.scan.ZScanOptions.ZScanOptionsBuilder;
 import glide.api.models.commands.stream.StreamAddOptions;
 import glide.api.models.commands.stream.StreamAddOptions.StreamAddOptionsBuilder;
 import glide.api.models.commands.stream.StreamClaimOptions;
@@ -7054,10 +7056,10 @@ public  T zscan(@NonNull ArgType key, @NonNull ArgType cursor) {
      *     always the cursor for the next iteration of results. "0" will be
      *     the cursor returned on the last iteration of the sorted set. The second
      *     element is always an Array of the subset of the sorted set held in key
-     *     . The array in the second element is always a flattened series of String
+     *     . The array in the second element is a flattened series of String
      *      pairs, where the value is at even indices and the score is at odd indices. If
-     *     options.noScores is to true, the second element will only contain the members
-     *     without scores.
+     *     {@link ZScanOptionsBuilder#noScores} is to true, the second element will only
+     *     contain the members without scores.
      */
     public  T zscan(
             @NonNull ArgType key, @NonNull ArgType cursor, @NonNull ZScanOptions zScanOptions) {
@@ -7103,9 +7105,10 @@ public  T hscan(@NonNull ArgType key, @NonNull ArgType cursor) {
      *     always the cursor for the next iteration of results. "0" will be
      *     the cursor returned on the last iteration of the result. The second element is
      *     always an Array of the subset of the hash held in key. The array
-     *     in the second element is always a flattened series of String pairs, where the
-     *     key is at even indices and the value is at odd indices. If options.noValues is set to
-     *     true, the second element will only contain the fields without the values.
+     *     in the second element is a flattened series of String pairs, where the key is
+     *     at even indices and the value is at odd indices. If {@link HScanOptionsBuilder#noValues} is
+     *     set to true, the second element will only contain the fields without the
+     *     values.
      */
     public  T hscan(
             @NonNull ArgType key, @NonNull ArgType cursor, @NonNull HScanOptions hScanOptions) {
diff --git a/java/client/src/main/java/glide/api/models/commands/scan/HScanOptions.java b/java/client/src/main/java/glide/api/models/commands/scan/HScanOptions.java
index 38ac80136a..b1249748fc 100644
--- a/java/client/src/main/java/glide/api/models/commands/scan/HScanOptions.java
+++ b/java/client/src/main/java/glide/api/models/commands/scan/HScanOptions.java
@@ -5,8 +5,8 @@
 import glide.api.models.GlideString;
 import glide.utils.ArgsBuilder;
 import java.util.Arrays;
-import java.util.Objects;
 import lombok.Builder;
+import lombok.EqualsAndHashCode;
 import lombok.experimental.SuperBuilder;
 
 /**
@@ -15,6 +15,7 @@
  * @see valkey.io
  */
 @SuperBuilder
+@EqualsAndHashCode(callSuper = false)
 public class HScanOptions extends BaseScanOptions {
 
     /** Option string to include in the HSCAN command when values are not included. */
@@ -22,7 +23,7 @@ public class HScanOptions extends BaseScanOptions {
 
     /**
      * When set to true, the command will not include values in the results. This option is available
-     * from Redis version 8.0.0 and above.
+     * from Valkey version 8.0.0 and above.
      */
     @Builder.Default protected boolean noValues = false;
 
@@ -53,13 +54,4 @@ public GlideString[] toGlideStringArgs() {
 
         return builder.toArray();
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof HScanOptions)) return false;
-        if (!super.equals(o)) return false;
-        HScanOptions that = (HScanOptions) o;
-        return Objects.equals(noValues, that.noValues);
-    }
 }
diff --git a/java/client/src/main/java/glide/api/models/commands/scan/HScanOptionsBinary.java b/java/client/src/main/java/glide/api/models/commands/scan/HScanOptionsBinary.java
index 5bd9b4b7d2..3c42a17be9 100644
--- a/java/client/src/main/java/glide/api/models/commands/scan/HScanOptionsBinary.java
+++ b/java/client/src/main/java/glide/api/models/commands/scan/HScanOptionsBinary.java
@@ -7,7 +7,6 @@
 import glide.api.models.GlideString;
 import glide.utils.ArgsBuilder;
 import java.util.Arrays;
-import java.util.Objects;
 import lombok.Builder;
 import lombok.experimental.SuperBuilder;
 
@@ -24,7 +23,7 @@ public class HScanOptionsBinary extends BaseScanOptionsBinary {
 
     /**
      * When set to true, the command will not include values in the results. This option is available
-     * from Redis version 8.0.0 and above.
+     * from Valkey version 8.0.0 and above.
      */
     @Builder.Default protected boolean noValues = false;
 
@@ -50,13 +49,4 @@ public String[] toArgs() {
 
         return Arrays.stream(builder.toArray()).map(GlideString::getString).toArray(String[]::new);
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof ZScanOptionsBinary)) return false;
-        if (!super.equals(o)) return false;
-        HScanOptionsBinary that = (HScanOptionsBinary) o;
-        return Objects.equals(noValues, that.noValues);
-    }
 }
diff --git a/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptions.java b/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptions.java
index e50838808f..20d63e4530 100644
--- a/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptions.java
+++ b/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptions.java
@@ -5,8 +5,8 @@
 import glide.api.models.GlideString;
 import glide.utils.ArgsBuilder;
 import java.util.Arrays;
-import java.util.Objects;
 import lombok.Builder;
+import lombok.EqualsAndHashCode;
 import lombok.experimental.SuperBuilder;
 
 /**
@@ -15,6 +15,7 @@
  * @see valkey.io
  */
 @SuperBuilder
+@EqualsAndHashCode(callSuper = false)
 public class ZScanOptions extends BaseScanOptions {
 
     /** Option string to include in the ZSCAN command when scores are not included. */
@@ -22,7 +23,7 @@ public class ZScanOptions extends BaseScanOptions {
 
     /**
      * When set to true, the command will not include scores in the results. This option is available
-     * from Redis version 8.0.0 and above.
+     * from Valkey version 8.0.0 and above.
      */
     @Builder.Default protected boolean noScores = false;
 
@@ -53,13 +54,4 @@ public GlideString[] toGlideStringArgs() {
 
         return builder.toArray();
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof ZScanOptions)) return false;
-        if (!super.equals(o)) return false;
-        ZScanOptions that = (ZScanOptions) o;
-        return Objects.equals(noScores, that.noScores);
-    }
 }
diff --git a/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptionsBinary.java b/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptionsBinary.java
index 75020bc375..d93a0b1e06 100644
--- a/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptionsBinary.java
+++ b/java/client/src/main/java/glide/api/models/commands/scan/ZScanOptionsBinary.java
@@ -7,8 +7,8 @@
 import glide.api.models.GlideString;
 import glide.utils.ArgsBuilder;
 import java.util.Arrays;
-import java.util.Objects;
 import lombok.Builder;
+import lombok.EqualsAndHashCode;
 import lombok.experimental.SuperBuilder;
 
 /**
@@ -18,6 +18,7 @@
  * @see valkey.io
  */
 @SuperBuilder
+@EqualsAndHashCode(callSuper = false)
 public class ZScanOptionsBinary extends BaseScanOptionsBinary {
     /** Option string to include in the ZSCAN command when scores are not included. */
     public static final GlideString NO_SCORES_API = gs("NOSCORES");
@@ -50,13 +51,4 @@ public String[] toArgs() {
 
         return Arrays.stream(builder.toArray()).map(GlideString::getString).toArray(String[]::new);
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof ZScanOptionsBinary)) return false;
-        if (!super.equals(o)) return false;
-        ZScanOptionsBinary that = (ZScanOptionsBinary) o;
-        return Objects.equals(noScores, that.noScores);
-    }
 }
diff --git a/node/npm/glide/index.ts b/node/npm/glide/index.ts
index 65840cf7a8..70458dfc8f 100644
--- a/node/npm/glide/index.ts
+++ b/node/npm/glide/index.ts
@@ -77,6 +77,8 @@ function initialize() {
     const {
         AggregationType,
         BaseScanOptions,
+        ZScanOptions,
+        HScanOptions,
         BitEncoding,
         BitFieldGet,
         BitFieldIncrBy,
@@ -185,6 +187,8 @@ function initialize() {
     module.exports = {
         AggregationType,
         BaseScanOptions,
+        HScanOptions,
+        ZScanOptions,
         BitEncoding,
         BitFieldGet,
         BitFieldIncrBy,
diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts
index 019d2db556..8b394586a4 100644
--- a/node/src/BaseClient.ts
+++ b/node/src/BaseClient.ts
@@ -56,6 +56,7 @@ import {
     StreamTrimOptions,
     TimeUnit,
     ZAddOptions,
+    ZScanOptions,
     convertElementsAndScores,
     createAppend,
     createBLMPop,
@@ -2054,9 +2055,9 @@ export class BaseClient {
      * @returns An array of the `cursor` and the subset of the hash held by `key`.
      * The first element is always the `cursor` for the next iteration of results. `"0"` will be the `cursor`
      * returned on the last iteration of the hash. The second element is always an array of the subset of the
-     * hash held in `key`. The array in the second element is always a flattened series of string pairs,
+     * hash held in `key`. The array in the second element is a flattened series of string pairs,
      * where the value is at even indices and the value is at odd indices.
-     * If options.noValues is set to `true`, the second element will only contain the fields without the values.
+     * If `options.noValues` is set to `true`, the second element will only contain the fields without the values.
      *
      * @example
      * ```typescript
@@ -6471,21 +6472,18 @@ export class BaseClient {
      * @returns An `Array` of the `cursor` and the subset of the sorted set held by `key`.
      *      The first element is always the `cursor` for the next iteration of results. `0` will be the `cursor`
      *      returned on the last iteration of the sorted set. The second element is always an `Array` of the subset
-     *      of the sorted set held in `key`. The `Array` in the second element is always a flattened series of
+     *      of the sorted set held in `key`. The `Array` in the second element is a flattened series of
      *      `string` pairs, where the value is at even indices and the score is at odd indices.
-     *      If options.noScores is to `true`, the second element will only contain the members without scores.
+     *      If `options.noScores` is to `true`, the second element will only contain the members without scores.
      *
      * @example
      * ```typescript
-     * // Assume "key" contains a sorted set with multiple members
-     * let newCursor = "0";
-     * let result = [];
-     *
+     * // Assume "key1" contains a sorted set with multiple members
+     * let cursor = "0";
      * do {
      *      const result = await client.zscan(key1, cursor, {
      *          match: "*",
      *          count: 5,
-     *          noScores: true,
      *      });
      *      cursor = result[0];
      *      console.log("Cursor: ", cursor);
@@ -6493,11 +6491,36 @@ export class BaseClient {
      * } while (cursor !== "0");
      * // The output of the code above is something similar to:
      * // Cursor:  123
+     * // Members:  ['value 163', '163', 'value 114', '114', 'value 25', '25', 'value 82', '82', 'value 64', '64']
+     * // Cursor:  47
+     * // Members:  ['value 39', '39', 'value 127', '127', 'value 43', '43', 'value 139', '139', 'value 211', '211']
+     * // Cursor:  0
+     * // Members:  ['value 55', '55', 'value 24', '24', 'value 90', '90', 'value 113', '113']
+     * ```
+     *
+     * @example
+     * ```typescript
+     * // Zscan with no scores
+     * let newCursor = "0";
+     * let result = [];
+     *
+     * do {
+     *      result = await client.zscan(key1, newCursor, {
+     *          match: "*",
+     *          count: 5,
+     *          noScores: true,
+     *      });
+     *      newCursor = result[0];
+     *      console.log("Cursor: ", newCursor);
+     *      console.log("Members: ", result[1]);
+     * } while (newCursor !== "0");
+     * // The output of the code above is something similar to:
+     * // Cursor:  123
      * // Members:  ['value 163', 'value 114', 'value 25', 'value 82', 'value 64']
      * // Cursor:  47
      * // Members:  ['value 39', 'value 127', 'value 43', 'value 139', 'value 211']
      * // Cursor:  0
-     * // Members:  ['value 55', '55', 'value 24', '24', 'value 90', '90', 'value 113', '113']
+     * // Members:  ['value 55', 'value 24' 'value 90', 'value 113']
      * ```
      */
     public async zscan(
diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts
index 2f02f65b71..87d86f8db3 100644
--- a/node/src/Transaction.ts
+++ b/node/src/Transaction.ts
@@ -68,6 +68,7 @@ import {
     StreamTrimOptions,
     TimeUnit,
     ZAddOptions,
+    ZScanOptions,
     convertElementsAndScores,
     createAppend,
     createBLMPop,
@@ -1002,9 +1003,9 @@ export class BaseTransaction> {
      * Command Response - An array of the `cursor` and the subset of the hash held by `key`.
      * The first element is always the `cursor` for the next iteration of results. `"0"` will be the `cursor`
      * returned on the last iteration of the hash. The second element is always an array of the subset of the
-     * hash held in `key`. The array in the second element is always a flattened series of string pairs,
+     * hash held in `key`. The array in the second element is a flattened series of string pairs,
      * where the value is at even indices and the value is at odd indices.
-     * If options.noValues is set to `true`, the second element will only contain the fields without the values.
+     * If `options.noValues` is set to `true`, the second element will only contain the fields without the values.
      */
     public hscan(key: string, cursor: string, options?: HScanOptions): T {
         return this.addAndReturn(createHScan(key, cursor, options));
@@ -3682,9 +3683,9 @@ export class BaseTransaction> {
      * Command Response - An `Array` of the `cursor` and the subset of the sorted set held by `key`.
      *      The first element is always the `cursor` for the next iteration of results. `0` will be the `cursor`
      *      returned on the last iteration of the sorted set. The second element is always an `Array` of the subset
-     *      of the sorted set held in `key`. The `Array` in the second element is always a flattened series of
+     *      of the sorted set held in `key`. The `Array` in the second element is a flattened series of
      *      `String` pairs, where the value is at even indices and the score is at odd indices.
-     *      If options.noScores is to `true`, the second element will only contain the members without scores.
+     *      If `options.noScores` is to `true`, the second element will only contain the members without scores.
      */
     public zscan(key: GlideString, cursor: string, options?: ZScanOptions): T {
         return this.addAndReturn(createZScan(key, cursor, options));
diff --git a/python/python/glide/async_commands/core.py b/python/python/glide/async_commands/core.py
index 87c2c9b593..ba8fd51f74 100644
--- a/python/python/glide/async_commands/core.py
+++ b/python/python/glide/async_commands/core.py
@@ -6142,7 +6142,7 @@ async def zscan(
             List[Union[bytes, List[bytes]]]: An `Array` of the `cursor` and the subset of the sorted set held by `key`.
             The first element is always the `cursor` for the next iteration of results. `0` will be the `cursor`
             returned on the last iteration of the sorted set. The second element is always an `Array` of the subset
-            of the sorted set held in `key`. The `Array` in the second element is always a flattened series of
+            of the sorted set held in `key`. The `Array` in the second element is a flattened series of
             `String` pairs, where the value is at even indices and the score is at odd indices.
             If `no_scores` is set to`True`, the second element will only contain the members without scores.
 
@@ -6225,7 +6225,7 @@ async def hscan(
             List[Union[bytes, List[bytes]]]: An `Array` of the `cursor` and the subset of the hash held by `key`.
                 The first element is always the `cursor` for the next iteration of results. `0` will be the `cursor`
                 returned on the last iteration of the hash. The second element is always an `Array` of the subset of the
-                hash held in `key`. The `Array` in the second element is always a flattened series of `String` pairs,
+                hash held in `key`. The `Array` in the second element is a flattened series of `String` pairs,
                 where the value is at even indices and the score is at odd indices.
                 If `no_values` is set to `True`, the second element will only contain the fields without the values.
 
diff --git a/python/python/glide/async_commands/transaction.py b/python/python/glide/async_commands/transaction.py
index 3906c17126..8201c482bc 100644
--- a/python/python/glide/async_commands/transaction.py
+++ b/python/python/glide/async_commands/transaction.py
@@ -4488,7 +4488,7 @@ def zscan(
             List[Union[bytes, List[bytes]]]: An `Array` of the `cursor` and the subset of the sorted set held by `key`.
             The first element is always the `cursor` for the next iteration of results. `0` will be the `cursor`
             returned on the last iteration of the sorted set. The second element is always an `Array` of the subset
-            of the sorted set held in `key`. The `Array` in the second element is always a flattened series of
+            of the sorted set held in `key`. The `Array` in the second element is a flattened series of
             `String` pairs, where the value is at even indices and the score is at odd indices.
             If `no_scores` is set to`True`, the second element will only contain the members without scores.
         """
@@ -4533,7 +4533,7 @@ def hscan(
             List[Union[bytes, List[bytes]]]: An `Array` of the `cursor` and the subset of the hash held by `key`.
                 The first element is always the `cursor` for the next iteration of results. `0` will be the `cursor`
                 returned on the last iteration of the hash. The second element is always an `Array` of the subset of the
-                hash held in `key`. The `Array` in the second element is always a flattened series of `String` pairs,
+                hash held in `key`. The `Array` in the second element is a flattened series of `String` pairs,
                 where the value is at even indices and the score is at odd indices.
                 If `no_values` is set to `True`, the second element will only contain the fields without the values.
         """