Repository: quarkusio/quarkus
Issue: Redis client filters the null values with the mget command

### Describe the bug

In the redis documentation, here : https://redis.io/commands/mget/

The missing values should be returned as null, it enables to detect in an efficient way, which keys are missing or have been deleted due to timeout policies.

However, the implementation of the ReactiveRedisClient uses the io.quarkus.redis.runtime.datasource.Marshaller which filters this values with the method #decodeAsOrderedMap. 

The null values should not be filtered and the user should do it, if necessary.


### Expected behavior

More compliance with the redis documentation, and non filtered null values.

### Actual behavior

The null values are filtered by default.

### How to Reproduce?

1. Create a quarkus app with version 2.12.2.Final
2. Create a ReactiveRedisDatasource
3. use the mget command to fetch non existing values
4. Recover an empty map

### Output of `uname -a` or `ver`

_No response_

### Output of `java -version`

_No response_

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

2.12.2.Final

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

maven

### Additional information

_No response_

Changed files:
extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/hash/HashCommands.java
extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/value/ValueCommands.java
extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/ValueCommandsTest.java
extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/Marshaller.java
extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/StringCommandsTest.java
extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/ReactiveStringCommands.java
extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/StringCommands.java
extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/HashCommandsTest.java
extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/value/ReactiveValueCommands.java
extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/hash/ReactiveHashCommands.java

diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/hash/HashCommands.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/hash/HashCommands.java
index 1f923856fa3..d71e80ae867 100644
--- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/hash/HashCommands.java
+++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/hash/HashCommands.java
@@ -125,6 +125,7 @@
      * @param key the key
      * @param fields the fields
      * @return list of values associated with the given fields, in the same order as they are requested.
+     *         If a requested field does not exist, the returned map contains a {@code null} value for that field.
      **/
     Map<F, V> hmget(K key, F... fields);
 
diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/hash/ReactiveHashCommands.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/hash/ReactiveHashCommands.java
index ccea4615e14..51647e07464 100644
--- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/hash/ReactiveHashCommands.java
+++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/hash/ReactiveHashCommands.java
@@ -126,6 +126,7 @@
      * @param key the key
      * @param fields the fields
      * @return list of values associated with the given fields, in the same order as they are requested.
+     *         If a requested field does not exist, the returned map contains a {@code null} value for that field.
      **/
     Uni<Map<F, V>> hmget(K key, F... fields);
 
diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/ReactiveStringCommands.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/ReactiveStringCommands.java
index fcbe689fb14..5bac8b361ff 100644
--- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/ReactiveStringCommands.java
+++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/ReactiveStringCommands.java
@@ -183,7 +183,8 @@
      * Requires Redis 1.0.0
      *
      * @param keys the keys
-     * @return list of values at the specified keys.
+     * @return the value of key, or {@code null} when key does not exist. If one of the passed key does not exist, the
+     *         returned map contains a {@code null} value associated with the missing key.
      **/
     Uni<Map<K, V>> mget(K... keys);
 
diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/StringCommands.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/StringCommands.java
index ac992c038ad..b3d592620e3 100644
--- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/StringCommands.java
+++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/string/StringCommands.java
@@ -182,7 +182,8 @@
      * Requires Redis 1.0.0
      *
      * @param keys the keys
-     * @return list of values at the specified keys.
+     * @return list of values at the specified keys. If one of the passed key does not exist, the returned map contains
+     *         a {@code null} value associated with the missing key.
      **/
     Map<K, V> mget(K... keys);
 
diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/value/ReactiveValueCommands.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/value/ReactiveValueCommands.java
index 896d7793a3a..e9ddc167549 100644
--- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/value/ReactiveValueCommands.java
+++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/value/ReactiveValueCommands.java
@@ -63,7 +63,8 @@
      * Requires Redis 1.0.0
      *
      * @param key the key
-     * @return the value of key, or {@code null} when key does not exist.
+     * @return the value of key, or {@code null} when key does not exist. If one of the passed key does not exist, the
+     *         returned map contains a {@code null} value associated with the missing key.
      **/
     Uni<V> get(K key);
 
diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/value/ValueCommands.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/value/ValueCommands.java
index 2f6416d11e8..48ad3e81489 100644
--- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/value/ValueCommands.java
+++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/value/ValueCommands.java
@@ -183,7 +183,8 @@
      * Requires Redis 1.0.0
      *
      * @param keys the keys
