Curiosity

Graph Query Language

The Curiosity Graph Query Language is a fluent C# interface for traversing and querying the knowledge graph. Two distinct IQuery interfaces exist depending on the context:

Context How to obtain Interface Results
Endpoints / Shell Q() / Query() global methods Mosaik.GraphDB.IQuery — full feature set Return Emit(...) directly, or materialize with AsEnumerable() / ToList() / etc.
Data Connectors Lambda in await graph.QueryAsync(q => q...) Curiosity.Library.IQuery — focused subset QueryResults returned by graph.QueryAsync; use GetEmitted() / GetEmittedCount().

Starting Points

Method Backend Library Notes
StartAt(string nodeType) All nodes of the given type.
StartAt(Node node) Start from a specific node. Library uses Node.FromKey().
StartAt(Node[] nodes) Start from a set of nodes.
StartAt(string nodeType, string[] keys) Start from nodes of a type identified by multiple keys.
StartAt(string type, string key) Start from a single node by type and key.
StartAt(UID128 uid) / StartAt(IEnumerable<UID128>) Start from one or more UIDs.
StartSearch(string nodeType, string field, ISearchExpression query) Full-text search on a specific field.
StartAtSimilarTextAsync(string text, int count, string[] nodeTypes, ...) Semantic similarity search.
StartNearTo(string nodeType, string field, GeoPoint point, double radius) Nodes within a geographic radius.
StartWhereString(string nodeType, string field, string value) Nodes where a string field matches a value.
StartWhereString(string nodeType, string field, string[] values) Nodes where a string field matches any of a set.
StartWhere(string nodeType, string field, Func<dynamic, bool> predicate) Nodes filtered by a field predicate.

Node references: In the backend, use Node.GetUID(type, key) to reference a node by key. In the library, use Node.FromKey(type, key).

// Backend (endpoint/shell)
Q().StartAt(N.Device.Type, "MacBook Air")
Q().StartAt(Node.GetUID(N.Device.Type, "MacBook Air"))

// Library (data connector)
await graph.QueryAsync(q => q.StartAt(Node.FromKey(nameof(Nodes.Device), "MacBook Air")).Emit("N"))
await graph.QueryAsync(q => q.StartAt(nameof(Nodes.Device), new[] { "MacBook Air", "MacBook Pro" }).Emit("N"))

Traversal

Method Backend Library Notes
Out() Traverse all outgoing edges.
Out(string nodeType) Traverse to a specific node type.
Out(string nodeType, string edgeType) Traverse via a specific edge type.
Out(string nodeType, string[] edgeTypes) Traverse via multiple edge types.
Out(string[] nodeTypes, string edgeType) Traverse to multiple node types via one edge type.
Out(string[] nodeTypes, string[] edgeTypes) Traverse to multiple types via multiple edge types.
OutMany(int levels, string[] nodeTypes, string[] edgeTypes, bool distinct) Multi-hop traversal.
OutWhere(string nodeType, string edgeType, Func<Edge, bool> predicate) Traverse with a predicate on the edge.
Search(string nodeType, string field, ISearchExpression query) Full-text search within the current result set.
PathBetween(UID128 from, UID128 to, int maxLevels) Find paths between two nodes.
// Backend
Q().StartAt(N.Manufacturer.Type, "Apple")
   .Out(N.Device.Type)
   .Out(N.SupportCase.Type)

// Library (data connector)
await graph.QueryAsync(q =>
    q.StartAt(Node.FromKey(nameof(Nodes.Manufacturer), "Apple"))
     .Out(nameof(Nodes.Device))
     .Out(nameof(Nodes.SupportCase))
     .Emit("Cases"))

Filtering

