Skip to main content
Adds new vectors to the encrypted index or updates existing vectors if they have the same ID. This operation supports two distinct calling patterns for maximum flexibility.

Method Overloads

Pattern 1: VectorItem Array

async upsert({
    items?: VectorItem[],    // Pattern 1: Provide items array
    ids?: string[],         // Pattern 2: Provide ids and vectors together
    vectors?: number[][]    // Pattern 2: Provide ids and vectors together
}): Promise<object>

// Note: Must provide either 'items' OR both 'ids' and 'vectors'

Parameters

Pattern 1: VectorItem Array

ParameterTypeDescription
itemsVectorItem[]Array of vector items to insert or update in the index

Pattern 2: Parallel Arrays

ParameterTypeDescription
idsstring[]Array of ID strings for each vector
vectorsnumber[][]Array of vector embeddings corresponding to each ID

VectorItem Structure

Each VectorItem must contain an id and vector, with optional fields:
FieldTypeRequiredDescription
idstringYesUnique identifier for the vector item
vectornumber[]YesThe vector representation
contentsstring | BufferOptionalContent associated with the vector (accepts both strings and bytes, encoded to bytes and encrypted before storage)
metadataobjectOptionalAdditional structured data associated with the vector
The contents field accepts both strings and Buffers. All contents are encoded to bytes and encrypted before storage, and will be returned as Buffers when retrieved with get().

Returns

Promise<object>: A Promise that resolves to a response object containing the operation status and message with the count of upserted vectors.

Exceptions

  • Throws if the API request fails due to network connectivity issues.
  • Throws if authentication fails (invalid API key).
  • Throws if the encryption key is invalid for the specified index.
  • Throws if there are internal server errors preventing the upsert.
  • Throws detailed validation errors for invalid VectorItem objects.
  • Throws if vector dimensions don’t match the index configuration.
  • Throws if required fields are missing from vector items.
  • Throws if array lengths don’t match in parallel array format.
  • Throws if vector elements are not finite numbers.

Example Usage

import { Client, VectorItem } from 'cyborgdb';

const client = new Client({ baseUrl: 'http://localhost:8000', apiKey: 'your-api-key' });

// Load an existing index
const indexKey = new Uint8Array(Buffer.from('your-stored-hex-key', 'hex'));
const index = await client.loadIndex({ indexName: 'my-vector-index', indexKey });

// Prepare vector items with rich metadata
const vectorItems: VectorItem[] = [
    {
        id: 'doc1',
        vector: [0.1, 0.2, 0.3, /* ... additional dimensions */],
        contents: 'This is the content of the first document',
        metadata: {
            title: 'Introduction to Machine Learning',
            author: 'Dr. Smith',
            category: 'education',
            tags: ['ml', 'ai', 'tutorial'],
            published_date: '2024-01-15',
            word_count: 1250
        }
    },
    {
        id: 'doc2',
        vector: [0.4, 0.5, 0.6, /* ... additional dimensions */],
        contents: 'This is the content of the second document',
        metadata: {
            title: 'Advanced Neural Networks',
            author: 'Dr. Jones',
            category: 'research',
            tags: ['neural-networks', 'deep-learning'],
            published_date: '2024-01-20',
            word_count: 2100
        }
    }
];

// Upsert vectors
try {
    const result = await index.upsert({ items: vectorItems });
    console.log('Upsert result:', result);
    // Typical output: { status: 'success', message: 'Upserted 2 vectors' }
    
    console.log(`Successfully added ${vectorItems.length} vectors to the index`);
} catch (error: any) {
    console.error('Upsert failed:', error.message);
}

Pattern 2: Parallel Arrays (For Bulk Operations)

// Efficient for bulk operations with just IDs and vectors
const ids = ['vec1', 'vec2', 'vec3'];
const vectors = [
    [0.1, 0.2, 0.3, 0.4],
    [0.5, 0.6, 0.7, 0.8],
    [0.9, 1.0, 1.1, 1.2]
];

try {
    const result = await index.upsert({ ids, vectors });
    console.log('Bulk upsert result:', result);

    // This approach is more efficient for large datasets
    // where you only need IDs and vectors (no metadata/contents)
} catch (error: any) {
    console.error('Bulk upsert failed:', error.message);
}

Updating Existing Vectors

// Add initial vector
const initialVector: VectorItem = {
    id: 'updatable_doc',
    vector: [0.1, 0.2, 0.3, 0.4],
    contents: 'Original content',
    metadata: { version: 1, status: 'draft' }
};

await index.upsert({ items: [initialVector] });

// Update the same vector with new data
const updatedVector: VectorItem = {
    id: 'updatable_doc', // Same ID - will update existing
    vector: [0.15, 0.25, 0.35, 0.45], // Updated vector
    contents: 'Updated content with more information',
    metadata: { 
        version: 2, 
        status: 'published', 
        updated_date: '2024-01-25',
        editor: 'John Doe'
    }
};

try {
    const updateResult = await index.upsert({ items: [updatedVector] });
    console.log('Update result:', updateResult);
    
    // Verify the update
    const retrievedVector = await index.get({ ids: ['updatable_doc'] });
    console.log('Updated vector metadata:', retrievedVector[0].metadata);
} catch (error: any) {
    console.error('Update failed:', error.message);
}

Batch Processing with Error Handling

async function processBatchDocuments(
    index: EncryptedIndex, 
    documents: Array<{
        id: string;
        title: string;
        content: string;
        author: string;
        category: string;
        vector: number[];
    }>
): Promise<{ success: boolean; count: number; message: string }> {
    
    // Convert documents to VectorItem format
    const vectorItems: VectorItem[] = documents.map(doc => ({
        id: doc.id,
        vector: doc.vector,
        contents: doc.content,
        metadata: {
            title: doc.title,
            author: doc.author,
            category: doc.category,
            word_count: doc.content.split(' ').length,
            char_count: doc.content.length,
            processed_date: new Date().toISOString()
        }
    }));
    
    try {
        console.log(`Processing batch of ${vectorItems.length} documents`);

        const result = await index.upsert({ items: vectorItems });
        console.log('Batch upsert completed:', result);
        
        return {
            success: true,
            count: vectorItems.length,
            message: `Successfully processed ${vectorItems.length} documents`
        };
    } catch (error: any) {
        console.error('Batch upsert failed:', error.message);
        
        // Provide detailed error information
        if (error.message.includes('Invalid VectorItem')) {
            console.error('Validation failed - check your vector data format');
        } else if (error.message.includes('dimension')) {
            console.error('Vector dimension mismatch - check index configuration');
        }
        
        return {
            success: false,
            count: 0,
            message: `Batch processing failed: ${error.message}`
        };
    }
}

// Usage
const documents = [
    {
        id: 'article_001',
        title: 'Getting Started with TypeScript',
        content: 'TypeScript is a powerful superset of JavaScript...',
        author: 'Jane Developer',
        category: 'programming',
        vector: [0.1, 0.2, 0.3, 0.4]
    },
    {
        id: 'article_002',
        title: 'React Best Practices',
        content: 'When building React applications, it is important...',
        author: 'John React',
        category: 'frontend',
        vector: [0.5, 0.6, 0.7, 0.8]
    }
];

const batchResult = await processBatchDocuments(index, documents);
console.log('Batch processing result:', batchResult);

Response Format

// Standard successful upsert response
{
    "status": "success",
    "message": "Upserted 5 vectors"
}