> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cyborg.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Upsert

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

```typescript theme={null}
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
    metadata?: (Record<string, unknown> | null)[], // Pattern 2: Optional metadata per vector
    contents?: (string | null)[]                // Pattern 2: Optional contents per vector
}): Promise<UpsertResponse>

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

### Parameters

#### Pattern 1: VectorItem Array

| Parameter | Type                                  | Description                                            |
| --------- | ------------------------------------- | ------------------------------------------------------ |
| `items`   | [`VectorItem[]`](../types#vectoritem) | Array of vector items to insert or update in the index |

#### Pattern 2: Parallel Arrays

| Parameter  | Type                                  | Description                                                     |
| ---------- | ------------------------------------- | --------------------------------------------------------------- |
| `ids`      | `string[]`                            | Array of ID strings for each vector                             |
| `vectors`  | `number[][]` \| `Float32Array`        | Array of vector embeddings corresponding to each ID             |
| `metadata` | `(Record<string, unknown> \| null)[]` | *(Optional)* Array of metadata objects corresponding to each ID |
| `contents` | `(string \| null)[]`                  | *(Optional)* Array of content strings corresponding to each ID  |

<Tip>Passing a `Float32Array` automatically uses an optimized binary transfer format for better performance with large batches.</Tip>

#### VectorItem Structure

Each `VectorItem` must contain an `id` and `vector`, with optional fields:

| Field      | Type                 | Required | Description                                                                                                        |
| ---------- | -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `id`       | `string`             | Yes      | Unique identifier for the vector item                                                                              |
| `vector`   | `number[]`           | Yes\*    | The vector representation. \*Required unless `contents` is provided (for auto-embedding via embedding model)       |
| `contents` | `string` \| `Buffer` | Optional | Content associated with the vector (accepts both strings and bytes, encoded to bytes and encrypted before storage) |
| `metadata` | `object`             | Optional | Additional structured data associated with the vector                                                              |

<Tip>The `contents` field accepts both strings and Buffers. All contents are encoded to bytes and encrypted before storage, and will be returned as decoded UTF-8 strings when retrieved with `get()`.</Tip>

### Returns

`Promise<UpsertResponse>`: A Promise that resolves to a response object containing the operation status, message, and count of upserted vectors. See the [UpsertResponse type](../types#upsertresponse) for more details.

### Exceptions

<AccordionGroup>
  <Accordion title="Error">
    * 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.
  </Accordion>

  <Accordion title="Validation Errors">
    * 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.
  </Accordion>
</AccordionGroup>

### Example Usage

#### Pattern 1: VectorItem Array (Recommended)

```typescript theme={null}
import { Client, VectorItem, UpsertResponse } 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: UpsertResponse = await index.upsert({ items: vectorItems });
    console.log('Upsert result:', result);
    console.log(`Status: ${result.status}`);
    console.log(`Upserted count: ${result.upsertedCount}`);

    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)

```typescript theme={null}
// Efficient for bulk operations with parallel arrays
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);
} catch (error: any) {
    console.error('Bulk upsert failed:', error.message);
}

// With optional metadata and contents
const metadata = [
    { title: 'Document 1', category: 'research' },
    { title: 'Document 2', category: 'tutorial' },
    null  // no metadata for third vector
];
const contents = [
    'First document content',
    'Second document content',
    null  // no content for third vector
];

try {
    const result = await index.upsert({ ids, vectors, metadata, contents });
    console.log('Bulk upsert with metadata:', result);
} catch (error: any) {
    console.error('Bulk upsert failed:', error.message);
}
```

#### Pattern 2 (Binary Fast Path): Float32Array

For large batches, pack vectors into a single `Float32Array` and pass it as `vectors`. The SDK detects the typed array and uses an optimized binary transfer (no per-vector JSON encoding), which substantially reduces wire size and CPU on both ends.

```typescript theme={null}
const dim = 384;
const ids = ['vec1', 'vec2', 'vec3'];

// Flat, contiguous buffer — vectors laid out back-to-back
const flat = new Float32Array(ids.length * dim);
for (let i = 0; i < ids.length; i++) {
    for (let j = 0; j < dim; j++) {
        flat[i * dim + j] = Math.random();
    }
}

const result = await index.upsert({ ids, vectors: flat });
console.log(`Upserted ${result.upsertedCount} vectors via binary transfer`);
```

<Tip>Use `Float32Array` whenever you already have vectors in a typed buffer (e.g. from a model output, an `ArrayBuffer` over a socket, or an embedding library that exposes `Float32Array` directly). Avoid the cost of converting back to `number[][]`.</Tip>

#### Updating Existing Vectors

```typescript theme={null}
// 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

```typescript theme={null}
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

```typescript theme={null}
// Standard successful upsert response
{
    "status": "success",
    "message": "Upserted 5 vectors"
}
```
