Upsert Vectors
⚠️ Warning
HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.
For the complete documentation index optimized for AI agents, see llms.txt.
Upsert Vectors using UpsertV
Create new vector embeddings or update existing ones with insert-or-update semantics.
::UpsertV(vector, {properties})
ℹ️ Info
UpsertVis a traversal step that operates on an existing traversal context. The type comes from the preceding traversal (V<Type>), not from the upsert call itself. If the traversal returns existing vectors, they are updated; if no vectors are found, a new vector is created.
⚠️ Warning
Vector data is required for
UpsertV. You can provide vector data as:
- A literal array of floats (e.g.,
[0.1, 0.2, 0.3])- The
Embed()function to generate embeddings from text- A variable containing vector data
Example 1: Basic vector upsert with properties
QUERY UpsertDoc(vector: [F64], content: String) =>
existing <- V<Document>::WHERE(_::{content}::EQ(content))
doc <- existing::UpsertV(vector, {content: content})
RETURN doc
V::Document {
content: String,
}
Here’s how to run the query using the SDKs or curl
from helix.client import Client
client = Client(local=True, port=6969)
# First call creates a new document
doc = client.query("UpsertDoc", {
"vector": [0.1, 0.2, 0.3, 0.4],
"content": "Introduction to machine learning",
})
print("Created document:", doc)
# Subsequent calls with same vector update the existing document
updated = client.query("UpsertDoc", {
"vector": [0.1, 0.2, 0.3, 0.4],
"content": "Updated introduction to machine learning",
})
print("Updated document:", updated)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = HelixDB::new(Some("http://localhost"), Some(6969), None);
// First call creates a new document
let doc: serde_json::Value = client.query("UpsertDoc", &json!({
"vector": [0.1, 0.2, 0.3, 0.4],
"content": "Introduction to machine learning",
})).await?;
println!("Created document: {doc:#?}");
// Subsequent calls update the existing document
let updated: serde_json::Value = client.query("UpsertDoc", &json!({
"vector": [0.1, 0.2, 0.3, 0.4],
"content": "Updated introduction to machine learning",
})).await?;
println!("Updated document: {updated:#?}");
Ok(())
}
package main
import (
"fmt"
"log"
"github.com/HelixDB/helix-go"
)
func main() {
client := helix.NewClient("http://localhost:6969")
// First call creates a new document
payload := map[string]any{
"vector": []float32{0.1, 0.2, 0.3, 0.4},
"content": "Introduction to machine learning",
}
var doc map[string]any
if err := client.Query("UpsertDoc", helix.WithData(payload)).Scan(&doc); err != nil {
log.Fatalf("UpsertDoc failed: %s", err)
}
fmt.Printf("Created document: %#v\n", doc)
// Subsequent calls update the existing document
payload["content"] = "Updated introduction to machine learning"
var updated map[string]any
if err := client.Query("UpsertDoc", helix.WithData(payload)).Scan(&updated); err != nil {
log.Fatalf("UpsertDoc failed: %s", err)
}
fmt.Printf("Updated document: %#v\n", updated)
}
import HelixDB from "helix-ts";
async function main() {
const client = new HelixDB("http://localhost:6969");
// First call creates a new document
const doc = await client.query("UpsertDoc", {
vector: [0.1, 0.2, 0.3, 0.4],
content: "Introduction to machine learning",
});
console.log("Created document:", doc);
// Subsequent calls update the existing document
const updated = await client.query("UpsertDoc", {
vector: [0.1, 0.2, 0.3, 0.4],
content: "Updated introduction to machine learning",
});
console.log("Updated document:", updated);
}
main().catch((err) => {
console.error("UpsertDoc query failed:", err);
});
# First call creates a new document
curl -X POST \
http://localhost:6969/UpsertDoc \
-H 'Content-Type: application/json' \
-d '{"vector":[0.1,0.2,0.3,0.4],"content":"Introduction to machine learning"}'
# Subsequent calls update the existing document
curl -X POST \
http://localhost:6969/UpsertDoc \
-H 'Content-Type: application/json' \
-d '{"vector":[0.1,0.2,0.3,0.4],"content":"Updated introduction to machine learning"}'
Example 2: Upsert vector using the Embed function
You can use the built-in Embed function to generate embeddings from text.
⚠️ Warning
All vectors in a vector type must have the same dimensions. If you change your embedding model, the new vectors will have different dimensions and will cause an error. Ensure you use the same embedding model consistently for all vectors.
QUERY UpsertDocEmbed(text: String) =>
existing <- V<Document>::WHERE(_::{content}::EQ(text))
doc <- existing::UpsertV(Embed(text), {content: text})
RETURN doc
V::Document {
content: String,
}
OPENAI_API_KEY=your_api_key
Here’s how to run the query using the SDKs or curl
from helix.client import Client
client = Client(local=True, port=6969)
# Upsert with automatic embedding generation
doc = client.query("UpsertDocEmbed", {
"text": "Introduction to machine learning",
})
print("Upserted document:", doc)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = HelixDB::new(Some("http://localhost"), Some(6969), None);
// Upsert with automatic embedding generation
let doc: serde_json::Value = client.query("UpsertDocEmbed", &json!({
"text": "Introduction to machine learning",
})).await?;
println!("Upserted document: {doc:#?}");
Ok(())
}
package main
import (
"fmt"
"log"
"github.com/HelixDB/helix-go"
)
func main() {
client := helix.NewClient("http://localhost:6969")
// Upsert with automatic embedding generation
payload := map[string]any{
"text": "Introduction to machine learning",
}
var doc map[string]any
if err := client.Query("UpsertDocEmbed", helix.WithData(payload)).Scan(&doc); err != nil {
log.Fatalf("UpsertDocEmbed failed: %s", err)
}
fmt.Printf("Upserted document: %#v\n", doc)
}
import HelixDB from "helix-ts";
async function main() {
const client = new HelixDB("http://localhost:6969");
// Upsert with automatic embedding generation
const doc = await client.query("UpsertDocEmbed", {
text: "Introduction to machine learning",
});
console.log("Upserted document:", doc);
}
main().catch((err) => {
console.error("UpsertDocEmbed query failed:", err);
});
curl -X POST \
http://localhost:6969/UpsertDocEmbed \
-H 'Content-Type: application/json' \
-d '{"text":"Introduction to machine learning"}'
Example 3: Complex operation with nodes, edges, and vectors
QUERY ComplexUpsertOperation(
person_name: String,
person_age: U32,
company_name: String,
position: String,
resume_content: String
) =>
existing_person <- N<Person>::WHERE(_::{name}::EQ(person_name))
person <- existing_person::UpsertN({name: person_name, age: person_age})
existing_company <- N<Company>::WHERE(_::{name}::EQ(company_name))
company <- existing_company::UpsertN({name: company_name})
existing_edge <- E<WorksAt>
edge <- existing_edge::UpsertE({position: position})::From(person)::To(company)
existing_resume <- V<Resume>::WHERE(_::{content}::EQ(resume_content))
resume <- existing_resume::UpsertV(Embed(resume_content), {content: resume_content})
RETURN person
QUERY GetPerson(name: String) =>
person <- N<Person>::WHERE(_::{name}::EQ(name))
RETURN person
N::Person {
INDEX name: String,
age: U32,
}
N::Company {
INDEX name: String,
}
V::Resume {
content: String,
}
E::WorksAt {
From: Person,
To: Company,
Properties: {
position: String,
}
}
Here’s how to run the query using the SDKs or curl
from helix.client import Client
client = Client(local=True, port=6969)
# Create or update person, company, employment, and resume all at once
result = client.query("ComplexUpsertOperation", {
"person_name": "Alice",
"person_age": 30,
"company_name": "TechCorp",
"position": "Software Engineer",
"resume_content": "Experienced software engineer with 5 years...",
})
print("Upserted person:", result)
# Running again will update existing records instead of creating duplicates
result = client.query("ComplexUpsertOperation", {
"person_name": "Alice",
"person_age": 31, # Updated age
"company_name": "TechCorp",
"position": "Senior Software Engineer", # Updated position
"resume_content": "Experienced senior software engineer with 6 years...",
})
print("Updated person:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = HelixDB::new(Some("http://localhost"), Some(6969), None);
// Create or update person, company, employment, and resume all at once
let result: serde_json::Value = client.query("ComplexUpsertOperation", &json!({
"person_name": "Alice",
"person_age": 30,
"company_name": "TechCorp",
"position": "Software Engineer",
"resume_content": "Experienced software engineer with 5 years...",
})).await?;
println!("Upserted person: {result:#?}");
// Running again will update existing records instead of creating duplicates
let result: serde_json::Value = client.query("ComplexUpsertOperation", &json!({
"person_name": "Alice",
"person_age": 31,
"company_name": "TechCorp",
"position": "Senior Software Engineer",
"resume_content": "Experienced senior software engineer with 6 years...",
})).await?;
println!("Updated person: {result:#?}");
Ok(())
}
package main
import (
"fmt"
"log"
"github.com/HelixDB/helix-go"
)
func main() {
client := helix.NewClient("http://localhost:6969")
// Create or update person, company, employment, and resume all at once
payload := map[string]any{
"person_name": "Alice",
"person_age": uint32(30),
"company_name": "TechCorp",
"position": "Software Engineer",
"resume_content": "Experienced software engineer with 5 years...",
}
var result map[string]any
if err := client.Query("ComplexUpsertOperation", helix.WithData(payload)).Scan(&result); err != nil {
log.Fatalf("ComplexUpsertOperation failed: %s", err)
}
fmt.Printf("Upserted person: %#v\n", result)
// Running again will update existing records instead of creating duplicates
payload["person_age"] = uint32(31)
payload["position"] = "Senior Software Engineer"
payload["resume_content"] = "Experienced senior software engineer with 6 years..."
if err := client.Query("ComplexUpsertOperation", helix.WithData(payload)).Scan(&result); err != nil {
log.Fatalf("ComplexUpsertOperation failed: %s", err)
}
fmt.Printf("Updated person: %#v\n", result)
}
import HelixDB from "helix-ts";
async function main() {
const client = new HelixDB("http://localhost:6969");
// Create or update person, company, employment, and resume all at once
let result = await client.query("ComplexUpsertOperation", {
person_name: "Alice",
person_age: 30,
company_name: "TechCorp",
position: "Software Engineer",
resume_content: "Experienced software engineer with 5 years...",
});
console.log("Upserted person:", result);
// Running again will update existing records instead of creating duplicates
result = await client.query("ComplexUpsertOperation", {
person_name: "Alice",
person_age: 31,
company_name: "TechCorp",
position: "Senior Software Engineer",
resume_content: "Experienced senior software engineer with 6 years...",
});
console.log("Updated person:", result);
}
main().catch((err) => {
console.error("ComplexUpsertOperation query failed:", err);
});
# Create or update person, company, employment, and resume all at once
curl -X POST \
http://localhost:6969/ComplexUpsertOperation \
-H 'Content-Type: application/json' \
-d '{
"person_name": "Alice",
"person_age": 30,
"company_name": "TechCorp",
"position": "Software Engineer",
"resume_content": "Experienced software engineer with 5 years..."
}'
# Running again will update existing records
curl -X POST \
http://localhost:6969/ComplexUpsertOperation \
-H 'Content-Type: application/json' \
-d '{
"person_name": "Alice",
"person_age": 31,
"company_name": "TechCorp",
"position": "Senior Software Engineer",
"resume_content": "Experienced senior software engineer with 6 years..."
}'
How Upsert differs from Add
| Operation | Behavior |
|---|---|
AddV | Always creates a new vector |
UpsertV | Operates on traversal context: updates if vectors found, creates if empty |
💡 Tip
When updating,
UpsertVmerges properties: it updates specified properties while preserving any existing properties that aren’t included in the upsert. The vector data itself is also updated.
Related operations
- Create vectors - Always create new vectors with AddV
- Vector search - Search for similar vectors
- Upsert nodes - Insert-or-update nodes
- Upsert edges - Insert-or-update edges