Skip to main content

Vector Top-K

UDTF: cuvs_top_k

Official cuVS reference: C++ API

Select the smallest or largest values in every dense vector row.

Quickstart​

The call below expects the registered relation input_vectors (the input role), passed as a parenthesized SELECT subquery. Substitute your own relations and column names.

SELECT *
FROM cuvs_top_k(
input => (SELECT id, d0, d1 FROM input_vectors),
k => 1,
select => 'max'
)
ORDER BY row_ordinal, rank;

Inputs​

Each relation argument is a parenthesized SELECT subquery that the planner keeps as a real child; metadata validation resolves a registered table or view for the same role instead. See Vector Inputs for the relation identity rules and the ID, dense-vector type, null, finite-value, and runtime-dimension contract.

RoleRequiredValidation referenceDescription
inputyestableDense-vector rows consumed by this operation.

Vector element types​

Element typeValid metrics
Float32Not applicable

Arguments and options​

Scalar SQL arguments​

ArgumentTypeRequiredDescription
kintegeryesNumber of values selected from each row.
selectenum ("min", "max")yesSelect the minimum or maximum values.

SQL value argument schemas​

ArgumentRequiredLiteral shapeDefaultConstraintsDescription
kyesintegerNo defaultminimum 1; maximum 4294967295Number of values selected from each row.
selectyesstringNo defaultone of "min", "max"Select the minimum or maximum values.

Vector binding shapes​

Each relation subquery must project a non-null id field followed by either one or more non-null feature fields of a supported element type (Float32) or one non-null list vector field named vector.

For wide vectors, the projection order defines the feature dimensions. A list vector relation must contain no feature field beside id and vector.

Output​

ColumnTypeNullableDescription
row_ordinalUInt64noZero-based physical ordinal of the evaluated input row; it disambiguates duplicate input IDs. Follows the input's top-level ORDER BY on output columns; unspecified without one.
idsame_as_input.idnoLogical ID copied from the input relation.
rankUInt32noOne-based value rank within an input row. Order consumers explicitly by row_ordinal, rank.
positionInt64noZero-based dimension index of the selected value within the input vector.
valueFloat32noSelected Float32 input vector value; rank follows ascending values for min and descending values for max.

Concrete schemas are call-specific. Run gpu_validate_call against registered relations to inspect the output schema after the actual ID types and literal options are validated.

Examples​

This example uses the raw products and reviews tables from the Amazon Reviews demo dataset. It does not require the semantic embedding preparation.

Which review themes describe each group of products?​

A product analyst wants a short description of groups with similar complaint profiles. This query filters 2022 low-rating reviews in the headphones and accessories category, calculates eight keyword-mention rates per product, and keeps products with at least ten reviews. KMeans groups the resulting 2,384 products into six clusters. SQL then averages each cluster's product profiles, and Top-K returns its three largest rates.

Both algorithms run in the statement below. The numerical features come from the raw text through visible SQL expressions; they are not stored embeddings or predefined cluster labels.

WITH complaint_features AS (
SELECT
p.product_id AS id,
COUNT(*) AS negative_reviews,
CAST(AVG(CASE WHEN LOWER(r.text) LIKE '%battery%' THEN 1.0 ELSE 0.0 END) AS REAL) AS battery,
CAST(AVG(CASE WHEN LOWER(r.text) LIKE '%connect%' OR LOWER(r.text) LIKE '%bluetooth%' THEN 1.0 ELSE 0.0 END) AS REAL) AS connection,
CAST(AVG(CASE WHEN LOWER(r.text) LIKE '%sound%' OR LOWER(r.text) LIKE '%volume%' THEN 1.0 ELSE 0.0 END) AS REAL) AS sound,
CAST(AVG(CASE WHEN LOWER(r.text) LIKE '%comfort%' OR LOWER(r.text) LIKE '%fit%' THEN 1.0 ELSE 0.0 END) AS REAL) AS comfort,
CAST(AVG(CASE WHEN LOWER(r.text) LIKE '%broke%' OR LOWER(r.text) LIKE '%stopped working%' THEN 1.0 ELSE 0.0 END) AS REAL) AS durability,
CAST(AVG(CASE WHEN LOWER(r.text) LIKE '%charg%' THEN 1.0 ELSE 0.0 END) AS REAL) AS charging,
CAST(AVG(CASE WHEN LOWER(r.text) LIKE '%microphone%' OR LOWER(r.text) LIKE '%mic %' THEN 1.0 ELSE 0.0 END) AS REAL) AS microphone,
CAST(AVG(CASE WHEN LOWER(r.text) LIKE '%return%' OR LOWER(r.text) LIKE '%refund%' THEN 1.0 ELSE 0.0 END) AS REAL) AS returns
FROM reviews AS r
JOIN products AS p
ON r.product_id = p.product_id
WHERE p.category_l2 = 'Headphones, Earbuds & Accessories'
AND r.review_time >= TIMESTAMP '2022-01-01 00:00:00'
AND r.review_time < TIMESTAMP '2023-01-01 00:00:00'
AND r.rating <= 2
AND r.text IS NOT NULL
GROUP BY p.product_id
HAVING COUNT(*) >= 10
),