-     * @return list of values at the specified keys.
+     * @return list of values at the specified keys. If one of the passed key does not exist, the returned map contains
+     *         a {@code null} value associated with the missing key.
      **/
     Map<K, V> mget(K... keys);
 
diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/Marshaller.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/Marshaller.java
index c6d1186d144..d34850f1085 100644
--- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/Marshaller.java
+++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/Marshaller.java
@@ -149,9 +149,7 @@ final <F, V> Map<F, V> decodeAsOrderedMap(Response response, Class<V> typeOfValu
         Map<F, V> map = new LinkedHashMap<>();
         for (F field : fields) {
             Response v = iterator.next();
-            if (v != null) { // Skip null entries
-                map.put(field, decode(typeOfValue, v));
-            }
+            map.put(field, decode(typeOfValue, v));
         }
         return map;
     }
diff --git a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/HashCommandsTest.java b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/HashCommandsTest.java
index 643952e39ab..a5dd256e798 100644
--- a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/HashCommandsTest.java
+++ b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/HashCommandsTest.java
@@ -142,12 +142,12 @@ void hstrlen() {
     void hmget() {
         populateForHmget();
         Map<String, Person> values = hash.hmget(key, "one", "missing", "two");
-        assertThat(values).hasSize(2);
-        assertThat(values).containsExactly(entry("one", Person.person1), entry("two", Person.person2));
+        assertThat(values).hasSize(3);
+        assertThat(values).containsExactly(entry("one", Person.person1), entry("missing", null), entry("two", Person.person2));
     }
 
     private void populateForHmget() {
-        assertThat(hash.hmget(key, "one", "two").values()).isEmpty();
+        assertThat(hash.hmget(key, "one", "two")).allSatisfy((s, p) -> assertThat(p).isNull());
         hash.hset(key, "one", Person.person1);
         hash.hset(key, "two", Person.person2);
     }
diff --git a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/StringCommandsTest.java b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/StringCommandsTest.java
index bb73b2b134b..3e0e16ca628 100644
--- a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/StringCommandsTest.java
+++ b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/StringCommandsTest.java
@@ -99,15 +99,24 @@ void getset() {
 
     @Test
     void mget() {
-        assertThat(strings.mget(key)).isEmpty();
+        assertThat(strings.mget(key)).containsExactly(entry(key, null));
         strings.set("one", "1");
         strings.set("two", "2");
         assertThat(strings.mget("one", "two")).containsExactly(entry("one", "1"), entry("two", "2"));
     }
 
+    @Test
+    void mgetWithMissingKey() {
+        assertThat(strings.mget(key)).containsExactly(entry(key, null));
+        strings.set("one", "1");
+        strings.set("two", "2");
+        assertThat(strings.mget("one", "missing", "two")).containsExactly(entry("one", "1"),
+                entry("missing", null), entry("two", "2"));
+    }
+
     @Test
     void mset() {
-        assertThat(strings.mget("one", "two")).isEmpty();
+        assertThat(strings.mget(key)).containsExactly(entry(key, null));
         Map<String, String> map = new LinkedHashMap<>();
         map.put("one", "1");
         map.put("two", "2");
diff --git a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/ValueCommandsTest.java b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/ValueCommandsTest.java
index 0bd8815e307..18d63308c18 100644
--- a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/ValueCommandsTest.java
+++ b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/ValueCommandsTest.java
@@ -99,15 +99,24 @@ void getset() {
 
     @Test
     void mget() {
-        assertThat(values.mget(key)).isEmpty();
+        assertThat(values.mget(key)).containsExactly(entry(key, null));
         values.set("one", "1");
         values.set("two", "2");
         assertThat(values.mget("one", "two")).containsExactly(entry("one", "1"), entry("two", "2"));
     }
 
+    @Test
+    void mgetWithMissingKey() {
+        assertThat(values.mget(key)).containsExactly(entry(key, null));
+        values.set("one", "1");
+        values.set("two", "2");
+        assertThat(values.mget("one", "missing", "two")).containsExactly(entry("one", "1"),
+                entry("missing", null), entry("two", "2"));
+    }
+
     @Test
     void mset() {
-        assertThat(values.mget("one", "two")).isEmpty();
+        assertThat(values.mget("one", "two")).containsExactly(entry("one", null), entry("two", null));
         Map<String, String> map = new LinkedHashMap<>();
         map.put("one", "1");
         map.put("two", "2");