#vector-database #database-client #vector #transaction #weaviate

weaviate-community

Community client for handling Weaviate vector database transactions written in Rust, for Rust

3 releases

0.2.2 Feb 17, 2024
0.2.1 Feb 10, 2024
0.2.0 Nov 25, 2023
0.1.0 Oct 4, 2023

#397 in Database interfaces

Download history 7/week @ 2023-12-17 19/week @ 2023-12-24 7/week @ 2023-12-31 46/week @ 2024-01-07 4/week @ 2024-01-14 10/week @ 2024-01-28 2/week @ 2024-02-04 104/week @ 2024-02-11 43/week @ 2024-02-18 25/week @ 2024-02-25 1/week @ 2024-03-10 61/week @ 2024-03-31

62 downloads per month

MIT license

345KB
5.5K SLoC

Weaviate Community

Crates.io CircleCI License

Community client for handling Weaviate vector database transactions written in Rust, for Rust.

More information on Weaviate can be found on the official Weaviate webpage.

Installation

Run the following in your project directory

cargo add weaviate-community

or add the following to your Cargo.toml file

weaviate-community = "0.2.2"

Documentation

The library reference documentation can be found here

Usage

Below are some examples on how to interact with the Weaviate community Rust client.

Creating a new WeaviateClient

use std::error::Error;
use weaviate_community::WeaviateClient;
use weaviate_community::collections::auth::AuthApiKey;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // With anonymous access
    let client = WeaviateClient::builder("http://localhost:8080")
        .build()?;
    
    // With Auth key
    let client = WeaviateClient::builder("http://localhost:8080")
        .with_auth_secret("your-key")
        .build()?;

    // With multiple other API key (eg, OpenAI, JinaAI, ..)
    let client = WeaviateClient::builder("http://localhost:8080")
        .with_auth_secret("your-key")
        .with_api_key("X-OpenAI-Api-Key", "abcdefg")
        .with_api_key("X-Jinaai-Api-Key", "hijklmn")
        .build()?;

    Ok(())
}

Schema endpoints

use weaviate_community::collections::schema::{
    Class,
    Property,
    ShardStatus,
    Tenants,
    Tenant,
    ActivityStatus
};

async fn schema_endpoints(client: WeaviateClient) -> Result<(), Box<dyn Error>> {
    // Get full schema
    let res = client.schema.get().await?;

    // Get the schema for a single class
    let res = client.schema.get_class("Article").await?;

    // Create a new class in the schema
    let my_class = Class::builder("Article").with_description("News article").build();
    let res = client.schema.create_class(&my_class).await?;

    // Update a class in the schema
    let my_class = Class::builder("Article").with_description("Updated information").build();
    let res = client.schema.update(&my_class).await?;

    // Add a property to a class
    let property = Property::builder("title", vec!["text"]).build();
    let res = client.schema.add_property("Article", &property).await?;

    // Get the shards for a class
    let res = client.schema.get_shards("Article").await?;

    // Update a class shard
    let res = client.schema.update_class_shard("Article", "abcdefg", ShardStatus::READONLY).await?;

    // List tenants for a class
    let res = client.schema.list_tenants("Article").await?;

    // Add tenants to a class
    let tenants = Tenants::new(vec![Tenant::builder("TENANT_B").build()]);
    let res = client.schema.add_tenants("Article", &tenants).await?;

    // Update tenants
    let tenants = Tenants::new(
        vec![
            Tenant::builder("TENANT_B").with_activity_status(ActivityStatus::COLD).build()
        ]
    );
    let res = client.schema.update_tenants("Article", &tenants).await?;

    // Remove tenants from a class
    let tenants = vec!["TENANT_B"];
    let res = client.schema.add_tenants("Article", &tenants).await?;

    // Delete a class from the schema
    let res = client.schema.delete("Article").await?;

    Ok(())
}

Objects endpoints

use uuid::Uuid;
use weaviate_community::collections::objects::{Object, ObjectListParameters};

