syntax = "proto3";

package chroma;

import "chromadb/proto/chroma.proto";

message ScanOperator {
    Collection collection = 1;
    // Reserve for deprecated fields
    reserved 2, 3, 4;
    Segment knn = 5;
    Segment metadata = 6;
    Segment record = 7;
}

message FilterOperator {
    optional UserIds ids = 1;
    optional Where where = 2;
    optional WhereDocument where_document = 3;
}

message KNNOperator {
    repeated Vector embeddings = 1;
    uint32 fetch = 2;
}

message LimitOperator {
    uint32 offset = 1;
    optional uint32 limit = 2;
}

message ProjectionOperator {
    bool document = 1;
    bool embedding = 2;
    bool metadata = 3;
}

message KNNProjectionOperator {
    ProjectionOperator projection = 1;
    bool distance = 2;
}

message CountPlan {
    ScanOperator scan = 1;
}

message CountResult {
    uint32 count = 1;
    uint64 pulled_log_bytes = 2;
}

message GetPlan {
    ScanOperator scan = 1;
    FilterOperator filter = 2;
    LimitOperator limit = 3;
    ProjectionOperator projection = 4;
}

message ProjectionRecord {
    string id = 1;
    optional string document = 2;
    optional Vector embedding = 3;
    optional UpdateMetadata metadata = 4;
}

message GetResult {
    repeated ProjectionRecord records = 1;
    uint64 pulled_log_bytes = 2;
}

message KNNPlan {
    ScanOperator scan = 1;
    FilterOperator filter = 2;
    KNNOperator knn = 3;
    KNNProjectionOperator projection = 4;
}

message KNNProjectionRecord {
    ProjectionRecord record = 1;
    optional float distance = 2;
}

message KNNResult {
    repeated KNNProjectionRecord records = 1;
}

message KNNBatchResult {
    repeated KNNResult results = 1;
    uint64 pulled_log_bytes = 2;
}

message QueryVector {
    oneof vector {
        Vector dense = 1;
        SparseVector sparse = 2;
    }
}

// RankExpr represents a ranking expression
message RankExpr {
    message Knn {
        QueryVector query = 1;
        string key = 2;
        uint32 limit = 3;
        optional float default = 4;
        bool return_rank = 5;
    }

    message RankPair {
        RankExpr left = 1;
        RankExpr right = 2;
    }

    message RankList {
        repeated RankExpr exprs = 1;
    }

    oneof rank {
        RankExpr absolute = 1;
        RankPair division = 2;
        RankExpr exponentiation = 3;
        Knn knn = 4;
        RankExpr logarithm = 5;
        RankList maximum = 6;
        RankList minimum = 7;
        RankList multiplication = 8;
        RankPair subtraction = 9;
        RankList summation = 10;
        float value = 11;
    }
}

message RankOperator {
    optional RankExpr expr = 1;
}

message SelectOperator {
    repeated string keys = 1;
}

// Aggregation function for group by
message Aggregate {
    message MinK {
        repeated string keys = 1;
        uint32 k = 2;
    }

    message MaxK {
        repeated string keys = 1;
        uint32 k = 2;
    }

    oneof aggregate {
        MinK min_k = 1;
        MaxK max_k = 2;
    }
}

// Groups results by metadata keys and aggregates within each group
message GroupByOperator {
    repeated string keys = 1;
    Aggregate aggregate = 2;
}

message SearchPayload {
    FilterOperator filter = 1;
    RankOperator rank = 2;
    GroupByOperator group_by = 5;
    LimitOperator limit = 3;
    SelectOperator select = 4;
}

// ReadLevel specifies which data sources to read from during queries.
// This affects consistency vs performance tradeoffs.
enum ReadLevel {
    // Read from both the index and the write-ahead log (default).
    // Provides full consistency with all committed writes visible.
    INDEX_AND_WAL = 0;
    // Read only from the index, skipping the write-ahead log.
    // Provides eventual consistency - recent uncommitted writes may not be visible.
    INDEX_ONLY = 1;
}

message SearchPlan {
    ScanOperator scan = 1;
    repeated SearchPayload payloads = 2;
    // Specifies the read level for this query
    ReadLevel read_level = 3;
}

message SearchRecord {
    string id = 1;
    optional string document = 2;
    optional Vector embedding = 3;
    optional UpdateMetadata metadata = 4;
    optional float score = 5;
}

message SearchPayloadResult {
    repeated SearchRecord records = 1;
}

message SearchResult {
    repeated SearchPayloadResult results = 1;
    uint64 pulled_log_bytes = 2;
}

service QueryExecutor {
    rpc Count(CountPlan) returns (CountResult) {}
    rpc Get(GetPlan) returns (GetResult) {}
    rpc KNN(KNNPlan) returns (KNNBatchResult) {}
    rpc Search(SearchPlan) returns (SearchResult) {}
}