clusters AS (
SELECT
id,
cluster_id
FROM cuvs_kmeans(
input => (
SELECT
id,
COALESCE(battery, CAST(0 AS REAL)) AS battery,
COALESCE(connection, CAST(0 AS REAL)) AS connection,
COALESCE(sound, CAST(0 AS REAL)) AS sound,
COALESCE(comfort, CAST(0 AS REAL)) AS comfort,
COALESCE(durability, CAST(0 AS REAL)) AS durability,
COALESCE(charging, CAST(0 AS REAL)) AS charging,
COALESCE(microphone, CAST(0 AS REAL)) AS microphone,
COALESCE(returns, CAST(0 AS REAL)) AS returns
FROM complaint_features
ORDER BY id
),
n_clusters => 6,
n_init => 5
)
),

profiles AS (
SELECT
CAST(k.cluster_id AS BIGINT) AS id,
COALESCE(CAST(AVG(c.battery) AS REAL), CAST(0 AS REAL)) AS battery,
COALESCE(CAST(AVG(c.connection) AS REAL), CAST(0 AS REAL)) AS connection,
COALESCE(CAST(AVG(c.sound) AS REAL), CAST(0 AS REAL)) AS sound,
COALESCE(CAST(AVG(c.comfort) AS REAL), CAST(0 AS REAL)) AS comfort,
COALESCE(CAST(AVG(c.durability) AS REAL), CAST(0 AS REAL)) AS durability,
COALESCE(CAST(AVG(c.charging) AS REAL), CAST(0 AS REAL)) AS charging,
COALESCE(CAST(AVG(c.microphone) AS REAL), CAST(0 AS REAL)) AS microphone,
COALESCE(CAST(AVG(c.returns) AS REAL), CAST(0 AS REAL)) AS returns
FROM clusters AS k
JOIN complaint_features AS c
ON k.id = c.id
GROUP BY k.cluster_id
)

SELECT
t.id AS cluster_id,
t.rank,
CASE t.position
WHEN 0 THEN 'battery'
WHEN 1 THEN 'connection'
WHEN 2 THEN 'sound'
WHEN 3 THEN 'comfort'
WHEN 4 THEN 'durability'
WHEN 5 THEN 'charging'
WHEN 6 THEN 'microphone'
WHEN 7 THEN 'returns'
END AS issue,
t.value AS mean_product_mention_rate
FROM cuvs_top_k(
input => (
SELECT
id,
battery,
connection,
sound,
comfort,
durability,
charging,
microphone,
returns
FROM profiles
),
k => 3,
select => 'max'
) AS t
ORDER BY
t.id,
t.rank;

The 2026-09-28 capture returned 18 rows in a median 0.776 seconds over three runs. Its cluster 0 has the following profile:

RankIssueMean product mention rate
1durability35.99%
2sound18.61%
3returns11.23%

For cluster 1, sound leads at 45.10%, followed by charging at 24.20% and connection at 24.09%. Those profiles give an analyst different starting points for reading reviews: breakage in one product group, audio and charging reports in another. The complete saved output contains all six groups; capture conditions include raw-text feature calculation and both algorithm calls.

Top-K selects dimensions within each cluster profile. Here position = 5 means the charging dimension, because charging is the sixth projected feature. An ORDER BY ... LIMIT 3 over the whole result would answer a different question.

Each rate counts reviews containing an English substring. Negation is not resolved, and a review can mention several themes. The profile is an average of product-level rates: a product with ten reviews has the same weight as a product with a thousand. Mentions of returns do not establish a completed return transaction.

Limits​

  • Validation resolves named tables or views and reads schemas only; it does not execute relation scans or GPU work.
  • Execution relation arguments require parenthesized subqueries; dry-run validation accepts registered named relations only.
  • k must not exceed the vector dimension. Empty input returns an empty result with the stable schema.
  • Equal selected values are ordered by position; when ties straddle rank k, the selected positions among them are unspecified.

To dry-run validate relation metadata, column types, and options without execution, see gpu_validate_call.