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
- Data connector query usage: Querying in Data Connectors
- Endpoint scope reference: Execution Scope
- Auto-generated type/edge helpers: Auto-generated Helpers