Method Backend Library Notes
OfType(string nodeType) Keep only nodes of the given type.
OfTypes(...) Keep only nodes of any of the given types.
ExceptType(string nodeType) Remove nodes of the given type.
ExceptTypes(...) Remove nodes of any of the given types.
IsRelatedTo(Node node) Keep nodes related to a specific node.
IsRelatedTo(Node[] nodes) Keep nodes related to any of the given nodes.
IsRelatedTo(string nodeType) Keep nodes related to any node of the given type.
IsRelatedTo(string[] nodeTypes) Keep nodes related to any of the given types.
IsRelatedToVia(Node node, string edgeType) Keep nodes related to a specific node via a specific edge.
IsRelatedToVia(string nodeType, string edgeType) Keep nodes related via matching node/edge type.
IsRelatedToVia(string[] nodeTypes, string[] edgeTypes) Keep nodes related via any matching combination.
IsNotRelatedTo(...) Remove nodes related to the given node(s)/type(s).
IsNotRelatedToVia(...) Remove nodes related via the given edge type.
WhereString(string nodeType, string field, string value) Keep nodes where a string field matches.
WhereString(string nodeType, string field, string[] values) Keep nodes where a string field matches any of a set.
WhereNotString(string nodeType, string field, string value) Keep nodes where a string field does not match.
WhereTimestamp(DateTimeOffset from, DateTimeOffset to, bool insideBoundary) Filter by timestamp (library uses DateTimeOffset).
WhereTimestamp(Time from, Time to, bool insideBoundary) Filter by timestamp (backend uses Time).
Where(Func<ReadOnlyNode, bool> predicate) Keep nodes matching a predicate.
Where(string field, Func<dynamic, bool> predicate) Keep nodes where a field satisfies a predicate.
Except(UID128 uid) / Except(IQuery other) Set difference.
Intersect(IQuery other) Set intersection.
Union(IQuery other) Set union.
Distinct() Deduplicate results.
// Library (data connector)
await graph.QueryAsync(q =>
    q.StartAt(nameof(Nodes.SupportCase))
     .IsRelatedTo(Node.FromKey(nameof(Nodes.Device), "MacBook Air"))
     .WhereString(nameof(Nodes.SupportCase), nameof(Nodes.SupportCase.Status), "Open")
     .WhereTimestamp(DateTimeOffset.UtcNow.AddDays(-7), DateTimeOffset.UtcNow, insideBoundary: true)
     .Emit("N"))

// Backend (endpoint/shell)
Q().StartAt(N.SupportCase.Type)
   .IsRelatedTo(Node.GetUID(N.Device.Type, "MacBook Air"))
   .WhereString(N.SupportCase.Type, N.SupportCase.Status, "Open")
   .WhereTimestamp(Time.Now().Add(TimeSpan.FromDays(-7)), Time.Now(), insideBoundary: true)

Sorting and Pagination

Method Backend Library Notes
SortByTimestamp(bool oldestFirst) Sort by creation time.
SortByLastUpdated(bool newestFirst) Sort by modification time.
SortByConnectivity(bool mostConnectedFirst) Sort by edge count.
SortByConnectivityTo(string[] nodeTypes, string[] edgeTypes, bool mostConnectedFirst) Sort by connections to specific types.
SortByDistanceFromNow() Sort by time distance from now.
SortByBoostValue() Sort by relevance score.
SortByRepetition(bool mostOftenFirst) Sort by repetition frequency.
Sort(Func<...> sorter) Custom in-memory sort.
Skip(int count) Skip the first N results.
Take(int count) Limit to N results.

Output

Emit methods

Available in both contexts. In endpoints/shell, Emit(...) returned from the endpoint serializes to the HTTP response. In data connectors, it configures what is included in the QueryResults returned by graph.QueryAsync().

Method Backend Library Notes
Emit(string key) Return matched nodes under a key.
Emit(string key, string[] fields) Return nodes, restricted to specific fields.
EmitCount(string key) Return only the count under a key.
EmitWithEdges(string key) Return nodes with their edge data.
EmitWithEdges(string key, string[] fields) Return nodes with edges, restricted to specific fields.
EmitWithScores(string name) Return nodes with relevance scores.
EmitUIDs(string name) Return only UIDs.
EmitUIDsWithScores(string name) Return UIDs with scores.
EmitSummary(string name) Return a structural graph summary (shell).
EmitNeighborsSummary(string name) Return an edge-type summary (shell).
Similar(string index, int count, float tolerance) Find similar nodes via a named index.
Similar(IndexTypes?, IndexUID, int count) Find similar nodes via backend index reference.
// Library (data connector) — results via QueryResults
var r = await graph.QueryAsync(q =>
    q.StartAt(nameof(Nodes.Device)).Take(10).Emit("N", [nameof(Nodes.Device.Name)]));
