Skip to content

Commit 455048a

Browse files
authored
Merge pull request #1895 from marklogic/feature/MLE-26339-implement-vec-precision-trunc
MLE-26339 Implement vec.trunc and vec.precision in Java Client API
2 parents 8f7117d + 92943f6 commit 455048a

File tree

3 files changed

+152
-3
lines changed

3 files changed

+152
-3
lines changed

marklogic-client-api/src/main/java/com/marklogic/client/expression/VecExpr.java

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
2+
* Copyright (c) 2010-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
33
*/
44

55
package com.marklogic.client.expression;
@@ -151,6 +151,26 @@ public interface VecExpr {
151151
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
152152
*/
153153
public ServerExpression normalize(ServerExpression vector1);
154+
/**
155+
* Returns a new vector which is a copy of the input vector with reduced precision. The precision reduction is achieved by clearing the bottom (32 - precision) bits of the mantissa for each dimension's float value. This can be useful for reducing storage requirements or for creating approximate vector representations.
156+
*
157+
* <a name="ml-server-type-precision"></a>
158+
159+
* <p>
160+
* Provides a client interface to the <a href="http://docs.marklogic.com/vec:precision" target="mlserverdoc">vec:precision</a> server function.
161+
* @param vector The input vector to reduce precision. Can be a vector or an empty sequence. (of <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a>)
162+
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
163+
*/
164+
public ServerExpression precision(ServerExpression vector);
165+
/**
166+
* Returns a new vector which is a copy of the input vector with reduced precision. The precision reduction is achieved by clearing the bottom (32 - precision) bits of the mantissa for each dimension's float value. This can be useful for reducing storage requirements or for creating approximate vector representations.
167+
* <p>
168+
* Provides a client interface to the <a href="http://docs.marklogic.com/vec:precision" target="mlserverdoc">vec:precision</a> server function.
169+
* @param vector The input vector to reduce precision. Can be a vector or an empty sequence. (of <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a>)
170+
* @param precision The number of mantissa bits to preserve (9-32 inclusive). Default is 16. Higher values preserve more precision. If the value is outside the valid range, throw VEC-INVALIDPRECISION. (of <a href="{@docRoot}/doc-files/types/xs_unsignedInt.html">xs:unsignedInt</a>)
171+
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
172+
*/
173+
public ServerExpression precision(ServerExpression vector, ServerExpression precision);
154174
/**
155175
* Returns the difference of two vectors. The vectors must be of the same dimension.
156176
*
@@ -185,6 +205,35 @@ public interface VecExpr {
185205
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
186206
*/
187207
public ServerExpression subvector(ServerExpression vector, ServerExpression start, ServerExpression length);
208+
/**
209+
* Returns a new vector which is a copy of the input vector with each element truncated to a specific number of digits.
210+
*
211+
* <a name="ml-server-type-trunc"></a>
212+
213+
* <p>
214+
* Provides a client interface to the <a href="http://docs.marklogic.com/vec:trunc" target="mlserverdoc">vec:trunc</a> server function.
215+
* @param vector The input vector to truncate. (of <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a>)
216+
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
217+
*/
218+
public ServerExpression trunc(ServerExpression vector);
219+
/**
220+
* Returns a new vector which is a copy of the input vector with each element truncated to a specific number of digits.
221+
* <p>
222+
* Provides a client interface to the <a href="http://docs.marklogic.com/vec:trunc" target="mlserverdoc">vec:trunc</a> server function.
223+
* @param vector The input vector to truncate. (of <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a>)
224+
* @param n The numbers of decimal places to truncate to. The default is 0. Negative values cause that many digits to the left of the decimal point to be truncated. (of <a href="{@docRoot}/doc-files/types/xs_int.html">xs:int</a>)
225+
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
226+
*/
227+
public ServerExpression trunc(ServerExpression vector, int n);
228+
/**
229+
* Returns a new vector which is a copy of the input vector with each element truncated to a specific number of digits.
230+
* <p>
231+
* Provides a client interface to the <a href="http://docs.marklogic.com/vec:trunc" target="mlserverdoc">vec:trunc</a> server function.
232+
* @param vector The input vector to truncate. (of <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a>)
233+
* @param n The numbers of decimal places to truncate to. The default is 0. Negative values cause that many digits to the left of the decimal point to be truncated. (of <a href="{@docRoot}/doc-files/types/xs_int.html">xs:int</a>)
234+
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
235+
*/
236+
public ServerExpression trunc(ServerExpression vector, ServerExpression n);
188237
/**
189238
* Returns a vector value.
190239
*

marklogic-client-api/src/main/java/com/marklogic/client/impl/VecExprImpl.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
2+
* Copyright (c) 2010-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
33
*/
44

55
package com.marklogic.client.impl;
@@ -93,6 +93,18 @@ public ServerExpression normalize(ServerExpression vector1) {
9393
}
9494

9595

96+
@Override
97+
public ServerExpression precision(ServerExpression vector) {
98+
return new VectorCallImpl("vec", "precision", new Object[]{ vector });
99+
}
100+
101+
102+
@Override
103+
public ServerExpression precision(ServerExpression vector, ServerExpression precision) {
104+
return new VectorCallImpl("vec", "precision", new Object[]{ vector, precision });
105+
}
106+
107+
96108
@Override
97109
public ServerExpression subtract(ServerExpression vector1, ServerExpression vector2) {
98110
return new VectorCallImpl("vec", "subtract", new Object[]{ vector1, vector2 });
@@ -111,6 +123,24 @@ public ServerExpression subvector(ServerExpression vector, ServerExpression star
111123
}
112124

113125

126+
@Override
127+
public ServerExpression trunc(ServerExpression vector) {
128+
return new VectorCallImpl("vec", "trunc", new Object[]{ vector });
129+
}
130+
131+
132+
@Override
133+
public ServerExpression trunc(ServerExpression vector, int n) {
134+
return trunc(vector, xs.intVal(n));
135+
}
136+
137+
138+
@Override
139+
public ServerExpression trunc(ServerExpression vector, ServerExpression n) {
140+
return new VectorCallImpl("vec", "trunc", new Object[]{ vector, n });
141+
}
142+
143+
114144
@Override
115145
public ServerExpression vector(ServerExpression values) {
116146
return new VectorCallImpl("vec", "vector", new Object[]{ values });

marklogic-client-api/src/test/java/com/marklogic/client/test/rows/VectorTest.java

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
2+
* Copyright (c) 2010-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
33
*/
44
package com.marklogic.client.test.rows;
55

@@ -57,6 +57,8 @@ void vectorFunctionsHappyPath() {
5757
.bind(op.as("base64Encode", op.vec.base64Encode(op.col("sampleVector"))))
5858
.bind(op.as("base64Decode", op.vec.base64Decode(op.col("base64Encode"))))
5959
.bind(op.as("subVector", op.vec.subvector(op.col("sampleVector"), op.xs.integer(1), op.xs.integer(1))))
60+
.bind(op.as("precision", op.vec.precision(op.col("sampleVector"), op.xs.unsignedInt(16))))
61+
.bind(op.as("trunc", op.vec.trunc(op.col("sampleVector"), 1)))
6062
.bind(op.as("vectorScore", op.vec.vectorScore(op.xs.unsignedInt(1), op.xs.doubleVal(0.5))))
6163
.bind(op.as("simpleVectorScore", op.vec.vectorScore(op.xs.unsignedInt(1), 0.5, 1)))
6264
.bind(op.as("simplestVectorScore", op.vec.vectorScore(op.xs.unsignedInt(1), 0.5)));
@@ -82,6 +84,8 @@ void vectorFunctionsHappyPath() {
8284
assertEquals(3, ((ArrayNode) row.get("base64Decode")).size());
8385
assertEquals(5.6, row.getDouble("get"));
8486
assertEquals(1, ((ArrayNode) row.get("subVector")).size());
87+
assertEquals(3, ((ArrayNode) row.get("precision")).size());
88+
assertEquals(3, ((ArrayNode) row.get("trunc")).size());
8589
assertEquals(333333.0, row.getDouble("vectorScore"));
8690
assertEquals(666666.0, row.getDouble("simpleVectorScore"));
8791
assertEquals(333333.0, row.getDouble("simplestVectorScore"));
@@ -184,4 +188,70 @@ void dslAnnTopK() {
184188
List<RowRecord> rows = resultRows(plan);
185189
assertEquals(2, rows.size(), "Just verifying that 'annTopK' works via the DSL and v1/rows.");
186190
}
191+
192+
@Test
193+
void precision() {
194+
// Test vec.precision with default precision (16 bits)
195+
PlanBuilder.ModifyPlan plan = op.fromView("vectors", "persons")
196+
.limit(1)
197+
.bind(op.as("testVector", op.vec.vector(op.xs.doubleSeq(3.14159265, 2.71828182, 1.41421356))))
198+
.bind(op.as("precisionDefault", op.vec.precision(op.col("testVector"))))
199+
.bind(op.as("precision10", op.vec.precision(op.col("testVector"), op.xs.unsignedInt(10))));
200+
201+
List<RowRecord> rows = resultRows(plan);
202+
assertEquals(1, rows.size());
203+
RowRecord row = rows.get(0);
204+
205+
// Verify that precision returns a vector
206+
ArrayNode precisionDefault = (ArrayNode) row.get("precisionDefault");
207+
assertNotNull(precisionDefault);
208+
assertEquals(3, precisionDefault.size());
209+
210+
// Verify precision with 10 bits - should truncate values
211+
ArrayNode precision10 = (ArrayNode) row.get("precision10");
212+
assertNotNull(precision10);
213+
assertEquals(3, precision10.size());
214+
assertEquals(3, precision10.get(0).asInt(), "First element should be truncated to 3");
215+
assertEquals(2, precision10.get(1).asInt(), "Second element should be truncated to 2");
216+
assertEquals(1, precision10.get(2).asInt(), "Third element should be truncated to 1");
217+
}
218+
219+
@Test
220+
void trunc() {
221+
// Test vec.trunc with different decimal places
222+
PlanBuilder.ModifyPlan plan = op.fromView("vectors", "persons")
223+
.limit(1)
224+
.bind(op.as("testVector", op.vec.vector(op.xs.doubleSeq(1.123456789, 2.123456789, 3.123456789))))
225+
.bind(op.as("truncDefault", op.vec.trunc(op.col("testVector"))))
226+
.bind(op.as("trunc1", op.vec.trunc(op.col("testVector"), 1)))
227+
.bind(op.as("trunc2", op.vec.trunc(op.col("testVector"), op.xs.intVal(2))));
228+
229+
List<RowRecord> rows = resultRows(plan);
230+
assertEquals(1, rows.size());
231+
RowRecord row = rows.get(0);
232+
233+
// Verify truncation with default (0 decimal places)
234+
ArrayNode truncDefault = (ArrayNode) row.get("truncDefault");
235+
assertNotNull(truncDefault);
236+
assertEquals(3, truncDefault.size());
237+
assertEquals(1, truncDefault.get(0).asInt(), "First element should be truncated to 1");
238+
assertEquals(2, truncDefault.get(1).asInt(), "Second element should be truncated to 2");
239+
assertEquals(3, truncDefault.get(2).asInt(), "Third element should be truncated to 3");
240+
241+
// Verify truncation with 1 decimal place
242+
ArrayNode trunc1 = (ArrayNode) row.get("trunc1");
243+
assertNotNull(trunc1);
244+
assertEquals(3, trunc1.size());
245+
assertEquals(1.1, trunc1.get(0).asDouble(), 0.001, "First element should be truncated to 1.1");
246+
assertEquals(2.1, trunc1.get(1).asDouble(), 0.001, "Second element should be truncated to 2.1");
247+
assertEquals(3.1, trunc1.get(2).asDouble(), 0.001, "Third element should be truncated to 3.1");
248+
249+
// Verify truncation with 2 decimal places
250+
ArrayNode trunc2 = (ArrayNode) row.get("trunc2");
251+
assertNotNull(trunc2);
252+
assertEquals(3, trunc2.size());
253+
assertEquals(1.12, trunc2.get(0).asDouble(), 0.001, "First element should be truncated to 1.12");
254+
assertEquals(2.12, trunc2.get(1).asDouble(), 0.001, "Second element should be truncated to 2.12");
255+
assertEquals(3.12, trunc2.get(2).asDouble(), 0.001, "Third element should be truncated to 3.12");
256+
}
187257
}

0 commit comments

Comments
 (0)