OpenAPI 3.0 doc generation

This commit is contained in:
Andrey Vasnetsov
2020-10-26 17:44:31 +01:00
parent f50904c962
commit 3dd550b6b9
29 changed files with 801 additions and 63 deletions

45
Cargo.lock generated
View File

@@ -608,6 +608,7 @@ dependencies = [
"parking_lot 0.11.0",
"rand 0.7.3",
"rmp-serde",
"schemars",
"segment",
"serde 1.0.116",
"serde_cbor",
@@ -857,6 +858,12 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
[[package]]
name = "dyn-clone"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d55796afa1b20c2945ca8eabfc421839f2b766619209f1ede813cf2484f31804"
[[package]]
name = "either"
version = "1.5.3"
@@ -1798,6 +1805,7 @@ dependencies = [
"log 0.4.8",
"num_cpus",
"rand 0.7.3",
"schemars",
"segment",
"serde 1.0.116",
"serde_json",
@@ -2070,6 +2078,30 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "schemars"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "763f667253711994847f7e73befe859d6fff7bea2b7a7f01669d2c5b60765c37"
dependencies = [
"dyn-clone",
"schemars_derive",
"serde 1.0.116",
"serde_json",
]
[[package]]
name = "schemars_derive"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1d457e2e37415f32b7628ddc5a7fea06ef63bd029ed180d65166e87ca25ce21"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@@ -2086,6 +2118,7 @@ dependencies = [
"memmap 0.7.0",
"ordered-float",
"rmp-serde",
"schemars",
"serde 1.0.116",
"serde_cbor",
"serde_json",
@@ -2159,6 +2192,17 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_derive_internals"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dbab34ca63057a1f15280bdf3c39f2b1eb1b54c17e98360e511637aef7418c6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.53"
@@ -2344,6 +2388,7 @@ dependencies = [
"num_cpus",
"parking_lot 0.11.0",
"rand 0.7.3",
"schemars",
"segment",
"serde 1.0.116",
"sled",

View File

@@ -22,6 +22,7 @@ rand = "0.7.3"
serde = { version = "~1.0", features = ["derive"] }
serde_yaml = "~0.8"
serde_json = "~1.0"
schemars = "0.8.0"
itertools = "0.9"
config = "~0.10.1"
@@ -29,10 +30,16 @@ config = "~0.10.1"
actix-web = "3"
tokio = {version = "~0.3", features = ["full"]}
segment = {path = "lib/segment"}
collection = {path = "lib/collection"}
storage = {path = "lib/storage"}
[[bin]]
name = "schema_generator"
path = "src/schema_generator.rs"
test = false
bench = false
[workspace]
members = ["lib/*"]

View File

@@ -33,3 +33,4 @@ segment = {path = "../segment"}
itertools = "0.9"
indicatif = "0.15.0"
schemars = "0.8.0"

View File

@@ -5,9 +5,10 @@ use segment::types::SegmentConfig;
use crate::segment_manager::optimizers::merge_optimizer::MergeOptimizer;
use std::path::Path;
use serde::{Deserialize, Serialize};
use schemars::{JsonSchema};
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
pub struct OptimizersConfig {
pub deleted_threshold: f64,
pub vacuum_min_vector_number: usize,

View File

@@ -3,8 +3,9 @@ pub mod point_ops;
pub mod payload_ops;
use serde::{Deserialize, Serialize};
use schemars::{JsonSchema};
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
#[serde(untagged)]
pub enum CollectionUpdateOperations {

View File

@@ -2,11 +2,12 @@
use serde;
use serde::{Deserialize, Serialize};
use schemars::{JsonSchema};
use segment::types::{PointIdType, PayloadKeyType, PayloadType, GeoPoint};
use std::collections::HashMap;
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
#[serde(untagged)]
pub enum PayloadVariant<T> {
@@ -23,7 +24,7 @@ impl<T: Clone> PayloadVariant<T> {
}
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type", content = "value")]
pub enum PayloadInterface {
@@ -46,7 +47,7 @@ impl PayloadInterface {
/// Define operations description for point payloads manipulation
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum PayloadOps {
/// Overrides

View File

@@ -1,10 +1,11 @@
use serde::{Deserialize, Serialize};
use schemars::{JsonSchema};
use segment::types::{PointIdType, PayloadKeyType};
use crate::operations::types::VectorType;
use std::collections::HashMap;
use crate::operations::payload_ops::PayloadInterface;
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct PointStruct {
pub id: PointIdType,
@@ -13,7 +14,7 @@ pub struct PointStruct {
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum PointInsertOps {
#[serde(rename = "batch")]
@@ -27,7 +28,7 @@ pub enum PointInsertOps {
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum PointOps {
/// Insert or update points

View File

@@ -1,12 +1,13 @@
use segment::types::{VectorElementType, PointIdType, TheMap, PayloadKeyType, PayloadType, SeqNumberType, Filter, SearchParams, SegmentConfig};
use serde;
use serde::{Deserialize, Serialize};
use schemars::{JsonSchema};
/// Type of vector in API
pub type VectorType = Vec<VectorElementType>;
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct Record {
pub id: PointIdType,
@@ -14,7 +15,7 @@ pub struct Record {
pub vector: Option<Vec<VectorElementType>>,
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct CollectionInfo {
pub vectors_count: usize,
pub segments_count: usize,
@@ -24,7 +25,7 @@ pub struct CollectionInfo {
}
#[derive(Debug, Deserialize, Serialize, PartialEq)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum UpdateStatus {
Acknowledged,
@@ -32,7 +33,7 @@ pub enum UpdateStatus {
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct UpdateResult {
pub operation_id: SeqNumberType,
@@ -40,7 +41,7 @@ pub struct UpdateResult {
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct SearchRequest {
pub vector: Vec<VectorElementType>,

View File

@@ -23,3 +23,4 @@ thiserror = "1.0"
atomic_refcell = "0.1.6"
atomicwrites = "0.2.5"
memmap = "0.7.0"
schemars = "0.8.0"

View File

@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
use schemars::{JsonSchema};
use std::cmp::{Ordering};
use ordered_float::OrderedFloat;
use std::collections::{BTreeMap, HashSet};
@@ -17,7 +18,7 @@ pub type TagType = u64;
pub type VectorElementType = f64;
/// Type of internal tags, build from payload
#[derive(Debug, Deserialize, Serialize, Clone, Copy)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Copy)]
pub enum Distance {
Cosine,
Euclid,
@@ -29,7 +30,7 @@ pub enum Order {
SmallBetter,
}
#[derive(Deserialize, Serialize, Copy, Clone, PartialEq, Debug)]
#[derive(Deserialize, Serialize, JsonSchema, Copy, Clone, PartialEq, Debug)]
pub struct ScoredPoint {
pub idx: PointIdType,
pub score: ScoreType,
@@ -50,7 +51,7 @@ impl PartialOrd for ScoredPoint {
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum SegmentType {
/// Segment cheap insert & delete operations
@@ -62,7 +63,7 @@ pub enum SegmentType {
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq)]
#[serde(rename_all = "snake_case")]
pub struct SegmentInfo {
pub segment_type: SegmentType,
@@ -74,7 +75,7 @@ pub struct SegmentInfo {
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, Copy, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum SearchParams {
Hnsw {
@@ -91,7 +92,7 @@ pub fn distance_order(distance: &Distance) -> Order {
}
}
#[derive(Debug, Deserialize, Serialize, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Copy, Clone, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type", content = "options")]
pub enum Indexes {
@@ -109,7 +110,7 @@ impl Default for Indexes {
}
#[derive(Debug, Deserialize, Serialize, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Copy, Clone, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type", content = "options")]
pub enum StorageType {
@@ -124,7 +125,7 @@ impl Default for StorageType {
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(rename_all = "snake_case")]
pub struct SegmentConfig {
pub vector_size: usize,
@@ -133,21 +134,21 @@ pub struct SegmentConfig {
pub storage_type: StorageType
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(rename_all = "snake_case")]
pub struct SegmentState {
pub version: SeqNumberType,
pub config: SegmentConfig,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(rename_all = "snake_case")]
pub struct GeoPoint {
pub lon: f64,
pub lat: f64,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type", content = "value")]
pub enum PayloadType {
@@ -158,7 +159,7 @@ pub enum PayloadType {
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(rename_all = "snake_case")]
pub struct Match {
pub key: PayloadKeyType,
@@ -166,7 +167,7 @@ pub struct Match {
pub integer: Option<i64>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(rename_all = "snake_case")]
pub struct Range {
pub key: PayloadKeyType,
@@ -176,7 +177,7 @@ pub struct Range {
pub lte: Option<f64>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(rename_all = "snake_case")]
pub struct GeoBoundingBox {
pub key: PayloadKeyType,
@@ -185,7 +186,7 @@ pub struct GeoBoundingBox {
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(rename_all = "snake_case")]
pub enum Condition {
Filter(Filter),
@@ -195,7 +196,7 @@ pub enum Condition {
HasId(HashSet<PointIdType>),
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
#[serde(rename_all = "snake_case")]
pub struct Filter {
pub should: Option<Vec<Condition>>,

View File

@@ -16,7 +16,7 @@ rand = "0.7.3"
wal = { git = "https://github.com/generall/wal.git" }
tokio = {version = "~0.3", features = ["rt-multi-thread"]}
serde = { version = "~1.0", features = ["derive"] }
schemars = "0.8.0"
segment = {path = "../segment"}

View File

@@ -1,7 +1,9 @@
use serde::{Deserialize, Serialize};
use schemars::{JsonSchema};
use segment::types::{Distance, Indexes};
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AliasOperations {
CreateAlias {
collection_name: String,
@@ -16,21 +18,19 @@ pub enum AliasOperations {
},
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum StorageOps {
/// Create new collection and (optionally) specify index params
CreateCollection {
name: String,
dim: usize,
vector_size: usize,
distance: Distance,
index: Option<Indexes>,
},
/// Drop collection
DeleteCollection {
collection_name: String,
},
/// Perform changes of index aliases
/// Delete collection with given name
DeleteCollection(String),
/// Perform changes of collection aliases
ChangeAliases {
actions: Vec<AliasOperations>,
}

View File

@@ -153,7 +153,7 @@ impl TableOfContent {
match operation {
StorageOps::CreateCollection {
name: collection_name,
dim,
vector_size,
distance,
index
} => {
@@ -168,7 +168,7 @@ impl TableOfContent {
let segment_config = SegmentConfig {
vector_size: dim,
vector_size,
index: index.unwrap_or(Default::default()),
distance,
storage_type: Default::default(),
@@ -187,7 +187,7 @@ impl TableOfContent {
write_collections.insert(collection_name, Arc::new(segment));
Ok(true)
}
StorageOps::DeleteCollection { collection_name } => {
StorageOps::DeleteCollection(collection_name) => {
let removed = self.collections.write().remove(&collection_name).is_some();
if removed {
let path = self.get_collection_path(&collection_name);

View File

@@ -1,22 +1,23 @@
use serde::{Deserialize, Serialize};
use schemars::{JsonSchema};
use collection::collection_builder::optimizers_builder::OptimizersConfig;
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
pub struct PerformanceConfig {
pub max_search_threads: usize,
pub max_optimize_threads: usize,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
pub struct WalConfig {
pub wal_capacity_mb: usize,
pub wal_segments_ahead: usize,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
pub struct StorageConfig {
pub storage_path: String,
pub optimizers: OptimizersConfig,

187
openapi/openapi.yaml Normal file
View File

@@ -0,0 +1,187 @@
openapi: 3.0.1
info:
title: Qdrant API
description: "API description for Qdrant vector search engine. Describes CRUD and search operations on collections of points (vectors with payload)."
contact:
email: andrey@vasnetsov.com
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
version: 0.1.0
externalDocs:
description: Find out more about Qdrant
url: https://github.com/generall/qdrant
servers:
- url: http://localhost:6333/
tags:
- name: collection
description: Searchable collection of points.
- name: point
description: Float-point vector with payload.
paths:
/collections:
get:
tags:
- collection
summary: Get list of existing collections
operationId: get_collections
responses:
200:
description: successful operation
content:
application/json:
schema:
type: object
properties:
time:
type: number
format: float
description: Time spent to process this request
status:
type: string
enum: ["Ok"]
result:
$ref: "./models.json#/definitions/CollectionsResponse"
default:
description: error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
post:
tags:
- collection
summary: Perform update operation on collections
operationId: update_collections
requestBody:
description: Operation to perform on collections
content:
application/json:
schema:
$ref: "./models.json#/definitions/StorageOps"
responses:
200:
description: successful operation
content:
application/json:
schema:
type: object
properties:
time:
type: number
format: float
description: Time spent to process this request
status:
type: string
enum: ["Ok"]
result:
type: boolean
default:
description: error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/collections/{name}:
get:
tags:
- collection
summary: Get information about existing collection
operationId: get_collection
parameters:
- name: name
in: path
description: Name of the collection to retrieve
required: true
schema:
type: string
responses:
200:
description: successful operation
content:
application/json:
schema:
type: object
properties:
time:
type: number
format: float
description: Time spent to process this request
status:
type: string
enum: ["Ok"]
result:
$ref: "./models.json#/definitions/CollectionInfo"
default:
description: error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/collections/{name}/vectors/{id}:
get:
tags:
- point
summary: Retrieve point by id
operationId: get_point
parameters:
- name: name
in: path
description: Name of the collection to retrieve from
required: true
schema:
type: string
- name: id
in: path
description: Id of the point
required: true
schema:
type: integer
responses:
200:
description: successful operation
content:
application/json:
schema:
type: object
properties:
time:
type: number
format: float
description: Time spent to process this request
status:
type: string
enum: ["Ok"]
result:
$ref: "./models.json#/definitions/Record"
default:
description: error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
components:
schemas:
ErrorResponse:
type: object
properties:
time:
type: number
format: float
description: Time spent to process this request
status:
type: object
properties:
error:
type: string
description: Description of the occurred error.
result:
type: object
nullable: true

0
openapi/schemas/.keep Normal file
View File

View File

@@ -1,14 +1,15 @@
use serde;
use serde::{Deserialize, Serialize};
use schemars::{JsonSchema};
use std::fmt::Debug;
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct CollectionDescription {
pub name: String
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct CollectionsResponse {
pub collections: Vec<CollectionDescription>

View File

@@ -3,37 +3,49 @@ use storage::content_manager::toc::TableOfContent;
use crate::common::helpers::process_response;
use actix_web::rt::time::Instant;
use segment::types::PointIdType;
use serde::{Deserialize};
use serde::{Deserialize, Serialize};
use schemars::{JsonSchema};
use storage::content_manager::errors::StorageError;
use collection::operations::types::Record;
#[derive(Deserialize)]
pub struct VectorRequest {
#[derive(Deserialize, Serialize, JsonSchema)]
pub struct PointRequest {
pub ids: Vec<PointIdType>
}
#[get("/collections/{name}/vectors/{id}")]
pub async fn get_vector(
#[get("/collections/{name}/points/{id}")]
pub async fn get_point(
toc: web::Data<TableOfContent>,
web::Path((name, vector_ids)): web::Path<(String, PointIdType)>,
web::Path((name, point_id)): web::Path<(String, PointIdType)>,
) -> impl Responder {
let timing = Instant::now();
let response = {
toc.get_collection(&name)
.and_then(|collection| collection
.retrieve(&vec![vector_ids], true, true)
.retrieve(&vec![point_id], true, true)
.map_err(|err| err.into())
.map(|points| points.into_iter().next())
)
};
let response = match response {
Ok(record) => match record {
None => Err(StorageError::NotFound { description: format!("Point with id {} does not exists!", point_id) }),
Some(record) => Ok(record)
},
Err(e) => Err(e)
};
process_response(response, timing)
}
#[post("/collections/{name}/vectors")]
#[post("/collections/{name}/points")]
pub async fn get_vectors(
toc: web::Data<TableOfContent>,
web::Path(name): web::Path<String>,
request: web::Json<VectorRequest>,
request: web::Json<PointRequest>,
) -> impl Responder {
let timing = Instant::now();

View File

@@ -5,7 +5,7 @@ use actix_web::rt::time::Instant;
use std::sync::Arc;
use collection::operations::types::SearchRequest;
#[post("/collections/{name}/vectors/search")]
#[post("/collections/{name}/points/search")]
pub async fn search_vectors(
toc: web::Data<TableOfContent>,
web::Path(name): web::Path<String>,

View File

@@ -4,10 +4,10 @@ use actix_web::rt::time::Instant;
use crate::common::helpers::process_response;
use collection::operations::CollectionUpdateOperations;
use actix_web::web::Query;
use serde::{Deserialize};
use serde::{Deserialize, Serialize};
use schemars::{JsonSchema};
#[derive(Deserialize)]
#[derive(Deserialize, Serialize, JsonSchema)]
pub struct UpdateParam {
pub wait: Option<bool>
}

View File

@@ -1,7 +1,8 @@
use serde::{Serialize, Deserialize};
use schemars::{JsonSchema};
use std::fmt::Debug;
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ApiStatus {
Ok,
@@ -9,7 +10,7 @@ pub enum ApiStatus {
}
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct ApiResponse<D: Serialize + Debug> {
pub result: Option<D>,

View File

@@ -14,7 +14,7 @@ use env_logger;
use storage::content_manager::toc::TableOfContent;
use crate::api::collections_api::{get_collections, update_collections, get_collection};
use crate::api::update_api::update_vectors;
use crate::api::retrieve_api::{get_vectors, get_vector};
use crate::api::retrieve_api::{get_vectors, get_point};
use crate::api::search_api::search_vectors;
@@ -57,7 +57,7 @@ async fn main() -> std::io::Result<()> {
.service(update_collections)
.service(get_collection)
.service(update_vectors)
.service(get_vector)
.service(get_point)
.service(get_vectors)
.service(search_vectors)
;

44
src/schema_generator.rs Normal file
View File

@@ -0,0 +1,44 @@
mod common;
mod api;
use schemars::{schema_for, JsonSchema};
use serde_json;
use std::path::{Path};
use std::fs::File;
use std::io::Write;
use crate::api::models::CollectionsResponse;
use crate::api::retrieve_api::PointRequest;
use collection::operations::types::{CollectionInfo, Record, SearchRequest, UpdateResult};
use storage::content_manager::storage_ops::StorageOps;
use serde::{Deserialize, Serialize};
use segment::types::ScoredPoint;
use collection::operations::CollectionUpdateOperations;
#[derive(Deserialize, Serialize, JsonSchema)]
struct AllDefinitions {
a1: CollectionsResponse,
a2: CollectionInfo,
a3: StorageOps,
a4: PointRequest,
a5: Record,
a6: SearchRequest,
a7: ScoredPoint,
a8: UpdateResult,
a9: CollectionUpdateOperations,
}
fn save_schema<T:JsonSchema>(path: &Path) {
let schema = schema_for!(T);
let mut file = File::create(path).unwrap();
let schema_str = serde_json::to_string_pretty(&schema).unwrap();
file.write_all(schema_str.as_bytes()).unwrap();
}
fn main() {
save_schema::<AllDefinitions>("./openapi/schemas/AllDefinitions.json".as_ref());
}

View File

@@ -0,0 +1,26 @@
#!/bin/bash
# This script generate model definitions for OpenAPI 3.0 documentation
set -e
# Ensure current path is project root
cd "$(dirname "$0")/../"
cargo run --package qdrant --bin schema_generator
(
cd tools/schema2openapi/
docker build . --tag schema2openapi
)
docker run --rm \
-v $(pwd)/openapi/schemas/AllDefinitions.json:/app/schema.json \
schema2openapi | sed -e 's/"type": "null"/"nullable": true/g' >./openapi/models.json
# (cd tools/openapi-merge/ ; docker build . --tag merge-openapi)
echo "WARNING: This file is auto-generated. Do NOT edit it manually!" >./openapi/openapi-merged.yaml
docker run \
-v $(pwd)/openapi:/project \
wework/speccy resolve /project/openapi.yaml >>./openapi/openapi-merged.yaml

View File

@@ -0,0 +1,9 @@
FROM node:lts-alpine
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "convert.js"]

View File

@@ -0,0 +1,16 @@
const toOpenApi = require('@openapi-contrib/json-schema-to-openapi-schema');
const fs = require('fs');
let rawdata = fs.readFileSync('schema.json');
let schema = JSON.parse(rawdata);
(async () => {
const convertedSchema = await toOpenApi(schema);
console.log(JSON.stringify(convertedSchema, null, 4));
})();

149
tools/schema2openapi/package-lock.json generated Normal file
View File

@@ -0,0 +1,149 @@
{
"name": "tools",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@cloudflare/json-schema-walker": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@cloudflare/json-schema-walker/-/json-schema-walker-0.1.1.tgz",
"integrity": "sha512-P3n0hEgk1m6uKWgL4Yb1owzXVG4pM70G4kRnDQxZXiVvfCRtaqiHu+ZRiRPzmwGBiLTO1LWc2yR1M8oz0YkXww=="
},
"@openapi-contrib/json-schema-to-openapi-schema": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@openapi-contrib/json-schema-to-openapi-schema/-/json-schema-to-openapi-schema-1.2.0.tgz",
"integrity": "sha512-oCxl/vecpA25UzI2zg11ekz9vEaXYEVtwC5krbjZw38qPUsvuYLjs/llFSjn9LjxPA+eMEARcXG/BmZZDLrt8w==",
"requires": {
"@cloudflare/json-schema-walker": "^0.1.1",
"@stoplight/json-ref-resolver": "^2.3.0",
"@stoplight/yaml": "^3.7.1",
"node-fetch": "^2.6.0",
"tslib": "^1.11.1"
}
},
"@stoplight/json": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.9.0.tgz",
"integrity": "sha512-jgEKIPMLbhaU5Nw9yw+VBw2OSfDxm8VQ/k5JNezAuDMjcmqCPz3rOQTVNOROStzi70l4DRxF7eBN9liYJq5Ojw==",
"requires": {
"@stoplight/ordered-object-literal": "^1.0.1",
"@stoplight/types": "^11.9.0",
"jsonc-parser": "~2.2.1",
"lodash": "^4.17.15",
"safe-stable-stringify": "^1.1"
}
},
"@stoplight/json-ref-resolver": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@stoplight/json-ref-resolver/-/json-ref-resolver-2.4.1.tgz",
"integrity": "sha512-y9G0BybShiJ/1NaPPG1BPelL2zI8/0rKXRtUpD2JBHEb72L9n4Bin85+MfrHYoHeJxojDpewsJA3FVRnUVL1dw==",
"requires": {
"@stoplight/json": "^3.1.2",
"@stoplight/path": "^1.3.0",
"@stoplight/types": "^11.0.0",
"@types/urijs": "1.x.x",
"dependency-graph": "~0.8.0",
"fast-memoize": "^2.5.1",
"immer": "^3.2.0",
"lodash": "^4.17.15",
"tslib": "^1.10.0",
"urijs": "~1.19.1"
}
},
"@stoplight/ordered-object-literal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.1.tgz",
"integrity": "sha512-kDcBIKwzAXZTkgzaiPXH2I0JXavBkOb3jFzYNFS5cBuvZS3s/K+knpk2wLVt0n8XrnRQsSffzN6XG9HqUhfq6Q=="
},
"@stoplight/path": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@stoplight/path/-/path-1.3.2.tgz",
"integrity": "sha512-lyIc6JUlUA8Ve5ELywPC8I2Sdnh1zc1zmbYgVarhXIp9YeAB0ReeqmGEOWNtlHkbP2DAA1AL65Wfn2ncjK/jtQ=="
},
"@stoplight/types": {
"version": "11.9.0",
"resolved": "https://registry.npmjs.org/@stoplight/types/-/types-11.9.0.tgz",
"integrity": "sha512-4bzPpWZobt0e+d0OtALCJyl1HGzKo6ur21qxnId9dTl8v3yeD+5HJKZ2h1mv7e94debH5QDtimMU80V6jbXM8Q==",
"requires": {
"@types/json-schema": "^7.0.4",
"utility-types": "^3.10.0"
}
},
"@stoplight/yaml": {
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-3.8.1.tgz",
"integrity": "sha512-wbhcgo7dTjwjjwFziC/SAcQlwPucYhYq6vjzyOjj8zeOVnnmoa7hzU1i9Kj31473FG/re7xtt6j3LWu2VnYbxg==",
"requires": {
"@stoplight/ordered-object-literal": "^1.0.1",
"@stoplight/types": "^11.1.1",
"@stoplight/yaml-ast-parser": "0.0.45",
"lodash": "^4.17.15"
}
},
"@stoplight/yaml-ast-parser": {
"version": "0.0.45",
"resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.45.tgz",
"integrity": "sha512-0MTEvgp3XMdeMUSTCGiNECuC+YlLbzytDEIOJVDHrrmzVZpIR3gGnHI6mmPI4P7saPxUiHxFF2uuoTuCNlKjrw=="
},
"@types/json-schema": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw=="
},
"@types/urijs": {
"version": "1.19.12",
"resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.12.tgz",
"integrity": "sha512-+BmVXyxXCmGuS177d6yj9zt3tBugh35ZIPgYTWctDf2/LwGirIWIyaH01dmTzxjro6LNiaYIIj3jveKetMmZ1A=="
},
"dependency-graph": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.8.1.tgz",
"integrity": "sha512-g213uqF8fyk40W8SBjm079n3CZB4qSpCrA2ye1fLGzH/4HEgB6tzuW2CbLE7leb4t45/6h44Ud59Su1/ROTfqw=="
},
"fast-memoize": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz",
"integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw=="
},
"immer": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/immer/-/immer-3.3.0.tgz",
"integrity": "sha512-vlWRjnZqoTHuEjadquVHK3GxsXe1gNoATffLEA8Qbrdd++Xb+wHEFiWtwAKTscMBoi1AsvEMXhYRzAXA8Ex9FQ=="
},
"jsonc-parser": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz",
"integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w=="
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
},
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"safe-stable-stringify": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz",
"integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw=="
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"urijs": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.2.tgz",
"integrity": "sha512-s/UIq9ap4JPZ7H1EB5ULo/aOUbWqfDi7FKzMC2Nz+0Si8GiT1rIEaprt8hy3Vy2Ex2aJPpOQv4P4DuOZ+K1c6w=="
},
"utility-types": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz",
"integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg=="
}
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "tools",
"version": "1.0.0",
"description": "",
"main": "convert.js",
"dependencies": {
"@openapi-contrib/json-schema-to-openapi-schema": "^1.2.0"
},
"devDependencies": {},
"scripts": {
"convert": "node convert.js"
},
"author": "",
"license": "ISC"
}

View File

@@ -0,0 +1,217 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "StorageOps",
"anyOf": [
{
"description": "Create new collection and (optionally) specify index params",
"type": "object",
"required": [
"create_collection"
],
"properties": {
"create_collection": {
"type": "object",
"required": [
"distance",
"name",
"vector_size"
],
"properties": {
"distance": {
"$ref": "#/definitions/Distance"
},
"index": {
"anyOf": [
{
"$ref": "#/definitions/Indexes"
},
{
"type": "null"
}
]
},
"name": {
"type": "string"
},
"vector_size": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
}
}
},
{
"description": "Delete collection with given name",
"type": "object",
"required": [
"delete_collection"
],
"properties": {
"delete_collection": {
"type": "string"
}
}
},
{
"description": "Perform changes of collection aliases",
"type": "object",
"required": [
"change_aliases"
],
"properties": {
"change_aliases": {
"type": "object",
"required": [
"actions"
],
"properties": {
"actions": {
"type": "array",
"items": {
"$ref": "#/definitions/AliasOperations"
}
}
}
}
}
}
],
"definitions": {
"AliasOperations": {
"anyOf": [
{
"type": "object",
"required": [
"create_alias"
],
"properties": {
"create_alias": {
"type": "object",
"required": [
"alias_name",
"collection_name"
],
"properties": {
"alias_name": {
"type": "string"
},
"collection_name": {
"type": "string"
}
}
}
}
},
{
"type": "object",
"required": [
"delete_alias"
],
"properties": {
"delete_alias": {
"type": "object",
"required": [
"alias_name"
],
"properties": {
"alias_name": {
"type": "string"
}
}
}
}
},
{
"type": "object",
"required": [
"rename_alias"
],
"properties": {
"rename_alias": {
"type": "object",
"required": [
"new_alias_name",
"old_alias_name"
],
"properties": {
"new_alias_name": {
"type": "string"
},
"old_alias_name": {
"type": "string"
}
}
}
}
}
]
},
"Distance": {
"description": "Type of internal tags, build from payload",
"type": "string",
"enum": [
"Cosine",
"Euclid",
"Dot"
]
},
"Indexes": {
"anyOf": [
{
"type": "object",
"required": [
"options",
"type"
],
"properties": {
"options": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"plain"
]
}
}
},
{
"type": "object",
"required": [
"options",
"type"
],
"properties": {
"options": {
"type": "object",
"required": [
"ef_construct",
"m"
],
"properties": {
"ef_construct": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"m": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
},
"type": {
"type": "string",
"enum": [
"hnsw"
]
}
}
}
]
}
}
}