> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lambdadb.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from Qdrant

> Use the LambdaDB Migration CLI to move Qdrant collections, points, vectors, sparse vectors, and payload indexes to LambdaDB.

Use the LambdaDB Migration CLI to migrate Qdrant collections into LambdaDB. The CLI inventories your Qdrant collection, generates an editable LambdaDB mapping, creates the target LambdaDB collection when needed, streams points as LambdaDB documents, saves local checkpoints, and validates migrated records before cutover.

Qdrant models searchable records as **points** made of an `id`, one or more vectors, and an optional `payload`. LambdaDB models records as **documents**: each document is stored as JSON, while searchable fields are declared in the collection's `indexConfigs`.

## What the CLI supports

| Qdrant data          | LambdaDB target      | Migration behavior                                                                                               |
| :------------------- | :------------------- | :--------------------------------------------------------------------------------------------------------------- |
| Collection           | Collection           | Keep one LambdaDB collection per Qdrant collection unless you are also changing tenancy or isolation boundaries. |
| Point ID             | Document `id`        | Numeric Qdrant IDs are converted to strings. UUID IDs remain the same string value.                              |
| Unnamed dense vector | `vector` field       | The generated mapping uses `dense` as the default target field.                                                  |
| Named dense vector   | `vector` field       | Each Qdrant named vector maps to its own LambdaDB vector field.                                                  |
| Sparse vector        | `sparseVector` field | Qdrant `indices` and `values` arrays are converted to a LambdaDB sparse object.                                  |
| Payload              | Document fields      | Payload fields are flattened by default. Indexed payload fields become LambdaDB index configs.                   |
| Payload index        | `indexConfigs` field | Supported types include `keyword`, `text`, `long`, `double`, `boolean`, `datetime`, and `object`.                |

<Warning>
  Qdrant multi-vectors and Manhattan distance collections require workload-specific review. The CLI detects them and rejects the default migration path instead of silently creating a misleading LambdaDB schema.
</Warning>

## Step 1: Install the CLI

Install the latest release:

```bash theme={null}
curl -fsSL https://raw.githubusercontent.com/lambdadb/lambdadb-migration/main/install.sh | sh
```

Install a specific version:

```bash theme={null}
curl -fsSLO https://raw.githubusercontent.com/lambdadb/lambdadb-migration/main/install.sh
sh install.sh --version v0.1.5 --install-dir "$HOME/.local/bin"
```

Check the Qdrant command:

```bash theme={null}
lambdadb-migration qdrant --help
```

## Step 2: Set credentials

<Note>
  LambdaDB Cloud uses region-specific API base URLs. Use the **base URL**, **project name**, and **project API key** shown for your project in the LambdaDB Cloud console. Do not assume a global default URL or a fixed project name.
</Note>

Set your LambdaDB connection values:

```bash theme={null}
export LAMBDADB_BASE_URL="YOUR_REGION_BASE_URL"
export LAMBDADB_PROJECT_NAME="YOUR_PROJECT_NAME"
export LAMBDADB_PROJECT_API_KEY="YOUR_PROJECT_API_KEY"
```

If your Qdrant deployment requires an API key, pass it with `--qdrant.api-key`.

## Step 3: Generate inventory and mapping

Run the inventory command against the Qdrant gRPC endpoint:

```bash theme={null}
lambdadb-migration inventory qdrant \
  --qdrant.url http://localhost:6334 \
  --qdrant.collection articles \
  --output qdrant-inventory.yaml
```

The output includes the source inventory and an editable LambdaDB mapping:

```yaml theme={null}
mapping:
  target:
    collection: articles
    createCollection: true
  vectors:
    dense:
      targetField: dense
      dimensions: 1536
      similarity: cosine
  sparseVectors:
    sparse:
      targetField: sparse
  payload:
    mode: flatten
    rename:
      metadata.url: metadata_url
    indexConfigs:
      tenant_id:
        type: keyword
      title:
        type: text
      metadata_url:
        type: keyword
  ids:
    targetField: id
```