async fn objects_endpoints(client: WeaviateClient) -> Result<(), Box<dyn Error>> {
    // List every single object
    let res = client.objects.list(ObjectListParameters::new()).await?;

    // List all objects for a single class
    let params = ObjectListParameters::builder().with_class_name("Article").build();
    let res = client.objects.list(params).await?;

    // Create a new object
    let my_object = Object::builder("Article", serde_json::json!({})).build();
    let res = client.objects.create(&my_object, None).await?;

    // Get an object based on its UUID
    let uuid = Uuid::new_v4();
    let res = client.objects.get("Article", uuid, None, None, None).await?;

    // Check if a data object exists
    let uuid = Uuid::new_v4();
    let res = client.objects.exists("Article", uuid, None, None).await?;

    // Update a data object
    let uuid = Uuid::parse_str("ee22d1b8-3b95-4e94-96d5-9a2b60fbd303")?;
    let properties = serde_json::json!({
        "title": "new title",
    });
    let res = client.objects.update(&properties, "Article", &uuid, None).await?;

    // Replace a data object
    let uuid = Uuid::parse_str("ee22d1b8-3b95-4e94-96d5-9a2b60fbd303")?;
    let properties = serde_json::json!({
        "properties": {
            "author": "Jodi Kantor",
        }
    });
    let res = client.objects.replace(&properties, "Publication", &uuid, None).await?;

    // Delete a data object
    let uuid = Uuid::parse_str("ee22d1b8-3b95-4e94-96d5-9a2b60fbd303")?;
    let res = client.objects.delete("Article", &uuid, None, None).await?;

    // Validate a data object
    let properties = serde_json::json!({
        "name": "New York Times"
    });
    let uuid = Uuid::parse_str("12345678-1234-1234-1234-123456789012")?;
    let res = client.objects.validate("Publication", &properties, &uuid).await?;

    // Add a cross-reference
    let uuid1 = Uuid::parse_str("12345678-1234-1234-1234-123456789012")?;
    let uuid2 = Uuid::parse_str("20ffc68d-986b-5e71-a680-228dba18d7ef")?;
    let reference = Reference::new(
        "JeopardyQuestion", 
        &uuid1,
        "hasCategory", 
        "JeopardyCategory",
        &uuid2,
    );
    let res = client.objects.reference_add(reference).await?;

    // Update a cross-reference
    let uuid1 = Uuid::parse_str("12345678-1234-1234-1234-123456789012")?;
    let uuid2 = Uuid::parse_str("20ffc68d-986b-5e71-a680-228dba18d7ef")?;
    let res = client.objects.reference_update(
        "JeopardyQuestion", 
        &uuid1,
        "hasCategory", 
        vec!["JeopardyCategory"],
        vec![&uuid2],
        None,
        None
    ).await?;

    // Delete a cross-reference
    let uuid1 = Uuid::parse_str("12345678-1234-1234-1234-123456789012")?;
    let uuid2 = Uuid::parse_str("20ffc68d-986b-5e71-a680-228dba18d7ef")?;
    let reference = Reference::new(
        "JeopardyQuestion", 
        &uuid1,
        "hasCategory", 
        "JeopardyCategory",
        &uuid2,
    );
    let res = client.objects.reference_delete(reference).await?;

    Ok(())
}

Backups endpoints

use weaviate_community::collections::backups::{
    BackupCreateRequest,
    BackupRestoreRequest,
    BackupBackends
};

async fn backups_endpoints(client: WeaviateClient) -> Result<(), Box<dyn Error>> {
    // Create a new backup - with wait for completion
    let req = BackupCreateRequest::builder("my-backup").build();
    let res = client.backups.create(BackupBackends::FILESYSTEM, req, true).await?;

    // Create a new backup - without wait for completion
    let req = BackupCreateRequest::builder("my-backup").build();
    let res = client.backups.create(BackupBackends::FILESYSTEM, req, false).await?;

    // Get the status of a backup create
    let res = client.backups.get_backup_status(
        BackupBackends::FILESYSTEM,
        "my-backup",
        false
    ).await?;

    // Restore a backup - with wait for completion
    let req = BackupRestoreRequest::builder().build();
    let res = client.backups.restore(BackupBackends::FILESYSTEM, "my-backup", req, true).await?;

    // Restore a backup - without wait for completion
    let req = BackupRestoreRequest::builder().build();
    let res = client.backups.restore(BackupBackends::FILESYSTEM, "my-backup", req, false).await?;

    // Get the status of a backup restore
    let res = client.backups.get_backup_status(
        BackupBackends::FILESYSTEM,
        "my-backup",
        true
    ).await?;

    Ok(())
}

Batch endpoints

use uuid::Uuid;
use weaviate_community::collections::objects::{
    Object, 
    MultiObject,
    Reference, 
    References, 
    ConsistencyLevel,
};
use weaviate_community::collections::batch::{BatchDeleteRequest, MatchConfig};
async fn batch_endpoints(client: WeaviateClient) -> Result<(), Box<dyn Error>> {
    // Batch add objects
    let author_uuid = Uuid::parse_str("36ddd591-2dee-4e7e-a3cc-eb86d30a4303").unwrap();
    let article_a_uuid = Uuid::parse_str("6bb06a43-e7f0-393e-9ecf-3c0f4e129064").unwrap();
    let article_b_uuid = Uuid::parse_str("b72912b9-e5d7-304e-a654-66dc63c55b32").unwrap();

    let article_a = Object::builder("Article", serde_json::json!({}))
        .with_id(article_a_uuid.clone())
        .build();

    let article_b = Object::builder("Article", serde_json::json!({}))
        .with_id(article_b_uuid.clone())
        .build();

    let author = Object::builder("Author", serde_json::json!({}))
        .with_id(author_uuid.clone())
        .build();

    let res = client.batch.objects_batch_add(
        MultiObjects::new(
            vec![article_a, article_b, author]),
            Some(ConsistencyLevel::ALL),
            None
    ).await?;

    // Batch delete objects
    let req = BatchDeleteRequest::builder(
        MatchConfig::new(
            "Article",
            serde_json::json!({
                "operator": "Like",
                "path": ["id"],
                "valueText": "*4*",
            })
        )
    ).build();
    let res = client.batch.objects_batch_delete(
        req,
        Some(ConsistencyLevel::ALL),
        None
    ).await?;

    // Batch add references
    let references = References::new(vec![
        Reference::new(
            "Author",
            &author_uuid,
            "wroteArticles",
            "Article",
            &article_a_uuid,
        ),
        Reference::new(
            "Author",
            &author_uuid,
            "wroteArticles",
            "Article",
            &article_b_uuid,
        ),
    ]);
    let res = client.batch.references_batch_add(
        references,
        Some(ConsistencyLevel::ALL),
        None
    ).await?;
    
    Ok(())
}

