Skip to content

Commit 963ed42

Browse files
committed
Implement DuckDB Graph Repository Methods and Unit Tests
- Added LabelMethods, NodeMethods, TagMethods, TenantMethods, UserMethods, VectorIndexMethods, and VectorMethods for DuckDB implementation. - Each method includes not implemented exceptions for future development. - Created DuckDBGraphRepositoryTests to validate repository functionality, including constructor tests, method implementations, and exception handling. - Added project file for tests with necessary dependencies.
1 parent 40274c1 commit 963ed42

17 files changed

Lines changed: 1778 additions & 2 deletions

DuckDBGraphRepository.cs

Lines changed: 282 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,287 @@
1-
namespace WebNet.LiteGraphExtensions.GraphRepositories
1+
using System;
2+
using DuckDB.NET.Data;
3+
using LiteGraph.GraphRepositories.Interfaces;
4+
5+
namespace WebNet.LiteGraphExtensions.GraphRepositories
26
{
3-
public class DuckDBGraphRepository
7+
/// <summary>
8+
/// DuckDB Graph Repository implementation.
9+
/// Provides graph storage and retrieval using DuckDB as the backend.
10+
/// </summary>
11+
public class DuckDBGraphRepository : LiteGraph.GraphRepositories.GraphRepositoryBase
412
{
13+
#region Public-Members
14+
15+
/// <summary>
16+
/// Admin methods.
17+
/// </summary>
18+
public override IAdminMethods Admin { get; }
19+
20+
/// <summary>
21+
/// Tenant methods.
22+
/// </summary>
23+
public override ITenantMethods Tenant { get; }
24+
25+
/// <summary>
26+
/// User methods.
27+
/// </summary>
28+
public override IUserMethods User { get; }
29+
30+
/// <summary>
31+
/// Credential methods.
32+
/// </summary>
33+
public override ICredentialMethods Credential { get; }
34+
35+
/// <summary>
36+
/// Label methods.
37+
/// </summary>
38+
public override ILabelMethods Label { get; }
39+
40+
/// <summary>
41+
/// Tag methods.
42+
/// </summary>
43+
public override ITagMethods Tag { get; }
44+
45+
/// <summary>
46+
/// Vector methods.
47+
/// </summary>
48+
public override IVectorMethods Vector { get; }
49+
50+
/// <summary>
51+
/// Graph methods.
52+
/// </summary>
53+
public override IGraphMethods Graph { get; }
54+
55+
/// <summary>
56+
/// Node methods.
57+
/// </summary>
58+
public override INodeMethods Node { get; }
59+
60+
/// <summary>
61+
/// Edge methods.
62+
/// </summary>
63+
public override IEdgeMethods Edge { get; }
64+
65+
/// <summary>
66+
/// Batch methods.
67+
/// </summary>
68+
public override IBatchMethods Batch { get; }
69+
70+
/// <summary>
71+
/// Vector index methods.
72+
/// </summary>
73+
public override IVectorIndexMethods VectorIndex { get; }
74+
75+
#endregion
76+
77+
#region Private-Members
78+
79+
private readonly string _connectionString;
80+
private readonly DuckDBConnection _connection;
81+
82+
#endregion
83+
84+
#region Constructors-and-Factories
85+
86+
/// <summary>
87+
/// Initialize the DuckDB graph repository.
88+
/// </summary>
89+
/// <param name="connectionString">DuckDB connection string.</param>
90+
public DuckDBGraphRepository(string connectionString)
91+
{
92+
if (string.IsNullOrEmpty(connectionString))
93+
throw new ArgumentNullException(nameof(connectionString));
94+
95+
_connectionString = connectionString;
96+
_connection = new DuckDBConnection(connectionString);
97+
_connection.Open();
98+
99+
// Initialize implementation classes
100+
Admin = new Implementations.AdminMethods(this);
101+
Tenant = new Implementations.TenantMethods(this);
102+
User = new Implementations.UserMethods(this);
103+
Credential = new Implementations.CredentialMethods(this);
104+
Label = new Implementations.LabelMethods(this);
105+
Tag = new Implementations.TagMethods(this);
106+
Vector = new Implementations.VectorMethods(this);
107+
Graph = new Implementations.GraphMethods(this);
108+
Node = new Implementations.NodeMethods(this);
109+
Edge = new Implementations.EdgeMethods(this);
110+
Batch = new Implementations.BatchMethods(this);
111+
VectorIndex = new Implementations.VectorIndexMethods(this);
112+
113+
InitializeRepository();
114+
}
115+
116+
#endregion
117+
118+
#region Public-Methods
119+
120+
/// <summary>
121+
/// Initialize the repository schema.
122+
/// </summary>
123+
public override void InitializeRepository()
124+
{
125+
// Create necessary database tables and schemas for DuckDB
126+
using (var command = _connection.CreateCommand())
127+
{
128+
command.CommandText = @"
129+
-- Create tenants table
130+
CREATE TABLE IF NOT EXISTS tenants (
131+
guid VARCHAR PRIMARY KEY,
132+
name VARCHAR NOT NULL,
133+
created_utc TIMESTAMP NOT NULL,
134+
data JSON
135+
);
136+
137+
-- Create graphs table
138+
CREATE TABLE IF NOT EXISTS graphs (
139+
guid VARCHAR PRIMARY KEY,
140+
tenant_guid VARCHAR NOT NULL,
141+
name VARCHAR NOT NULL,
142+
created_utc TIMESTAMP NOT NULL,
143+
labels VARCHAR[],
144+
tags JSON,
145+
data JSON,
146+
FOREIGN KEY (tenant_guid) REFERENCES tenants(guid)
147+
);
148+
149+
-- Create nodes table
150+
CREATE TABLE IF NOT EXISTS nodes (
151+
guid VARCHAR PRIMARY KEY,
152+
tenant_guid VARCHAR NOT NULL,
153+
graph_guid VARCHAR NOT NULL,
154+
name VARCHAR,
155+
created_utc TIMESTAMP NOT NULL,
156+
labels VARCHAR[],
157+
tags JSON,
158+
data JSON,
159+
FOREIGN KEY (tenant_guid) REFERENCES tenants(guid),
160+
FOREIGN KEY (graph_guid) REFERENCES graphs(guid)
161+
);
162+
163+
-- Create edges table
164+
CREATE TABLE IF NOT EXISTS edges (
165+
guid VARCHAR PRIMARY KEY,
166+
tenant_guid VARCHAR NOT NULL,
167+
graph_guid VARCHAR NOT NULL,
168+
from_node_guid VARCHAR NOT NULL,
169+
to_node_guid VARCHAR NOT NULL,
170+
name VARCHAR,
171+
created_utc TIMESTAMP NOT NULL,
172+
labels VARCHAR[],
173+
tags JSON,
174+
data JSON,
175+
FOREIGN KEY (tenant_guid) REFERENCES tenants(guid),
176+
FOREIGN KEY (graph_guid) REFERENCES graphs(guid),
177+
FOREIGN KEY (from_node_guid) REFERENCES nodes(guid),
178+
FOREIGN KEY (to_node_guid) REFERENCES nodes(guid)
179+
);
180+
181+
-- Create labels table
182+
CREATE TABLE IF NOT EXISTS labels (
183+
guid VARCHAR PRIMARY KEY,
184+
tenant_guid VARCHAR NOT NULL,
185+
graph_guid VARCHAR,
186+
name VARCHAR NOT NULL,
187+
created_utc TIMESTAMP NOT NULL,
188+
FOREIGN KEY (tenant_guid) REFERENCES tenants(guid)
189+
);
190+
191+
-- Create users table
192+
CREATE TABLE IF NOT EXISTS users (
193+
guid VARCHAR PRIMARY KEY,
194+
tenant_guid VARCHAR,
195+
email VARCHAR NOT NULL,
196+
created_utc TIMESTAMP NOT NULL,
197+
data JSON
198+
);
199+
200+
-- Create credentials table
201+
CREATE TABLE IF NOT EXISTS credentials (
202+
guid VARCHAR PRIMARY KEY,
203+
user_guid VARCHAR NOT NULL,
204+
credential_type VARCHAR NOT NULL,
205+
credential_data JSON NOT NULL,
206+
created_utc TIMESTAMP NOT NULL,
207+
FOREIGN KEY (user_guid) REFERENCES users(guid)
208+
);
209+
210+
-- Create vectors table
211+
CREATE TABLE IF NOT EXISTS vectors (
212+
guid VARCHAR PRIMARY KEY,
213+
tenant_guid VARCHAR NOT NULL,
214+
graph_guid VARCHAR NOT NULL,
215+
node_guid VARCHAR NOT NULL,
216+
embedding DOUBLE[],
217+
created_utc TIMESTAMP NOT NULL,
218+
metadata JSON,
219+
FOREIGN KEY (tenant_guid) REFERENCES tenants(guid),
220+
FOREIGN KEY (graph_guid) REFERENCES graphs(guid),
221+
FOREIGN KEY (node_guid) REFERENCES nodes(guid)
222+
);
223+
224+
-- Create tags table
225+
CREATE TABLE IF NOT EXISTS tags (
226+
guid VARCHAR PRIMARY KEY,
227+
tenant_guid VARCHAR NOT NULL,
228+
graph_guid VARCHAR,
229+
name VARCHAR NOT NULL,
230+
value VARCHAR,
231+
created_utc TIMESTAMP NOT NULL,
232+
FOREIGN KEY (tenant_guid) REFERENCES tenants(guid)
233+
);
234+
235+
-- Create indexes
236+
CREATE INDEX IF NOT EXISTS idx_graphs_tenant ON graphs(tenant_guid);
237+
CREATE INDEX IF NOT EXISTS idx_nodes_graph ON nodes(graph_guid);
238+
CREATE INDEX IF NOT EXISTS idx_nodes_tenant ON nodes(tenant_guid);
239+
CREATE INDEX IF NOT EXISTS idx_edges_graph ON edges(graph_guid);
240+
CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(from_node_guid);
241+
CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(to_node_guid);
242+
CREATE INDEX IF NOT EXISTS idx_labels_tenant ON labels(tenant_guid);
243+
CREATE INDEX IF NOT EXISTS idx_vectors_node ON vectors(node_guid);
244+
CREATE INDEX IF NOT EXISTS idx_tags_tenant ON tags(tenant_guid);
245+
";
246+
247+
command.ExecuteNonQuery();
248+
}
249+
}
250+
251+
/// <summary>
252+
/// Flush database contents to disk.
253+
/// </summary>
254+
public override void Flush()
255+
{
256+
// DuckDB automatically flushes to disk, but we can force a checkpoint
257+
using (var command = _connection.CreateCommand())
258+
{
259+
command.CommandText = "CHECKPOINT;";
260+
command.ExecuteNonQuery();
261+
}
262+
}
263+
264+
/// <summary>
265+
/// Get the DuckDB connection.
266+
/// </summary>
267+
/// <returns>DuckDB connection.</returns>
268+
internal DuckDBConnection GetConnection()
269+
{
270+
return _connection;
271+
}
272+
273+
/// <summary>
274+
/// Dispose the repository.
275+
/// </summary>
276+
public void Dispose()
277+
{
278+
_connection?.Dispose();
279+
}
280+
281+
#endregion
282+
283+
#region Private-Methods
5284

285+
#endregion
6286
}
7287
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using LiteGraph.GraphRepositories.Interfaces;
5+
6+
namespace WebNet.LiteGraphExtensions.GraphRepositories.Implementations
7+
{
8+
/// <summary>
9+
/// Admin methods for DuckDB.
10+
/// </summary>
11+
public class AdminMethods : IAdminMethods
12+
{
13+
private readonly DuckDBGraphRepository _repo;
14+
15+
public AdminMethods(DuckDBGraphRepository repo)
16+
{
17+
_repo = repo ?? throw new ArgumentNullException(nameof(repo));
18+
}
19+
20+
public async Task Backup(string outputFilename, CancellationToken token = default)
21+
{
22+
token.ThrowIfCancellationRequested();
23+
24+
using (var command = _repo.GetConnection().CreateCommand())
25+
{
26+
command.CommandText = $"EXPORT DATABASE '{outputFilename}';";
27+
await command.ExecuteNonQueryAsync();
28+
}
29+
}
30+
}
31+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using LiteGraph;
5+
using LiteGraph.GraphRepositories.Interfaces;
6+
7+
namespace WebNet.LiteGraphExtensions.GraphRepositories.Implementations
8+
{
9+
/// <summary>
10+
/// Batch methods for DuckDB.
11+
/// </summary>
12+
public class BatchMethods : IBatchMethods
13+
{
14+
private readonly DuckDBGraphRepository _repo;
15+
16+
public BatchMethods(DuckDBGraphRepository repo)
17+
{
18+
_repo = repo ?? throw new ArgumentNullException(nameof(repo));
19+
}
20+
21+
public Task<ExistenceResult> Existence(Guid tenantGuid, Guid graphGuid, ExistenceRequest req, CancellationToken token = default)
22+
{
23+
throw new NotImplementedException("BatchMethods.Existence not yet implemented for DuckDB");
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)