Review the generated mapping before running the migration. In particular, check vector dimensions, vector similarities, payload index types, renamed dotted fields, and the target collection name.

<Note>
  Generated mappings set `target.createCollection: true` by default. With that setting, the migration creates the LambdaDB collection if it is missing and waits until it is ready before writing documents.
</Note>

## Step 4: Run a dry run

Use a dry run to validate the mapping and inspect the planned migration without writing documents:

```bash theme={null}
lambdadb-migration qdrant \
  --qdrant.url http://localhost:6334 \
  --qdrant.collection articles \
  --lambdadb.base-url "$LAMBDADB_BASE_URL" \
  --lambdadb.project-name "$LAMBDADB_PROJECT_NAME" \
  --lambdadb.api-key "$LAMBDADB_PROJECT_API_KEY" \
  --lambdadb.collection articles \
  --mapping-file qdrant-inventory.yaml \
  --migration.dry-run
```

## Step 5: Run the migration

Run the migration with validation enabled:

```bash theme={null}
lambdadb-migration qdrant \
  --qdrant.url http://localhost:6334 \
  --qdrant.collection articles \
  --lambdadb.base-url "$LAMBDADB_BASE_URL" \
  --lambdadb.project-name "$LAMBDADB_PROJECT_NAME" \
  --lambdadb.api-key "$LAMBDADB_PROJECT_API_KEY" \
  --lambdadb.collection articles \
  --mapping-file qdrant-inventory.yaml \
  --migration.write-mode bulk \
  --migration.validate \
  --migration.validation-report validation-report.json
```

Migration progress is written to stderr with accepted count, percent, batch size, rate, and elapsed time.

The CLI stores checkpoints under `.lambdadb-migration/checkpoints` by default. If the command is interrupted, rerun the same command to resume. Use `--migration.restart` to ignore an existing checkpoint and start from the beginning.

Use `--migration.create-collection=false` when the target LambdaDB collection already exists and the migration should not create it.

## Step 6: Review validation

`--migration.validate` checks the accepted record count, fetches a sample of migrated documents from LambdaDB using strongly consistent reads, and compares sampled fields.

`--migration.validation-report` writes the validation result as JSON. The report includes source count, accepted records, LambdaDB `numDocs`, sampled IDs, compared sample count, query overlap results, and validation errors.

<Note>
  `numDocs` is reported for visibility, but sample fetch and field comparison are the stronger validation checks for read-after-write confirmation.
</Note>

For dense-vector migrations, add query overlap checks:

```bash theme={null}
lambdadb-migration qdrant \
  --qdrant.url http://localhost:6334 \
  --qdrant.collection articles \
  --lambdadb.base-url "$LAMBDADB_BASE_URL" \
  --lambdadb.project-name "$LAMBDADB_PROJECT_NAME" \
  --lambdadb.api-key "$LAMBDADB_PROJECT_API_KEY" \
  --lambdadb.collection articles \
  --mapping-file qdrant-inventory.yaml \
  --migration.validate \
  --migration.validation-report validation-report.json \
  --migration.query-overlap
```

By default, `--migration.query-overlap` reports dense-vector overlap without failing the migration. Set `--migration.query-overlap-min-ratio` above `0` to require a minimum average overlap.

## Mapping details

Use this distance mapping when creating LambdaDB vector fields:

| Qdrant distance | LambdaDB similarity                                                            |
| :-------------- | :----------------------------------------------------------------------------- |
| `Cosine`        | `cosine`                                                                       |
| `Dot`           | `dot_product`                                                                  |
| `Euclid`        | `euclidean`                                                                    |
| `Manhattan`     | No direct equivalent. Re-evaluate the embedding/search setup before migrating. |

For a Qdrant point with a single dense vector:

```json Qdrant point theme={null}
{
  "id": 1,
  "vector": [0.12, 0.34, 0.56],
  "payload": {
    "tenant_id": "acme",
    "title": "Refund policy",
    "body": "Refunds are available within 7 days.",
    "created_at": "2026-05-01T10:00:00Z"
  }
}
```

