Sign in

    moon-qdrant

    A MoonBit client for the Qdrant vector database REST API.

    qdrant
    vector-database
    client
    rag
    embedding
    Download zip
    Author
    Version
    0.3.0
    License
    Apache-2.0
    Last updated
    last month
    Downloads
    44

    #yuzhiblue/moon-qdrant

    A MoonBit client for the Qdrant vector database REST API.

    moon-qdrant lets MoonBit programs manage Qdrant collections, upsert points, and run vector searches without leaving the MoonBit ecosystem. It is built on oboard/mio for HTTP transport and the MoonBit core JSON library.

    #Why

    Qdrant is one of the most widely used vector databases for RAG and embedding search. The MoonBit ecosystem already has vector engines and servers (e.g. trkbt10/vcdb), but no client library to talk to a running Qdrant service. moon-qdrant fills that gap with a small, explicit API surface that maps 1:1 to Qdrant REST endpoints.

    #Install

    moon add yuzhiblue/moon-qdrant

    #Quick start

    Start a local Qdrant server (Docker):

    docker run -p 6333:6333 qdrant/qdrant

    Then use the client:

    let client = QdrantClient::new("http://localhost:6333")

    // health check
    let ok = client.health()

    // create a collection with 4-dimensional cosine vectors
    client.create_collection(
    "demo",
    CollectionConfig::new(VectorParams::new(4, Distance::Cosine)),
    )

    // list collections
    let names = client.list_collections()

    // inspect a collection
    let info = client.collection_info("demo")

    // does it exist?
    let exists = client.collection_exists("demo")

    // write some points
    client.upsert_points("demo", [
    PointStruct::new(1, [0.1, 0.2, 0.3, 0.4], { "tag": "alpha" }),
    PointStruct::new(2, [0.5, 0.6, 0.7, 0.8], { "tag": "beta" }),
    ])

    // read one back
    let point = client.get_point("demo", 1)

    // search for the most similar points
    let hits = client.search_points("demo", [0.1, 0.2, 0.3, 0.4], limit=2)

    // search with a payload filter
    let filtered = client.search_points(
    "demo",
    [0.1, 0.2, 0.3, 0.4],
    limit=2,
    filter={ "must": [{ "key": "tag", "match": { "value": "alpha" } }] },
    )

    // delete points by id
    client.delete_points("demo", [2])

    // drop the collection
    client.delete_collection("demo")

    #Examples

    Health-check CLI (needs a running Qdrant server):

    moon run cmd/main -- http://localhost:6333

    End-to-end demo: create collection, upsert points, search (with and without a payload filter), read a point, delete points, drop the collection:

    docker run -p 6333:6333 qdrant/qdrant moon run examples/demo -- http://localhost:6333

    #Status / roadmap

    Implemented:

    #Development

    moon check --deny-warn moon test moon fmt

    #License

    Apache-2.0

    VectorProvider

    pub trait VectorProvider {
    async fn create_collection(self : Self, name : String, config : CollectionConfig) -> Unit
    async fn delete_collection(self : Self, name : String) -> Unit
    async fn collection_exists(self : Self, name : String) -> Bool
    async fn upsert_points(self : Self, name : String, points : Array[PointStruct]) -> Unit
    async fn upsert_points_batch(self : Self, name : String, points : Array[PointStruct]) -> Unit
    async fn search_points(self : Self, name : String, vector : Array[Double], limit : Int, filter? : Json?) -> Array[ScoredPoint]
    async fn search_with_filter(self : Self, name : String, vector : Array[Double], limit : Int, filter : Filter) -> Array[ScoredPoint]
    }

    A common interface implemented by vector-service clients.

    QdrantClient implements this trait. The trait exists so that higher-level code (RAG pipelines, embedding indexes) can be written against one shape and later switch to another vector service without changing call sites.

    AliasInfo

    pub struct AliasInfo {
    alias_name : String
    collection_name : String
    }

    One collection alias entry, as returned by GET /collections/aliases.

    AliasInfo::from_json

    fn AliasInfo::from_json(json : Json) -> AliasInfo?

    Parse one alias entry: { "alias_name": "prod", "collection_name": "demo" }.

    CollectionConfig

    pub struct CollectionConfig {
    vectors : VectorParams
    }

    Collection configuration accepted by PUT /collections/{name}.

    CollectionConfig::new

    CollectionConfig::to_json

    fn CollectionConfig::to_json(self : CollectionConfig) -> Json

    CollectionInfo

    pub struct CollectionInfo {
    status : String
    points_count : Int?
    vectors : VectorParams?
    }

    Detailed information about a collection, from GET /collections/{name}.

    CollectionInfo::from_json

    fn CollectionInfo::from_json(json : Json) -> CollectionInfo?

    Parse the result object of GET /collections/{name} into a CollectionInfo. points_count and vectors are optional because Qdrant may omit them while a collection is still building.

    CollectionList

    pub struct CollectionList {
    names : Array[String]
    }

    Parsed result of GET /collections: the list of collection names.

    CollectionList::from_json

    fn CollectionList::from_json(json : Json) -> CollectionList?

    CollectionSummary

    pub struct CollectionSummary {
    name : String
    }

    Minimal parsed view of one collection, from GET /collections.

    CollectionSummary::from_json

    fn CollectionSummary::from_json(json : Json) -> CollectionSummary?

    Condition

    pub(all) enum Condition {
    MatchValue(String, Json)
    MatchKeyword(String, String)
    MatchValues(String, Array[Json])
    Range(String, Double?, Double?, Double?, Double?)
    IsEmpty(String)
    IsNull(String)
    }

    A single payload filter condition, in the subset of Qdrant's filter DSL that covers the common exact-match, set-membership, range and existence cases. Construct one with the Condition::match_* / Condition::range_* helpers and combine them inside a Filter.

    Condition::is_empty

    fn Condition::is_empty(field : String) -> Condition

    Convenience constructor: payload field has no value.

    Condition::is_null

    fn Condition::is_null(field : String) -> Condition

    Convenience constructor: payload field is explicitly null.

    Condition::match_keyword

    fn Condition::match_keyword(field : String, keyword : String) -> Condition

    Convenience constructor: payload field equals a keyword string.

    Condition::match_value

    fn Condition::match_value(field : String, value : Json) -> Condition

    Convenience constructor: payload field equals a single JSON value.

    Condition::match_values

    fn Condition::match_values(field : String, values : Array[Json]) -> Condition

    Convenience constructor: payload field equals one of the given values.

    Condition::range

    fn Condition::range(field : String, gte? : Double?, lte? : Double?, gt? : Double?, lt? : Double?) -> Condition

    Convenience constructor: numeric range on a payload field. All four bounds are optional; omitted bounds are dropped from the serialized filter.

    Condition::to_json

    fn Condition::to_json(self : Condition) -> Json

    Serialize a Condition to its Qdrant filter JSON fragment.

    Distance

    pub(all) enum Distance {
    Cosine
    Euclid
    Dot
    }

    Distance metric used by a Qdrant vector collection. Wire values follow the Qdrant REST API enum names.

    Distance::from_wire

    fn Distance::from_wire(s : String) -> Distance?

    Parse a Distance from its Qdrant wire string. Returns None for unknown values.

    Distance::to_wire

    fn Distance::to_wire(self : Distance) -> String

    Convert a Distance to its Qdrant wire string.

    Filter

    pub struct Filter {
    must : Array[Condition]
    should : Array[Condition]
    must_not : Array[Condition]
    }

    A Qdrant filter, composed of three conjunctive clause groups.

    Empty clause groups are omitted from the serialized JSON. Use Filter::new() to start, then chain must / should / must_not to add conditions.

    Filter::must

    fn Filter::must(self : Filter, c : Condition) -> Filter

    Append a condition to the must clause group (all must hold).

    Filter::must_not

    fn Filter::must_not(self : Filter, c : Condition) -> Filter

    Append a condition to the must_not clause group (none may hold).

    Filter::new

    fn Filter::new() -> Filter

    Create an empty filter.

    Filter::should

    fn Filter::should(self : Filter, c : Condition) -> Filter

    Append a condition to the should clause group (any may hold).

    Filter::to_json

    fn Filter::to_json(self : Filter) -> Json

    Serialize the filter to Qdrant's filter JSON. Empty clause groups are omitted so the serialized object only contains non-empty groups.

    PointStruct

    pub struct PointStruct {
    id : Int
    vector : Array[Double]
    payload : Json
    }

    A single point: an id, a dense vector, and an optional JSON payload.

    PointStruct::from_json

    fn PointStruct::from_json(json : Json) -> PointStruct?

    Parse a point object as returned by the point read endpoint: { "id": 1, "vector": [...], "payload": {...} }.

    PointStruct::new

    fn PointStruct::new(id : Int, vector : Array[Double], payload : Json) -> PointStruct

    PointStruct::to_json

    fn PointStruct::to_json(self : PointStruct) -> Json

    Convert to the point object used by PUT /collections/{name}/points: { "id": 1, "vector": [...], "payload": {...} }.

    QdrantClient

    pub struct QdrantClient {
    base_url : String
    api_key : String?
    http :
    RequestClient

    }

    Configuration for a QdrantClient.

    QdrantClient::collection_exists

    async fn QdrantClient::collection_exists(self : QdrantClient, name : String) -> Bool

    Check whether a collection exists via GET /collections/{name}. Returns false when the server answers 404; raises on other errors.

    QdrantClient::collection_info

    async fn QdrantClient::collection_info(self : QdrantClient, name : String) -> CollectionInfo

    Get detailed information about a collection via GET /collections/{name}. Raises when the collection does not exist or the server reports an error.

    QdrantClient::count_points

    async fn QdrantClient::count_points(self : QdrantClient, name : String, filter? : Filter?, exact? : Bool) -> Int

    Count points in a collection via POST /collections/{name}/points/count.

    When exact is false (default) Qdrant answers with an estimate from the index; pass exact=true for an exact count, which is slower on large collections. An optional filter narrows the counted subset.

    QdrantClient::create_alias

    async fn QdrantClient::create_alias(self : QdrantClient, alias_name : String, collection : String) -> Unit

    Create an alias for a collection: POST /collections/aliases with a create_alias action.

    QdrantClient::create_collection

    async fn QdrantClient::create_collection(self : QdrantClient, name : String, config : CollectionConfig) -> Unit

    Create a collection via PUT /collections/{name}.

    QdrantClient::delete_alias

    async fn QdrantClient::delete_alias(self : QdrantClient, alias_name : String) -> Unit

    Delete an alias: POST /collections/aliases with a delete_alias action.

    QdrantClient::delete_collection

    async fn QdrantClient::delete_collection(self : QdrantClient, name : String) -> Unit

    Delete a collection via DELETE /collections/{name}. Raises when the collection does not exist or the server reports an error.

    QdrantClient::delete_points

    async fn QdrantClient::delete_points(self : QdrantClient, name : String, ids : Array[Int], wait? : Bool) -> Unit

    Delete points by id via POST /collections/{name}/points/delete. Pass wait=false to return immediately.

    QdrantClient::delete_points_by_filter

    async fn QdrantClient::delete_points_by_filter(self : QdrantClient, name : String, filter : Filter, wait? : Bool) -> Unit

    Delete points selected by a filter via POST /collections/{name}/points/delete.

    This is the bulk-delete variant: every point matching filter is removed in one operation, without listing ids first.

    QdrantClient::get_point

    async fn QdrantClient::get_point(self : QdrantClient, name : String, id : Int, with_payload? : Bool, with_vector? : Bool) -> PointStruct?

    Fetch a single point via GET /collections/{name}/points/{id}. Returns None when the point does not exist (404).

    QdrantClient::health

    async fn QdrantClient::health(self : QdrantClient) -> Bool

    Check the server health via GET /healthz. Returns true when the server reports status "ok".

    QdrantClient::list_aliases

    async fn QdrantClient::list_aliases(self : QdrantClient) -> Array[AliasInfo]

    List all collection aliases via GET /collections/aliases.

    QdrantClient::list_collections

    async fn QdrantClient::list_collections(self : QdrantClient) -> Array[String]

    List all collection names via GET /collections.

    QdrantClient::new

    fn QdrantClient::new(base_url : String, api_key? : String?, timeout_ms? : Int) -> QdrantClient

    Create a client for the given base URL.

    Example: QdrantClient::new("http://localhost:6333")

    api_key is optional and, when set, is sent as the api-key header expected by Qdrant's API-key authentication.

    QdrantClient::retrieve_points

    async fn QdrantClient::retrieve_points(self : QdrantClient, name : String, ids : Array[Int], with_payload? : Bool, with_vector? : Bool) -> Array[PointStruct]

    Fetch several points at once by id via POST /collections/{name}/points.

    Points that do not exist are simply omitted from the returned list; check the length against the requested ids to detect missing ones.

    QdrantClient::scroll_points

    async fn QdrantClient::scroll_points(self : QdrantClient, name : String, limit : Int, filter? : Filter?, offset? : Int?, with_payload? : Bool, with_vector? : Bool) -> ScrollResult

    Scroll through all points of a collection via POST /collections/{name}/points/scroll.

    Returns one page of points together with next_page_offset, which is Some(offset) when more pages remain and None on the last page. Pass it back as offset to fetch the following page.

    QdrantClient::search_points

    async fn QdrantClient::search_points(self : QdrantClient, name : String, vector : Array[Double], limit : Int, filter? : Json?, with_payload? : Bool, with_vector? : Bool) -> Array[ScoredPoint]

    Search the most similar points via POST /collections/{name}/points/search.

    limit bounds how many hits are returned. filter is an optional Qdrant filter JSON object, for example { "must": [{ "key": "tag", "match": { "value": "alpha" } }] }.

    QdrantClient::search_points_with_filter

    async fn QdrantClient::search_points_with_filter(self : QdrantClient, name : String, vector : Array[Double], limit : Int, filter : Filter, with_payload? : Bool, with_vector? : Bool) -> Array[ScoredPoint]

    Search with a type-safe Filter built from Condition helpers. This is the preferred way to build payload filters; the raw-Json search_points variant remains for advanced cases.

    QdrantClient::upsert_points

    async fn QdrantClient::upsert_points(self : QdrantClient, name : String, points : Array[PointStruct], wait? : Bool) -> Unit

    Upsert points into a collection via PUT /collections/{name}/points. Pass wait=false to return immediately; the operation may still be propagating on the server.

    QdrantClient::upsert_points_batch

    async fn QdrantClient::upsert_points_batch(self : QdrantClient, name : String, points : Array[PointStruct], wait? : Bool) -> Unit

    Upsert points through the batch endpoint via PUT /collections/{name}/points/batch. The batch format packs ids, vectors and payloads into parallel arrays, which Qdrant applies as a single request.

    ScoredPoint

    pub struct ScoredPoint {
    id : Int
    score : Double
    payload : Json?
    }

    One hit returned by the search endpoint: a point id, its similarity score, and the payload attached to the point (when requested).

    ScoredPoint::from_json

    fn ScoredPoint::from_json(json : Json) -> ScoredPoint?

    Parse a search hit object: { "id": 1, "score": 0.99, "payload": {...} }.

    ScrollResult

    pub struct ScrollResult {
    points : Array[PointStruct]
    next_page_offset : Int?
    }

    Result of POST /collections/{name}/points/scroll: the points on the current page, and the next page offset when more pages remain.

    ScrollResult::from_json

    fn ScrollResult::from_json(json : Json) -> ScrollResult?

    Parse the result object of a scroll response.

    VectorParams

    pub struct VectorParams {
    size : Int
    distance : Distance
    }

    Vector configuration for a collection, as accepted by PUT /collections/{name}.

    VectorParams::from_json

    fn VectorParams::from_json(json : Json) -> VectorParams?

    Parse VectorParams from the config fragment returned by GET /collections/{name}.

    VectorParams::new

    fn VectorParams::new(size : Int, distance : Distance) -> VectorParams

    VectorParams::to_json

    fn VectorParams::to_json(self : VectorParams) -> Json

    Convert to the JSON body fragment used by the Qdrant API: { "size": 4, "distance": "Cosine" }.

    count_request_body

    fn count_request_body(filter : Filter?, exact : Bool) -> Json

    Build the request body for POST /collections/{name}/points/count.

    join_url

    fn join_url(base : String, path : String) -> String

    Join a path fragment onto the base URL, keeping a single slash between them.

    parse_aliases

    fn parse_aliases(json : Json) -> Array[AliasInfo]?

    Parse the result.aliases array of GET /collections/aliases.

    parse_count_result

    fn parse_count_result(json : Json) -> Int?

    Parse the result object of a count response: { "count": 42 }. Returns None when the shape does not match.

    parse_retrieve_result

    fn parse_retrieve_result(json : Json) -> Array[PointStruct]?

    Parse the result array of a retrieve response into points. Returns None when the response does not match the expected shape.

    parse_search_result

    fn parse_search_result(json : Json) -> Array[ScoredPoint]?

    Parse the result array of a search response into scored points. Returns None when the response does not match the expected shape.

    scroll_request_body

    fn scroll_request_body(limit : Int, filter : Filter?, offset : Int?, with_payload : Bool, with_vector : Bool) -> Json

    Build the request body for POST /collections/{name}/points/scroll.

    vector_to_json

    fn vector_to_json(vector : Array[Double]) -> Json

    Convert a dense vector to its JSON array representation. Shared by point serialization, batch upsert and search.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io