Meta endpoint

async fn meta_endpoint(client: WeaviateClient) -> Result<(), Box<dyn Error>> {
    // Get database metadata
    let res = client.meta.get_meta().await?;

    Ok(())
}

Nodes endpoint

async fn nodes_endpoint(client: WeaviateClient) -> Result<(), Box<dyn Error>> {
    // Get the nodes status'
    let res = client.nodes.get_nodes_status().await?;

    Ok(())
}

OIDC endpoint

async fn oidc_endpoint(client: WeaviateClient) -> Result<(), Box<dyn Error>> {
    // Get the OIDC config
    let res = client.oidc.get_open_id_configuration().await?;

    Ok(())
}

Querying

use weaviate_community::collections::query::{
    GetQuery,
    AggregateQuery,
    ExploreQuery,
    RawQuery
};
async fn querying(client: WeaviateClient) -> Result<(), Box<dyn Error>> {
    // Get
    let query = GetQuery::builder(
        "JeopardyQuestion", 
        vec![
            "question",
            "answer",
            "points",
            "hasCategory { ... on JeopardyCategory { title }}"
        ])
        .with_limit(1)
        .with_additional(vec!["id"])
        .build();
    let res = client.query.get(query).await?;

    // Aggregate
    let query = AggregateQuery::builder("Article")
        .with_meta_count()
        .with_fields(vec!["wordCount {count maximum mean median minimum mode sum type}"])
        .build();
    let res = client.query.aggregate(query).await?;

    // Explore
    let query = ExploreQuery::builder()
        .with_limit(1)
        .with_near_vector("{vector: [-0.36840257,0.13973749,-0.28994447]}")
        .with_fields(vec!["beacon", "className", "certainty"])
        .build();
    let res = client.query.explore(query).await?;

    // Raw
    let query = RawQuery::new("{ Get { JeopardyQuestion { question answer points } } }");
    let res = client.query.raw(query).await?;

    Ok(())
}

Health endpoints

async fn health_endpoints(client: WeaviateClient) -> Result<(), Box<dyn Error>> {
    // Check database is live
    let res = client.is_live().await?;

    // Check database is ready
    let res = client.is_ready().await?;

    Ok(())
}

Classification endpoints

use uuid::Uuid;
use weaviate_community::collections::classification::{
    ClassificationRequest,
    ClassificationType
};
async fn classification_endpoints(client: WeaviateClient) -> Result<(), Box<dyn Error>> {
    // Schedule a new classification
    let req = ClassificationRequest::builder()
        .with_type(ClassificationType::KNN)
        .with_class("Article")
        .with_based_on_properties(vec!["summary"])
        .with_classify_properties(vec!["hasPopularity"])
        .with_filters(serde_json::json!({
            "trainingSetWhere": {
                "path": ["wordCount"],
                "operator": "GreaterThan",
                "valueInt": 100
            }
        }))
        .with_settings(serde_json::json!({
            "k": 3
        }))
        .build();
    let res = client.classification.schedule(req).await?;

    // Get the status of a classification
    let uuid = Uuid::parse_str("00037775-1432-35e5-bc59-443baaef7d80")?;
    let res = client.classification.get(uuid).await?;

    Ok(())
}

Module (text2vec-contextionary) endpoints

use weaviate_community::collections::modules::ContextionaryExtend;
async fn module_endpoints(client: WeaviateClient) -> Result<(), Box<dyn Error>> {
    // Get a concept 
    let res = client.modules.contextionary_get_concept("magazine").await?;

    // Extend contextionary
    let ext = ContextionaryExtension::new(
        "weaviate",
        "Open source cloud native real time vector database",
        1.0
    );
    let res = client.modules.contextionary_extend(ext).await?;

    Ok(())
}

Roadmap

  • SI test update
  • Improvements to the GraphQL query system (and batch delete match config)
  • Create full schema in one command
  • General improvements to try and remove as much serde_json in the deserialized objects..
  • Embedded functionality
  • gRPC (after beta testing for official clients)

Contributing

Any bug reports and feature requests welcome on GitHub

License

This project is licensed under the MIT License open-source license.

Dependencies

~7–20MB
~291K SLoC