Migration Examples
This page provides common examples of migration scripts. You can adapt these to fit your specific data model and requirements.
1. Updating Data
This example iterates through all Person nodes and normalizes the Email property to lowercase.
// Report indefinite progress initially
Progress.Indeterminate();
var query = Q().StartAt("Person");
var total = query.Count();
var count = 0;
// Iterate using AsTypedUIDEnumerable() for better memory management with large datasets
// This returns TypedUID128 structs which contain both the UID and the Type ID
foreach (var uid in query.AsTypedUIDEnumerable())
{
// Always check for cancellation
CancellationToken.ThrowIfCancellationRequested();
// Lock the node for update
var node = await Graph.TryGetLockedAsync(uid.UID);
if (node != null)
{
var email = node.GetString("Email");
// Check if update is needed
if (!string.IsNullOrEmpty(email) && email.Any(char.IsUpper))
{
node["Email"] = email.ToLowerInvariant();
// Commit the change
await Graph.CommitAsync(node);
}
}
count++;
// Report progress periodically
if (count % 100 == 0)
{
Progress.Set(count, total).Message($"Processed {count}/{total}");
}
}
Progress.Done();
2. Adding Edges Based on Data
This example creates a relationship between Ticket and Person nodes based on an email address stored in the ticket.
Progress.Indeterminate();
var query = Q().StartAt("Ticket");
var total = query.Count();
var count = 0;
foreach (var uid in query.AsTypedUIDEnumerable())
{
CancellationToken.ThrowIfCancellationRequested();
var ticketNode = await Graph.TryGetLockedAsync(uid.UID);
if (ticketNode != null)
{
var assigneeEmail = ticketNode.GetString("AssigneeEmail");
if (!string.IsNullOrEmpty(assigneeEmail))
{
// Find the corresponding Person node (read-only query)
// Note: Assuming "Email" is indexed or unique
var personNode = Q().StartAt("Person").Where("Email", assigneeEmail).FirstOrDefault();
if (personNode != null)
{
// Create a unique edge "AssignedTo" from Ticket to Person
// AddUniqueEdgeTo ensures no duplicate edges of this type to the same target
ticketNode.AddUniqueEdgeTo("AssignedTo", personNode.UID);
await Graph.CommitAsync(ticketNode);
}
}
}
count++;
if (count % 100 == 0)
{
Progress.Set(count, total).Message($"Processed {count}/{total}");
}
}
Progress.Done();
3. Search and Queue for Re-indexing
This example uses a similarity search to find nodes related to a specific topic and queues them for file content re-extraction. This is useful if you want to re-process specific files with updated extraction logic or OCR settings.
Progress.Indeterminate();
// Search for File nodes containing the text "contract updates"
// StartSearch uses the full-text index
var query = Q().StartSearch("File", "", Search.Parse("contract updates"));
var total = query.Count();
var count = 0;
foreach (var uid in query.AsTypedUIDEnumerable())
{
CancellationToken.ThrowIfCancellationRequested();
// Queue the file for re-indexing (content extraction)
// Accessing 'Internals' is necessary to reach the low-level IndexManager
Graph.Internals.Indexes.Enqueue(uid.Type, uid.UID);
count++;
if (count % 10 == 0)
{
Progress.Set(count, total).Message($"Queued {count}/{total}");
}
}
Progress.Done();