the CLI writes a LambdaDB document like this:

```json LambdaDB document theme={null}
{
  "id": "1",
  "tenant_id": "acme",
  "title": "Refund policy",
  "body": "Refunds are available within 7 days.",
  "created_at": "2026-05-01T10:00:00Z",
  "dense": [0.12, 0.34, 0.56]
}
```

For Qdrant named vectors:

```json Qdrant point with named vectors theme={null}
{
  "id": 1,
  "vector": {
    "title_dense": [0.1, 0.2, 0.3],
    "body_dense": [0.4, 0.5, 0.6]
  },
  "payload": {
    "title": "Refund policy",
    "body": "Refunds are available within 7 days."
  }
}
```

the CLI writes separate LambdaDB vector fields:

```json LambdaDB document theme={null}
{
  "id": "1",
  "title": "Refund policy",
  "body": "Refunds are available within 7 days.",
  "title_dense": [0.1, 0.2, 0.3],
  "body_dense": [0.4, 0.5, 0.6]
}
```

For Qdrant sparse vectors, the CLI converts `indices` and `values` to an object whose keys are index strings:

```json Qdrant sparse vector theme={null}
{
  "indices": [1, 42],
  "values": [0.22, 0.8]
}
```

```json LambdaDB sparse vector theme={null}
{
  "sparse": {
    "1": 0.22,
    "42": 0.8
  }
}
```

## Reduce application changes with SDK compatibility

After data is in LambdaDB, Python and TypeScript applications can use LambdaDB's Qdrant compatibility clients as a bridge before rewriting every query to native LambdaDB APIs.

The compatibility clients support common Qdrant-style calls such as collection creation, dense-vector upsert, dense-vector query, retrieve, delete, filtered scroll, and unfiltered count. They are not full Qdrant client replacements, and unsupported behavior raises an explicit error where possible.

<CardGroup cols={1}>
  <Card title="Qdrant SDK compatibility" href="/guides/migrations/qdrant/qdrant-sdk-compatibility" icon="code">
    Use Python `lambdadb.compat.qdrant` or TypeScript `@functional-systems/lambdadb/compat/qdrant` to reduce application code changes.
  </Card>
</CardGroup>

## Rewrite vector search

A Qdrant query against a named vector:

```python Python theme={null}
from qdrant_client import models

qdrant.query_points(
    collection_name="articles",
    query=query_vector,
    using="dense",
    limit=10,
    query_filter=models.Filter(
        must=[
            models.FieldCondition(
                key="tenant_id",
                match=models.MatchValue(value="acme"),
            )
        ]
    ),
    with_payload=True,
)
```

becomes a LambdaDB `knn` query:

```python Python theme={null}
results = coll.query(
    query={
        "knn": {
            "field": "dense",
            "queryVector": query_vector,
            "k": 10,
            "filter": {
                "queryString": {
                    "query": '"acme"',
                    "defaultField": "tenant_id",
                }
            },
        }
    },
    size=10,
)
```

When using LambdaDB managed embeddings, send query text instead of a vector:

```python Python theme={null}
results = coll.query(
    query={
        "knn": {
            "field": "bodyEmbedding",
            "queryText": "refund policy",
            "k": 10,
        }
    },
    size=10,
)
```

## Rewrite filters

Qdrant filters commonly use `must`, `must_not`, and `should` clauses. In LambdaDB, use a `bool` query when the filter logic is larger than a single query string.

```json Qdrant filter theme={null}
{
  "must": [
    { "key": "tenant_id", "match": { "value": "acme" } },
    { "key": "created_at", "range": { "gte": "2026-01-01T00:00:00Z" } }
  ],
  "must_not": [
    { "key": "status", "match": { "value": "deleted" } }
  ]
}
```

```json LambdaDB bool query theme={null}
{
  "bool": [
    {
      "queryString": {
        "query": "tenant_id:acme"
      },
      "occur": "filter"
    },
    {
      "queryString": {
        "query": "created_at:[2026-01-01T00:00:00Z TO *]"
      },
      "occur": "filter"
    },
    {
      "queryString": {
        "query": "status:deleted"
      },
      "occur": "must_not"
    }
  ]
}
```