var nodes = r.GetEmitted("N");

var r2 = await graph.QueryAsync(q => q.StartAt(nameof(Nodes.Device)).EmitCount("C"));
var count = r2.GetEmittedCount("C");

// Backend (endpoint/shell) — return directly
return Q().StartAt(N.Part.Type).Skip(page * 50).Take(50).Emit();
return Q().StartAt(N.SupportCase.Type).EmitNeighborsSummary();
return Q().EmitSummary();

In-memory materialization (Backend / Endpoints and Shell only)

These methods execute the query and return C# objects. They are not available in the library IQuery.

Method Return Type Description
AsEnumerable() IEnumerable<ReadOnlyNode> Lazy enumerable.
AsEnumerableAsync(...) IAsyncEnumerable<ReadOnlyNode> Async lazy enumerable.
AsUIDEnumerable() IEnumerable<UID128> Raw UIDs only.
AsTypedUIDEnumerable() IEnumerable<TypedUID128> UID + type pairs.
AsScoredUIDEnumerable() IEnumerable<ScoredUID> UIDs with relevance scores.
ToList() List<ReadOnlyNode> Materialize to a list.
ToListWithEdges() List<ReadOnlyNodeWithEdges> All results with edge data.
ToUIDList() List<UID128> All UIDs as a list.
ToScoredUIDList() List<ScoredUID> All scored UIDs as a list.
ToArrayAsync(int concurrency) Task<ReadOnlyNode[]> Async array materialization.
ToJsonAsync() Task<string> Serialize to JSON string.
ToWorkbookAsync() Task<IWorkbook> Export as a workbook.
ToCsvAsync(Stream stream) Task Write as CSV to a stream.
Count() int Total count.
Any() bool True if any results exist.
CollectUIDs(out List<TypedUID128> uids) IQuery Capture UIDs mid-pipeline.
CollectNodes(out List<ReadOnlyNodeWithEdges> nodes) IQuery Capture nodes mid-pipeline.
// Iterate in endpoint code
foreach (var node in Q().StartAt(N.SupportCase.Type).AsEnumerable())
{
    var summary = node.GetString(N.SupportCase.Summary);
}

// Materialize to list
var recent = Q().StartAt(N.SupportCase.Type)
               .SortByTimestamp(oldestFirst: false)
               .Take(20)
               .ToList();

// Check existence
bool hasOpen = Q().StartWhereString(N.SupportCase.Type, N.SupportCase.Status, "Open").Any();

Reading Node Properties

In endpoints and shell, nodes returned from AsEnumerable(), ToList(), or inside a Where() predicate expose typed accessors:

Accessor Type
GetString(key) / GetStringList(key) string / IReadOnlyList<string>
GetInt(key) / GetIntList(key) int / IReadOnlyList<int>
GetBool(key) / GetBoolList(key) bool / IReadOnlyList<bool>
GetFloat(key) / GetFloatList(key) float / IReadOnlyList<float>
GetDouble(key) / GetDoubleList(key) double / IReadOnlyList<double>
GetLong(key) / GetLongList(key) long / IReadOnlyList<long>
GetTime(key) / GetTimeList(key) Time / IReadOnlyList<Time>
GetUID128(key) / GetUID128List(key) UID128 / IReadOnlyList<UID128>
node[key] dynamic

In data connectors, EmittedNode (from QueryResults.GetEmitted()) provides GetField<T>(string field).

Use auto-generated constants to avoid string typos:

foreach (var node in Q().StartAt(N.SupportCase.Type).AsEnumerable())
{
    var id      = node.GetString(N.SupportCase.Id);
    var summary = node.GetString(N.SupportCase.Summary);
    var time    = node.GetTime(N.SupportCase.Time);
}

Next Steps

© 2026 Curiosity. All rights reserved.