vsimWithScores(byte[] key, float[] vector, VSimParams params);
+ /**
+ * VSIM Command Return elements
+ * similar to a given vector with their similarity scores and attributes.
+ *
+ * Time complexity: O(log(N)) where N is the number of elements in the vector set.
+ * @param key the name of the key that holds the vector set data
+ * @param vector the vector to use as similarity reference
+ * @param params additional parameters for the VSIM command (WITHSCORES and WITHATTRIBS will be
+ * automatically added)
+ * @return map of element names to their similarity scores and attributes
+ */
+ @Experimental
+ Map vsimWithScoresAndAttribs(byte[] key, float[] vector,
+ VSimParams params);
+
/**
* VSIM Command Return elements
* similar to a given element in the vector set.
@@ -183,6 +199,21 @@ public interface VectorSetBinaryCommands {
@Experimental
Map vsimByElementWithScores(byte[] key, byte[] element, VSimParams params);
+ /**
+ * VSIM Command Return elements
+ * similar to a given element in the vector set with their similarity scores and attributes.
+ *
+ * Time complexity: O(log(N)) where N is the number of elements in the vector set.
+ * @param key the name of the key that holds the vector set data
+ * @param element the name of the element to use as similarity reference
+ * @param params additional parameters for the VSIM command (WITHSCORES and WITHATTRIBS will be
+ * automatically added)
+ * @return map of element names to their similarity scores and attributes
+ */
+ @Experimental
+ Map vsimByElementWithScoresAndAttribs(byte[] key, byte[] element,
+ VSimParams params);
+
/**
* VDIM Command Return the number
* of dimensions of the vectors in the specified vector set.
diff --git a/src/main/java/redis/clients/jedis/commands/VectorSetCommands.java b/src/main/java/redis/clients/jedis/commands/VectorSetCommands.java
index 965e86b991..b2cbb96f50 100644
--- a/src/main/java/redis/clients/jedis/commands/VectorSetCommands.java
+++ b/src/main/java/redis/clients/jedis/commands/VectorSetCommands.java
@@ -7,6 +7,7 @@
import redis.clients.jedis.params.VAddParams;
import redis.clients.jedis.params.VSimParams;
import redis.clients.jedis.resps.RawVector;
+import redis.clients.jedis.resps.VSimScoreAttribs;
import redis.clients.jedis.resps.VectorInfo;
/**
@@ -145,6 +146,21 @@ public interface VectorSetCommands {
@Experimental
Map vsimWithScores(String key, float[] vector, VSimParams params);
+ /**
+ * VSIM Command Return elements
+ * similar to a given vector with their similarity scores and attributes.
+ *
+ * Time complexity: O(log(N)) where N is the number of elements in the vector set.
+ * @param key the name of the key that holds the vector set data
+ * @param vector the vector to use as similarity reference
+ * @param params additional parameters for the VSIM command (WITHSCORES and WITHATTRIBS will be
+ * automatically added)
+ * @return map of element names to their similarity scores and attributes
+ */
+ @Experimental
+ Map vsimWithScoresAndAttribs(String key, float[] vector,
+ VSimParams params);
+
/**
* VSIM Command Return elements
* similar to a given element in the vector set.
@@ -184,6 +200,21 @@ public interface VectorSetCommands {
@Experimental
Map vsimByElementWithScores(String key, String element, VSimParams params);
+ /**
+ * VSIM Command Return elements
+ * similar to a given element in the vector set with their similarity scores and attributes.
+ *
+ * Time complexity: O(log(N)) where N is the number of elements in the vector set.
+ * @param key the name of the key that holds the vector set data
+ * @param element the name of the element to use as similarity reference
+ * @param params additional parameters for the VSIM command (WITHSCORES and WITHATTRIBS will be
+ * automatically added)
+ * @return map of element names to their similarity scores and attributes
+ */
+ @Experimental
+ Map vsimByElementWithScoresAndAttribs(String key, String element,
+ VSimParams params);
+
/**
* VDIM Command Return the number
* of dimensions of the vectors in the specified vector set.
diff --git a/src/main/java/redis/clients/jedis/resps/VSimScoreAttribs.java b/src/main/java/redis/clients/jedis/resps/VSimScoreAttribs.java
new file mode 100644
index 0000000000..4beab9fc17
--- /dev/null
+++ b/src/main/java/redis/clients/jedis/resps/VSimScoreAttribs.java
@@ -0,0 +1,63 @@
+package redis.clients.jedis.resps;
+
+import redis.clients.jedis.annots.Experimental;
+
+/**
+ * Response object containing both similarity score and attributes for VSIM command when used with
+ * WITHSCORES and WITHATTRIBS options.
+ */
+@Experimental
+public class VSimScoreAttribs {
+
+ private final Double score;
+ private final String attributes;
+
+ /**
+ * Creates a new VSimScoreAttribs instance.
+ * @param score the similarity score (0.0 to 1.0)
+ * @param attributes the element attributes as JSON string, or null if no attributes
+ */
+ public VSimScoreAttribs(Double score, String attributes) {
+ this.score = score;
+ this.attributes = attributes;
+ }
+
+ /**
+ * Gets the similarity score.
+ * @return the similarity score between 0.0 and 1.0
+ */
+ public Double getScore() {
+ return score;
+ }
+
+ /**
+ * Gets the element attributes.
+ * @return the attributes as JSON string, or null if no attributes are set
+ */
+ public String getAttributes() {
+ return attributes;
+ }
+
+ @Override
+ public String toString() {
+ return "VSimScoreAttribs{" + "score=" + score + ", attributes='" + attributes + '\'' + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ VSimScoreAttribs that = (VSimScoreAttribs) o;
+
+ if (score != null ? !score.equals(that.score) : that.score != null) return false;
+ return attributes != null ? attributes.equals(that.attributes) : that.attributes == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = score != null ? score.hashCode() : 0;
+ result = 31 * result + (attributes != null ? attributes.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/commands/jedis/VectorSetCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/VectorSetCommandsTest.java
index 038e62bb9c..327a83dbf4 100644
--- a/src/test/java/redis/clients/jedis/commands/jedis/VectorSetCommandsTest.java
+++ b/src/test/java/redis/clients/jedis/commands/jedis/VectorSetCommandsTest.java
@@ -12,6 +12,7 @@
import redis.clients.jedis.params.VAddParams;
import redis.clients.jedis.params.VSimParams;
import redis.clients.jedis.resps.RawVector;
+import redis.clients.jedis.resps.VSimScoreAttribs;
import redis.clients.jedis.resps.VectorInfo;
import redis.clients.jedis.util.SafeEncoder;
@@ -1441,6 +1442,81 @@ public void testVsimWithScores(TestInfo testInfo) {
}
}
+ /**
+ * Test VSIM with scores and attributes.
+ */
+ @Test
+ @SinceRedisVersion("8.2.0")
+ public void testVsimWithScoresAndAttribs(TestInfo testInfo) {
+ String testKey = testInfo.getDisplayName() + ":test:vector:set:scores:attribs";
+
+ // Add test vectors with attributes
+ VAddParams addParams1 = new VAddParams().setAttr("category=test,priority=high");
+ jedis.vadd(testKey, new float[] { 0.1f, 0.2f, 0.3f }, "element1", addParams1);
+
+ VAddParams addParams2 = new VAddParams().setAttr("category=prod,priority=low");
+ jedis.vadd(testKey, new float[] { 0.15f, 0.25f, 0.35f }, "element2", addParams2);
+
+ // Add element without attributes
+ jedis.vadd(testKey, new float[] { 0.9f, 0.8f, 0.7f }, "element3");
+
+ VSimParams params = new VSimParams();
+ Map similarWithScoresAndAttribs = jedis
+ .vsimWithScoresAndAttribs(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params);
+ assertNotNull(similarWithScoresAndAttribs);
+ assertThat(similarWithScoresAndAttribs, is(not(anEmptyMap())));
+
+ // Verify scores and attributes are present
+ for (Map.Entry entry : similarWithScoresAndAttribs.entrySet()) {
+ String element = entry.getKey();
+ VSimScoreAttribs data = entry.getValue();
+
+ assertNotNull(data);
+ assertNotNull(data.getScore());
+ assertThat(data.getScore(), is(both(greaterThanOrEqualTo(0.0)).and(lessThanOrEqualTo(1.0))));
+
+ // Check attributes based on element
+ if ("element1".equals(element)) {
+ assertEquals("category=test,priority=high", data.getAttributes());
+ } else if ("element2".equals(element)) {
+ assertEquals("category=prod,priority=low", data.getAttributes());
+ } else if ("element3".equals(element)) {
+ assertNull(data.getAttributes()); // No attributes set
+ }
+ }
+ }
+
+ /**
+ * Test VSIM by element with scores and attributes.
+ */
+ @Test
+ @SinceRedisVersion("8.2.0")
+ public void testVsimByElementWithScoresAndAttribs(TestInfo testInfo) {
+ String testKey = testInfo.getDisplayName() + ":test:vector:set:element:scores:attribs";
+
+ // Add test vectors with attributes
+ VAddParams addParams1 = new VAddParams().setAttr("type=reference,quality=high");
+ jedis.vadd(testKey, new float[] { 0.1f, 0.2f, 0.3f }, "reference", addParams1);
+
+ VAddParams addParams2 = new VAddParams().setAttr("type=similar,quality=medium");
+ jedis.vadd(testKey, new float[] { 0.12f, 0.22f, 0.32f }, "similar1", addParams2);
+
+ VAddParams addParams3 = new VAddParams().setAttr("type=different,quality=low");
+ jedis.vadd(testKey, new float[] { 0.9f, 0.8f, 0.7f }, "different", addParams3);
+
+ VSimParams params = new VSimParams();
+ Map similarWithScoresAndAttribs = jedis
+ .vsimByElementWithScoresAndAttribs(testKey, "reference", params);
+ assertNotNull(similarWithScoresAndAttribs);
+ assertThat(similarWithScoresAndAttribs, is(not(anEmptyMap())));
+
+ // Reference element should have perfect similarity with itself
+ assertTrue(similarWithScoresAndAttribs.containsKey("reference"));
+ VSimScoreAttribs referenceData = similarWithScoresAndAttribs.get("reference");
+ assertThat(referenceData.getScore(), is(closeTo(1.0, 0.001)));
+ assertEquals("type=reference,quality=high", referenceData.getAttributes());
+ }
+
/**
* Test VSIM command with binary keys and elements. Verifies vector similarity search works with
* byte arrays.
diff --git a/src/test/java/redis/clients/jedis/commands/unified/VectorSetCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/VectorSetCommandsTestBase.java
index ca2a9ef4a2..9b30f01784 100644
--- a/src/test/java/redis/clients/jedis/commands/unified/VectorSetCommandsTestBase.java
+++ b/src/test/java/redis/clients/jedis/commands/unified/VectorSetCommandsTestBase.java
@@ -27,6 +27,7 @@
import redis.clients.jedis.params.VAddParams;
import redis.clients.jedis.params.VSimParams;
import redis.clients.jedis.resps.RawVector;
+import redis.clients.jedis.resps.VSimScoreAttribs;
import redis.clients.jedis.resps.VectorInfo;
import redis.clients.jedis.util.SafeEncoder;
import redis.clients.jedis.util.VectorTestUtils;
@@ -1446,6 +1447,81 @@ public void testVsimWithScores(TestInfo testInfo) {
}
}
+ /**
+ * Test VSIM with scores and attributes.
+ */
+ @Test
+ @SinceRedisVersion("8.2.0")
+ public void testVsimWithScoresAndAttribs(TestInfo testInfo) {
+ String testKey = testInfo.getDisplayName() + ":test:vector:set:scores:attribs";
+
+ // Add test vectors with attributes
+ VAddParams addParams1 = new VAddParams().setAttr("category=test,priority=high");
+ jedis.vadd(testKey, new float[] { 0.1f, 0.2f, 0.3f }, "element1", addParams1);
+
+ VAddParams addParams2 = new VAddParams().setAttr("category=prod,priority=low");
+ jedis.vadd(testKey, new float[] { 0.15f, 0.25f, 0.35f }, "element2", addParams2);
+
+ // Add element without attributes
+ jedis.vadd(testKey, new float[] { 0.9f, 0.8f, 0.7f }, "element3");
+
+ VSimParams params = new VSimParams();
+ Map similarWithScoresAndAttribs = jedis
+ .vsimWithScoresAndAttribs(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params);
+ assertNotNull(similarWithScoresAndAttribs);
+ assertThat(similarWithScoresAndAttribs, is(not(anEmptyMap())));
+
+ // Verify scores and attributes are present
+ for (Map.Entry entry : similarWithScoresAndAttribs.entrySet()) {
+ String element = entry.getKey();
+ VSimScoreAttribs data = entry.getValue();
+
+ assertNotNull(data);
+ assertNotNull(data.getScore());
+ assertThat(data.getScore(), is(both(greaterThanOrEqualTo(0.0)).and(lessThanOrEqualTo(1.0))));
+
+ // Check attributes based on element
+ if ("element1".equals(element)) {
+ assertEquals("category=test,priority=high", data.getAttributes());
+ } else if ("element2".equals(element)) {
+ assertEquals("category=prod,priority=low", data.getAttributes());
+ } else if ("element3".equals(element)) {
+ assertNull(data.getAttributes()); // No attributes set
+ }
+ }
+ }
+
+ /**
+ * Test VSIM by element with scores and attributes.
+ */
+ @Test
+ @SinceRedisVersion("8.2.0")
+ public void testVsimByElementWithScoresAndAttribs(TestInfo testInfo) {
+ String testKey = testInfo.getDisplayName() + ":test:vector:set:element:scores:attribs";
+
+ // Add test vectors with attributes
+ VAddParams addParams1 = new VAddParams().setAttr("type=reference,quality=high");
+ jedis.vadd(testKey, new float[] { 0.1f, 0.2f, 0.3f }, "reference", addParams1);
+
+ VAddParams addParams2 = new VAddParams().setAttr("type=similar,quality=medium");
+ jedis.vadd(testKey, new float[] { 0.12f, 0.22f, 0.32f }, "similar1", addParams2);
+
+ VAddParams addParams3 = new VAddParams().setAttr("type=different,quality=low");
+ jedis.vadd(testKey, new float[] { 0.9f, 0.8f, 0.7f }, "different", addParams3);
+
+ VSimParams params = new VSimParams();
+ Map similarWithScoresAndAttribs = jedis
+ .vsimByElementWithScoresAndAttribs(testKey, "reference", params);
+ assertNotNull(similarWithScoresAndAttribs);
+ assertThat(similarWithScoresAndAttribs, is(not(anEmptyMap())));
+
+ // Reference element should have perfect similarity with itself
+ assertTrue(similarWithScoresAndAttribs.containsKey("reference"));
+ VSimScoreAttribs referenceData = similarWithScoresAndAttribs.get("reference");
+ assertThat(referenceData.getScore(), is(closeTo(1.0, 0.001)));
+ assertEquals("type=reference,quality=high", referenceData.getAttributes());
+ }
+
/**
* Test VSIM command with binary keys and elements. Verifies vector similarity search works with
* byte arrays.
@@ -1514,7 +1590,7 @@ public void testVsimBinaryWithScores(TestInfo testInfo) {
assertThat(similarWithScores, is(not(anEmptyMap())));
// Element1 should have perfect similarity with itself
- Double element1Score = getBinaryScoreForElement(similarWithScores, "element1");
+ Double element1Score = similarWithScores.get("element1".getBytes());
assertNotNull(element1Score);
assertThat(element1Score, is(closeTo(1.0, 0.001)));
@@ -1547,16 +1623,99 @@ private List getBinaryElementNames(List binaryElements) {
}
/**
- * Helper method to get score for a specific element from binary score map.
+ * Test VSIM command with binary keys and scores and attributes. Verifies vector similarity search
+ * returns scores and attributes with binary data.
*/
- private Double getBinaryScoreForElement(Map scoreMap, String elementName) {
- byte[] elementBytes = elementName.getBytes();
- for (Map.Entry entry : scoreMap.entrySet()) {
- if (java.util.Arrays.equals(entry.getKey(), elementBytes)) {
- return entry.getValue();
+ @Test
+ @SinceRedisVersion("8.2.0")
+ public void testVsimBinaryWithScoresAndAttribs(TestInfo testInfo) {
+ byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary:scores:attribs")
+ .getBytes();
+
+ setupVSimTestSetBinaryWithAttribs(testKey);
+
+ // Test vsim with vector, scores and attributes (binary)
+ VSimParams params = new VSimParams();
+ Map similarWithScoresAndAttribs = jedis
+ .vsimWithScoresAndAttribs(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params);
+ assertNotNull(similarWithScoresAndAttribs);
+ assertThat(similarWithScoresAndAttribs, is(not(anEmptyMap())));
+
+ // Verify scores and attributes are present and valid
+ for (Map.Entry entry : similarWithScoresAndAttribs.entrySet()) {
+ byte[] element = entry.getKey();
+ VSimScoreAttribs data = entry.getValue();
+
+ assertNotNull(data);
+ assertNotNull(data.getScore());
+ assertThat(data.getScore(), is(both(greaterThanOrEqualTo(0.0)).and(lessThanOrEqualTo(1.0))));
+
+ // Check attributes based on element
+ String elementName = new String(element);
+ if ("element1".equals(elementName)) {
+ assertEquals("category=test,priority=high", data.getAttributes());
+ } else if ("element2".equals(elementName)) {
+ assertEquals("category=prod,priority=low", data.getAttributes());
+ } else if ("element3".equals(elementName)) {
+ assertNull(data.getAttributes()); // No attributes set
}
}
- return null;
+
+ }
+
+ /**
+ * Test VSIM by element command with binary keys and scores and attributes. Verifies element-based
+ * vector similarity search returns scores and attributes with binary data.
+ */
+ @Test
+ @SinceRedisVersion("8.2.0")
+ public void testVsimByElementBinaryWithScoresAndAttribs(TestInfo testInfo) {
+ byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:element:binary:scores:attribs")
+ .getBytes();
+
+ // Add test vectors with attributes
+ byte[] referenceElement = "reference".getBytes();
+ VAddParams addParams1 = new VAddParams().setAttr("type=reference,quality=high");
+ jedis.vadd(testKey, new float[] { 0.1f, 0.2f, 0.3f }, referenceElement, addParams1);
+
+ byte[] similar1Element = "similar1".getBytes();
+ VAddParams addParams2 = new VAddParams().setAttr("type=similar,quality=medium");
+ jedis.vadd(testKey, new float[] { 0.12f, 0.22f, 0.32f }, "similar1".getBytes(), addParams2);
+
+ byte[] differentElement = "different".getBytes();
+ VAddParams addParams3 = new VAddParams().setAttr("type=different,quality=low");
+ jedis.vadd(testKey, new float[] { 0.9f, 0.8f, 0.7f }, "different".getBytes(), addParams3);
+
+ VSimParams params = new VSimParams();
+ Map similarWithScoresAndAttribs = jedis
+ .vsimByElementWithScoresAndAttribs(testKey, referenceElement, params);
+ assertNotNull(similarWithScoresAndAttribs);
+ assertThat(similarWithScoresAndAttribs, is(not(anEmptyMap())));
+
+ // Reference element should have perfect similarity with itself
+ VSimScoreAttribs referenceData = similarWithScoresAndAttribs.get(referenceElement);
+ assertThat(referenceData.getScore(), is(closeTo(1.0, 0.001)));
+ assertEquals("type=reference,quality=high", referenceData.getAttributes());
+
+ }
+
+ /**
+ * Helper method to set up test vector set for binary VSIM tests with attributes.
+ */
+ private void setupVSimTestSetBinaryWithAttribs(byte[] testKey) {
+ // Add test vectors with attributes - same as non-binary version
+ float[] vector1 = { 0.1f, 0.2f, 0.3f };
+ float[] vector2 = { 0.15f, 0.25f, 0.35f };
+ float[] vector3 = { 0.9f, 0.8f, 0.7f };
+
+ VAddParams addParams1 = new VAddParams().setAttr("category=test,priority=high");
+ jedis.vadd(testKey, vector1, "element1".getBytes(), addParams1);
+
+ VAddParams addParams2 = new VAddParams().setAttr("category=prod,priority=low");
+ jedis.vadd(testKey, vector2, "element2".getBytes(), addParams2);
+
+ // Element without attributes
+ jedis.vadd(testKey, vector3, "element3".getBytes());
}
}