#10607·qdrant

Binary-quantized Euclidean query results are not ordered by reported score when rescore=false

Author: leemeiiCreated Sep 11, 2026Updated Sep 11, 2026
Labelsbugaccepted

Current Behavior

With Qdrant v1.19.0, an Euclidean nearest-neighbor query using binary quantization and quantization.rescore=false can return results whose reported score values are not ordered from smaller to larger.

Typical output:

ids:    [4, 0, 1, 2, 3]
scores: [1.4142135, 0.0, 0.0, 1.4142135, 1.4142135]

The result with score 1.4142135 appears before results with score 0.0.

The issue reproduces repeatedly on a fresh collection. It does not reproduce when rescore=true or when quantization is ignored.

Steps to Reproduce

  1. Start Qdrant v1.19.0:
bash
docker run --rm --name qdrant-quant-order-repro \
  -p 127.0.0.1:16353:6333 \
  qdrant/qdrant:v1.19.0
  1. Install the Python dependency:
bash
pip install requests==2.34.2
  1. Run this standalone Python script:
python
import time
import uuid
import requests

BASE = "http://127.0.0.1:16353"
COLLECTION = "quant_order_" + uuid.uuid4().hex[:8]

SEED = [
    [-0.0494680889, -1.5822200775],
    [-0.5807184577, -1.3024002314],
    [-0.2222844660,  1.0905684233],
    [-0.5575756431,  0.6532790065],
    [ 0.7734391689, -0.2747519016],
]

# Update point 1 by changing only coordinate 0.
UPDATED_POINT_1 = [-0.2057184577, -1.3024002314]
QUERY = [1.3748825788, -1.0415413379]


def call(method, path, **kwargs):
    response = requests.request(
        method,
        BASE + path,
        timeout=30,
        **kwargs,
    )
    response.raise_for_status()
    return response.json()


def search(params):
    points = call(
        "POST",
        f"/collections/{COLLECTION}/points/query",
        json={
            "query": QUERY,
            "limit": 5,
            "params": params,
        },
    )["result"]["points"]

    return [
        (int(point["id"]), float(point["score"]))
        for point in points
    ]


def is_monotonic(rows):
    scores = [score for _, score in rows]
    return all(
        left <= right + 2e-5
        for left, right in zip(scores, scores[1:])
    )


try:
    call(
        "PUT",
        f"/collections/{COLLECTION}",
        json={
            "vectors": {
                "size": 2,
                "distance": "Euclid",
                "hnsw_config": {
                    "m": 4,
                    "ef_construct": 64,
                },
            },
            "quantization_config": {
                "binary": {
                    "always_ram": True,
                },
            },
        },
    )

    call(
        "PUT",
        f"/collections/{COLLECTION}/points?wait=true",
        json={
            "points": [
                {"id": i, "vector": vector}
                for i, vector in enumerate(SEED)
            ]
        },
    )

    call(
        "PUT",
        f"/collections/{COLLECTION}/points/vectors?wait=true",
        json={
            "points": [
                {"id": 1, "vector": UPDATED_POINT_1}
            ]
        },
    )

    time.sleep(1)

    quantized_params = {
        "exact": False,
        "hnsw_ef": 256,
        "quantization": {
            "rescore": False,
        },
    }

    for attempt in range(3):
        rows = search(quantized_params)
        print(f"quantized run {attempt + 1}: {rows}")

        # Euclidean results should be ordered from smaller
        # to larger reported distance.
        assert not is_monotonic(rows), (
            "The anomaly was not reproduced: "
            f"{rows}"
        )

    rescored = search({
        "exact": False,
        "hnsw_ef": 256,
        "quantization": {
            "rescore": True,
            "oversampling": 4.0,
        },
    })
    print("rescore=true:", rescored)
    assert is_monotonic(rescored)

    exact = search({
        "exact": True,
        "quantization": {
            "ignore": True,
        },
    })
    print("quantization ignored:", exact)
    assert is_monotonic(exact)

    print("BUG REPRODUCED")

finally:
    requests.delete(
        f"{BASE}/collections/{COLLECTION}",
        timeout=30,
    )

Expected Behavior

For an Euclidean nearest-neighbor query, returned results should be ordered by their reported distance:

score[i] <= score[i + 1]

Quantization-related approximation error and recall changes are acceptable, but the returned list should not violate the ordering implied by its own reported scores.

Possible Solution

Ensure that the final results from the binary-quantized, non-rescored search path are sorted by the reported Euclidean score before being returned.

A regression test should verify that the returned score sequence is monotonic when:

distance = Euclid
quantization = binary
exact = false
quantization.rescore = false

Detailed Description

The test creates a two-dimensional Euclidean collection with binary quantization enabled. Five points are inserted, and one stored vector is updated before querying.

With rescore=false, Qdrant returns quantized scores such as:

[1.4142135, 0.0, 0.0, 1.4142135, 1.4142135]

For Euclidean distance, smaller scores represent closer points. Therefore, a result with score 1.4142135 should not precede results with score 0.0.

The same collection and query return monotonic scores when rescoring is enabled or quantization is ignored.

Possible Implementation

  • Add an integration regression test using the minimal reproducer above.
  • Inspect the binary-quantized search path used when rescore=false.
  • Ensure the final returned Top-k list is sorted by the score reported in the response.
  • Verify the behavior for both fresh points and updated vectors.
  • Keep the existing approximation and recall behavior unchanged unless the ordering contract requires otherwise.