Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
* Copyright (c) 2010-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/

package com.marklogic.client.expression;
Expand Down Expand Up @@ -151,6 +151,26 @@ public interface VecExpr {
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
*/
public ServerExpression normalize(ServerExpression vector1);
/**
* 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.
*
* <a name="ml-server-type-precision"></a>

* <p>
* Provides a client interface to the <a href="http://docs.marklogic.com/vec:precision" target="mlserverdoc">vec:precision</a> server function.
Comment on lines +154 to +160
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new Javadocs are hard to read due to very long lines, duplicated text across overloads, and some inconsistent blank-line formatting (e.g., the whitespace-only line after the <a name=...> anchor). Consider wrapping lines, removing/avoiding whitespace-only lines, and factoring shared wording into one place (e.g., keep a shorter overload doc that references the main one) to keep the public API docs consistent and maintainable.

Copilot uses AI. Check for mistakes.
* @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>)
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
*/
public ServerExpression precision(ServerExpression vector);
/**
* 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.
* <p>
* Provides a client interface to the <a href="http://docs.marklogic.com/vec:precision" target="mlserverdoc">vec:precision</a> server function.
* @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>)
* @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>)
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
*/
public ServerExpression precision(ServerExpression vector, ServerExpression precision);
/**
* Returns the difference of two vectors. The vectors must be of the same dimension.
*
Expand Down Expand Up @@ -185,6 +205,35 @@ public interface VecExpr {
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
*/
public ServerExpression subvector(ServerExpression vector, ServerExpression start, ServerExpression length);
/**
* Returns a new vector which is a copy of the input vector with each element truncated to a specific number of digits.
*
* <a name="ml-server-type-trunc"></a>

* <p>
* Provides a client interface to the <a href="http://docs.marklogic.com/vec:trunc" target="mlserverdoc">vec:trunc</a> server function.
* @param vector The input vector to truncate. (of <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a>)
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
*/
public ServerExpression trunc(ServerExpression vector);
/**
* Returns a new vector which is a copy of the input vector with each element truncated to a specific number of digits.
* <p>
* Provides a client interface to the <a href="http://docs.marklogic.com/vec:trunc" target="mlserverdoc">vec:trunc</a> server function.
* @param vector The input vector to truncate. (of <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a>)
* @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>)
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
*/
public ServerExpression trunc(ServerExpression vector, int n);
/**
* Returns a new vector which is a copy of the input vector with each element truncated to a specific number of digits.
* <p>
* Provides a client interface to the <a href="http://docs.marklogic.com/vec:trunc" target="mlserverdoc">vec:trunc</a> server function.
* @param vector The input vector to truncate. (of <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a>)
* @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>)
* @return a server expression with the <a href="{@docRoot}/doc-files/types/vec_vector.html">vec:vector</a> server data type
*/
public ServerExpression trunc(ServerExpression vector, ServerExpression n);
/**
* Returns a vector value.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
* Copyright (c) 2010-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/

package com.marklogic.client.impl;
Expand Down Expand Up @@ -93,6 +93,18 @@ public ServerExpression normalize(ServerExpression vector1) {
}


@Override
public ServerExpression precision(ServerExpression vector) {
return new VectorCallImpl("vec", "precision", new Object[]{ vector });
}


@Override
public ServerExpression precision(ServerExpression vector, ServerExpression precision) {
return new VectorCallImpl("vec", "precision", new Object[]{ vector, precision });
Comment on lines +103 to +104
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter name precision is easy to confuse with the method name and with the concept of “precision” more generally. Consider renaming the parameter to something more specific like precisionBits (or whatever term aligns with server docs) to improve readability.

Suggested change
public ServerExpression precision(ServerExpression vector, ServerExpression precision) {
return new VectorCallImpl("vec", "precision", new Object[]{ vector, precision });
public ServerExpression precision(ServerExpression vector, ServerExpression precisionBits) {
return new VectorCallImpl("vec", "precision", new Object[]{ vector, precisionBits });

Copilot uses AI. Check for mistakes.
}


@Override
public ServerExpression subtract(ServerExpression vector1, ServerExpression vector2) {
return new VectorCallImpl("vec", "subtract", new Object[]{ vector1, vector2 });
Expand All @@ -111,6 +123,24 @@ public ServerExpression subvector(ServerExpression vector, ServerExpression star
}


@Override
public ServerExpression trunc(ServerExpression vector) {
return new VectorCallImpl("vec", "trunc", new Object[]{ vector });
}


@Override
public ServerExpression trunc(ServerExpression vector, int n) {
return trunc(vector, xs.intVal(n));
}


@Override
public ServerExpression trunc(ServerExpression vector, ServerExpression n) {
return new VectorCallImpl("vec", "trunc", new Object[]{ vector, n });
}


@Override
public ServerExpression vector(ServerExpression values) {
return new VectorCallImpl("vec", "vector", new Object[]{ values });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
* Copyright (c) 2010-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/
package com.marklogic.client.test.rows;

Expand Down Expand Up @@ -57,6 +57,8 @@ void vectorFunctionsHappyPath() {
.bind(op.as("base64Encode", op.vec.base64Encode(op.col("sampleVector"))))
.bind(op.as("base64Decode", op.vec.base64Decode(op.col("base64Encode"))))
.bind(op.as("subVector", op.vec.subvector(op.col("sampleVector"), op.xs.integer(1), op.xs.integer(1))))
.bind(op.as("precision", op.vec.precision(op.col("sampleVector"), op.xs.unsignedInt(16))))
.bind(op.as("trunc", op.vec.trunc(op.col("sampleVector"), 1)))
.bind(op.as("vectorScore", op.vec.vectorScore(op.xs.unsignedInt(1), op.xs.doubleVal(0.5))))
.bind(op.as("simpleVectorScore", op.vec.vectorScore(op.xs.unsignedInt(1), 0.5, 1)))
.bind(op.as("simplestVectorScore", op.vec.vectorScore(op.xs.unsignedInt(1), 0.5)));
Expand All @@ -82,6 +84,8 @@ void vectorFunctionsHappyPath() {
assertEquals(3, ((ArrayNode) row.get("base64Decode")).size());
assertEquals(5.6, row.getDouble("get"));
assertEquals(1, ((ArrayNode) row.get("subVector")).size());
assertEquals(3, ((ArrayNode) row.get("precision")).size());
assertEquals(3, ((ArrayNode) row.get("trunc")).size());
assertEquals(333333.0, row.getDouble("vectorScore"));
assertEquals(666666.0, row.getDouble("simpleVectorScore"));
assertEquals(333333.0, row.getDouble("simplestVectorScore"));
Expand Down Expand Up @@ -184,4 +188,70 @@ void dslAnnTopK() {
List<RowRecord> rows = resultRows(plan);
assertEquals(2, rows.size(), "Just verifying that 'annTopK' works via the DSL and v1/rows.");
}

@Test
void precision() {
// Test vec.precision with default precision (16 bits)
PlanBuilder.ModifyPlan plan = op.fromView("vectors", "persons")
.limit(1)
.bind(op.as("testVector", op.vec.vector(op.xs.doubleSeq(3.14159265, 2.71828182, 1.41421356))))
.bind(op.as("precisionDefault", op.vec.precision(op.col("testVector"))))
.bind(op.as("precision10", op.vec.precision(op.col("testVector"), op.xs.unsignedInt(10))));

List<RowRecord> rows = resultRows(plan);
assertEquals(1, rows.size());
RowRecord row = rows.get(0);

// Verify that precision returns a vector
ArrayNode precisionDefault = (ArrayNode) row.get("precisionDefault");
assertNotNull(precisionDefault);
assertEquals(3, precisionDefault.size());

// Verify precision with 10 bits - should truncate values
ArrayNode precision10 = (ArrayNode) row.get("precision10");
assertNotNull(precision10);
assertEquals(3, precision10.size());
assertEquals(3, precision10.get(0).asInt(), "First element should be truncated to 3");
assertEquals(2, precision10.get(1).asInt(), "Second element should be truncated to 2");
assertEquals(1, precision10.get(2).asInt(), "Third element should be truncated to 1");
Comment on lines +214 to +216
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These assertions don’t actually verify reduced precision behavior; they only check the integer part (which would be the same even if vec:precision were a no-op). Consider asserting on the returned floating-point values (via asDouble() with an expected tolerance) and/or asserting that precision10 differs from the original input (or from precisionDefault) by a measurable amount.

Suggested change
assertEquals(3, precision10.get(0).asInt(), "First element should be truncated to 3");
assertEquals(2, precision10.get(1).asInt(), "Second element should be truncated to 2");
assertEquals(1, precision10.get(2).asInt(), "Third element should be truncated to 1");
// Check floating-point values with a tolerance instead of only integer parts
assertEquals(3.0, precision10.get(0).asDouble(), 0.001, "First element should be truncated near 3.0");
assertEquals(2.0, precision10.get(1).asDouble(), 0.001, "Second element should be truncated near 2.0");
assertEquals(1.0, precision10.get(2).asDouble(), 0.001, "Third element should be truncated near 1.0");
// Verify that reduced-precision values differ from default-precision values
double default0 = precisionDefault.get(0).asDouble();
double default1 = precisionDefault.get(1).asDouble();
double default2 = precisionDefault.get(2).asDouble();
double p10_0 = precision10.get(0).asDouble();
double p10_1 = precision10.get(1).asDouble();
double p10_2 = precision10.get(2).asDouble();
assertNotEquals(default0, p10_0, "Reduced precision should differ from default precision for first element");
assertNotEquals(default1, p10_1, "Reduced precision should differ from default precision for second element");
assertNotEquals(default2, p10_2, "Reduced precision should differ from default precision for third element");

Copilot uses AI. Check for mistakes.
}

@Test
void trunc() {
// Test vec.trunc with different decimal places
PlanBuilder.ModifyPlan plan = op.fromView("vectors", "persons")
.limit(1)
.bind(op.as("testVector", op.vec.vector(op.xs.doubleSeq(1.123456789, 2.123456789, 3.123456789))))
.bind(op.as("truncDefault", op.vec.trunc(op.col("testVector"))))
.bind(op.as("trunc1", op.vec.trunc(op.col("testVector"), 1)))
.bind(op.as("trunc2", op.vec.trunc(op.col("testVector"), op.xs.intVal(2))));

List<RowRecord> rows = resultRows(plan);
assertEquals(1, rows.size());
RowRecord row = rows.get(0);

// Verify truncation with default (0 decimal places)
ArrayNode truncDefault = (ArrayNode) row.get("truncDefault");
assertNotNull(truncDefault);
assertEquals(3, truncDefault.size());
assertEquals(1, truncDefault.get(0).asInt(), "First element should be truncated to 1");
assertEquals(2, truncDefault.get(1).asInt(), "Second element should be truncated to 2");
assertEquals(3, truncDefault.get(2).asInt(), "Third element should be truncated to 3");

// Verify truncation with 1 decimal place
ArrayNode trunc1 = (ArrayNode) row.get("trunc1");
assertNotNull(trunc1);
assertEquals(3, trunc1.size());
assertEquals(1.1, trunc1.get(0).asDouble(), 0.001, "First element should be truncated to 1.1");
assertEquals(2.1, trunc1.get(1).asDouble(), 0.001, "Second element should be truncated to 2.1");
assertEquals(3.1, trunc1.get(2).asDouble(), 0.001, "Third element should be truncated to 3.1");

// Verify truncation with 2 decimal places
ArrayNode trunc2 = (ArrayNode) row.get("trunc2");
assertNotNull(trunc2);
assertEquals(3, trunc2.size());
assertEquals(1.12, trunc2.get(0).asDouble(), 0.001, "First element should be truncated to 1.12");
assertEquals(2.12, trunc2.get(1).asDouble(), 0.001, "Second element should be truncated to 2.12");
assertEquals(3.12, trunc2.get(2).asDouble(), 0.001, "Third element should be truncated to 3.12");
}
}