Use field types intentionally:

| Qdrant payload use                        | LambdaDB index type |
| :---------------------------------------- | :------------------ |
| Exact string match, tags, IDs, tenant IDs | `keyword`           |
| Natural-language matching                 | `text`              |
| Integer range or sorting                  | `long`              |
| Floating-point range or sorting           | `double`            |
| Date/time range or sorting                | `datetime`          |
| Boolean flags                             | `boolean`           |
| Nested JSON kept as a searchable object   | `object`            |

## Rewrite hybrid search

Qdrant hybrid queries often use `prefetch` plus RRF fusion across dense and sparse vectors.

```json Qdrant hybrid query theme={null}
{
  "prefetch": [
    {
      "query": { "indices": [1, 42], "values": [0.22, 0.8] },
      "using": "sparse",
      "limit": 20
    },
    {
      "query": [0.01, 0.45, 0.67],
      "using": "dense",
      "limit": 20
    }
  ],
  "query": { "fusion": "rrf" },
  "limit": 10
}
```

In LambdaDB, express the same dense/sparse fusion with `rrf`:

```json LambdaDB hybrid query theme={null}
{
  "rrf": [
    {
      "sparseVector": {
        "field": "sparse",
        "queryVector": {
          "1": 0.22,
          "42": 0.8
        }
      }
    },
    {
      "knn": {
        "field": "dense",
        "queryVector": [0.01, 0.45, 0.67],
        "k": 20
      }
    }
  ]
}
```

You can also combine lexical and vector relevance:

```json LambdaDB lexical + vector hybrid query theme={null}
{
  "l2": [
    {
      "queryString": {
        "query": "refund policy",
        "defaultField": "body"
      },
      "boost": 0.4
    },
    {
      "knn": {
        "field": "dense",
        "queryVector": [0.01, 0.45, 0.67],
        "k": 20
      },
      "boost": 0.6
    }
  ]
}
```

## Common gotchas

* **IDs**: LambdaDB document IDs are strings. Numeric Qdrant IDs are converted to strings during migration.
* **Field names**: LambdaDB field names cannot contain dots. The generated mapping renames dotted Qdrant payload keys, such as `metadata.url` to `metadata_url`.
* **Schema**: Qdrant can store payload without deciding every searchable field up front. In LambdaDB, decide which fields need indexes before migration.
* **Bulk writes**: Regular upsert accepts request payloads up to 6 MB. Bulk upsert accepts up to 200 MB, but not for collections with managed embeddings.
* **Sparse vectors**: Qdrant sparse vectors use separate `indices` and `values`; LambdaDB uses object key-value pairs.
* **Multi-vectors**: Qdrant multi-vectors store matrices for late-interaction models. Plan and validate these workloads separately.
* **Consistency**: LambdaDB uses eventual reads by default, but supports `consistentRead` for strong read-after-write checks. The CLI validation uses strongly consistent sample fetches. For bulk upsert, allow time for documents to become visible after indexing completes.

## Next steps

<CardGroup cols={2}>
  <Card title="Migration CLI" href="/guides/migrations/overview" icon="arrows-rotate">
    Review the shared LambdaDB Migration CLI workflow.
  </Card>

  <Card title="Qdrant SDK compatibility" href="/guides/migrations/qdrant/qdrant-sdk-compatibility" icon="code">
    Keep common Qdrant-style Python and TypeScript client calls while migrating application code.
  </Card>

  <Card title="Create a collection" href="/guides/collections/create-a-collection" icon="database">
    Define LambdaDB index configurations for your migrated data.
  </Card>

  <Card title="Bulk upsert data" href="/guides/documents/bulk-upsert-data" icon="upload">
    Load larger migrated datasets through the bulk upsert workflow.
  </Card>

  <Card title="Hybrid query" href="/guides/search/hybrid" icon="bolt">
    Combine lexical, dense vector, and sparse vector search.
  </Card>
</CardGroup>
