-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathQuerySelectedGhComponentsFromNeo4j.cs
More file actions
311 lines (264 loc) · 10.6 KB
/
Copy pathQuerySelectedGhComponentsFromNeo4j.cs
File metadata and controls
311 lines (264 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#r "nuget: Neo4j.Driver"
// async: true
using Neo4j.Driver;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using Grasshopper.Kernel;
using GraphHop2.Utilities; // Make sure this is present for FileSelector
using System.IO;
public class Neo4jConnector : IDisposable {
public Neo4jConnector(
string _uri = null,
string _user = null,
string _pass = null
)
{
var uri = _uri ?? Environment.GetEnvironmentVariable("NEO4J_URI");
var user = _user ?? Environment.GetEnvironmentVariable("NEO4J_USER");
var password = _pass ?? Environment.GetEnvironmentVariable("NEO4J_PASSWORD");
Driver = GraphDatabase.Driver(uri, AuthTokens.Basic(user, password));
Session = Driver.AsyncSession();
}
public IAsyncSession Session {get;set;}
IDriver Driver;
// print debug info about a node
public void PrintDebugInfo(string key, INode node, bool indent = false)
{
var prefix = indent ? "\t" : "";
Console.WriteLine($"{prefix}Node {key}, Labels: {string.Join(",", node.Labels)}, Id {node.ElementId}");
foreach (var prop in node.Properties)
{
Console.WriteLine($"{prefix}\t{prop.Key} = {prop.Value}");
}
}
// print debug info about a relationship
public void PrintDebugInfo(string key, IRelationship rel, bool indent = false)
{
var prefix = indent ? "\t" : "";
Console.WriteLine($"{prefix}Relationship {key}, Type: {rel.Type}, Id {rel.ElementId}");
foreach (var prop in rel.Properties)
{
Console.WriteLine($"{prefix}\t{prop.Key} = {prop.Value}");
}
}
// print debug info about a path
public void PrintDebugInfo(string key, IPath path, bool indent = false)
{
var prefix = indent ? "\t" : "";
Console.WriteLine($"{prefix}Path {key}, Length: {path.Nodes.Count}");
for (int i=0; i<path.Nodes.Count-1; i++)
{
PrintDebugInfo("", path.Nodes[i], true);
PrintDebugInfo("", path.Relationships[i], true);
}
PrintDebugInfo("", path.End, true);
}
// run a query, optionally print debug info
public async Task<IReadOnlyList<IRecord>> RunQuery(string queryString, bool printDebugInfo = false)
{
var result = await Session.RunAsync(queryString);
var records = new List<IRecord>();
// Fetch records, optionally print debug info
await result.ForEachAsync(record =>
{
records.Add(record);
if (!printDebugInfo)
return;
// Print debug info about all fields in the record
for (int i = 0; i < record.Keys.Count; i++)
{
var key = record.Keys[i];
var value = record[i];
if (value is INode node) {
PrintDebugInfo(key, node);
}
else if (value is IRelationship rel)
{
PrintDebugInfo(key, rel);
}
else if (value is IPath path)
{
PrintDebugInfo(key, path);
}
else {
Console.Write($"{key} = {value}; ");
}
}
Console.WriteLine();
});
return records;
}
public void Dispose()
{
Driver?.Dispose();
}
}
public class QueryGenerator {
public QueryGenerator(Neo4jConnector connector)
{
Connector = connector;
}
Neo4jConnector Connector;
T GetRecordValue<T>(IRecord record, string key)
{
if (record.TryGet(key, out T value))
return value;
throw new ArgumentException($"Could not get key {key} from record");
}
public async Task<List<string>> QueryDocumentVersionFilePathsFromComponent(IReadOnlyList<IRecord> records)
{
List<string> filePaths = new List<string>();
foreach (var record in records)
{
var versionId = GetRecordValue<string>(record, "VersionId");
var queryString = $"MATCH (n:DocumentVersion) WHERE n.VersionId = '{versionId}' RETURN n.FilePath AS FilePath, n.IsCluster as IsCluster";
var result = await Connector.RunQuery(queryString, true);
if (result.Count != 1)
throw new ArgumentException($"Could not get document with version {versionId}");
var filePath = GetRecordValue<string>(result.First(), "FilePath");
if (string.IsNullOrEmpty(filePath))
continue;
filePaths.Add(filePath);
}
return filePaths;
}
public async Task<List<(string, int, int)>> QueryDocumentDataFromComponent(IReadOnlyList<IRecord> records)
{
List<(string, int, int)> filePathsAndCoords = new List<(string, int, int)>();
foreach (var record in records)
{
var versionId = GetRecordValue<string>(record, "VersionId");
var queryString = $"MATCH (n:DocumentVersion) WHERE n.VersionId = '{versionId}' RETURN n.FilePath AS FilePath, n.IsCluster as IsCluster";
var result = await Connector.RunQuery(queryString, true);
if (result.Count != 1)
throw new ArgumentException($"Could not get document with version {versionId}");
var filePath = GetRecordValue<string>(result.First(), "FilePath");
if (string.IsNullOrEmpty(filePath))
continue;
var pivotX = GetRecordValue<int>(record, "PivotX");
var pivotY = GetRecordValue<int>(record, "PivotY");
filePathsAndCoords.Add((filePath, pivotX, pivotY));
}
return filePathsAndCoords;
}
public async Task<IReadOnlyList<IRecord>> QueryFromTuples(List<(IGH_DocumentObject, IGH_DocumentObject)> tuples)
{
// create a dictionary from componentinstance to variable name
var nodeVarDict = new Dictionary<IGH_DocumentObject, string>();
int i = 0;
foreach (var tuple in tuples)
{
var node = tuple.Item1;
if (!nodeVarDict.ContainsKey(node))
nodeVarDict.Add(node, $"n{i++}");
node = tuple.Item2;
if (!nodeVarDict.ContainsKey(node))
nodeVarDict.Add(node, $"n{i++}");
}
// create match clauses
i = 0;
var matches = tuples.Select(tuple => {
var v1 = nodeVarDict[tuple.Item1];
var v2 = nodeVarDict[tuple.Item2];
return $"p{i++}=({v1}:ComponentInstance)-[:Wire]->({v2}:ComponentInstance)";
});
// create where clauses
i = 0;
var wheres = nodeVarDict.Keys.Select(node => $"{nodeVarDict[node]}.ComponentGuid = '{node.ComponentGuid}'");
// create complete query
var queryString = $"MATCH {string.Join(", ", matches)} WHERE {string.Join(" AND ", wheres)} RETURN DISTINCT n1.VersionId AS VersionId, n1.InstanceGuid AS InstanceGuid, n1.PivotX AS PivotX, n1.PivotY AS PivotY";
Console.WriteLine(queryString);
return await Connector.RunQuery(queryString, true);
}
}
List<(IGH_DocumentObject, IGH_DocumentObject)> GetConnections(IEnumerable<IGH_DocumentObject> selectedObjects)
{
var connections = new List<(IGH_DocumentObject, IGH_DocumentObject)>();
foreach (IGH_DocumentObject obj in selectedObjects)
{
if (obj is IGH_Component component)
{
// Output connections (this component to recipients)
foreach (var outputParam in component.Params.Output)
{
foreach (IGH_Param recipient in outputParam.Recipients)
{
IGH_DocumentObject recipientComponent = recipient.Attributes.GetTopLevel.DocObject;
if (selectedObjects.Contains(recipientComponent))
connections.Add((obj, recipientComponent));
}
}
}
else if (obj is IGH_Param par)
{
foreach (IGH_Param recipient in par.Recipients)
{
IGH_DocumentObject recipientComponent = recipient.Attributes.GetTopLevel.DocObject;
if (selectedObjects.Contains(recipientComponent))
connections.Add((obj, recipientComponent));
}
}
}
return connections;
}
List<(IGH_DocumentObject, IGH_DocumentObject)> GetSelectedTuples()
{
var empty = new List<(IGH_DocumentObject, IGH_DocumentObject)>();
if (Grasshopper.Instances.ActiveCanvas == null)
{
Console.WriteLine("Grasshopper is not loaded or no canvas is active!");
return empty;
}
var ghDoc = Grasshopper.Instances.ActiveCanvas.Document;
if (ghDoc == null)
{
Console.WriteLine("No active Grasshopper document found!");
return empty;
}
var selectedObjects = ghDoc.SelectedObjects();
if (selectedObjects.Count == 0)
{
Console.WriteLine("No Grasshopper components selected!");
return empty;
}
var connections = GetConnections(selectedObjects);
if (connections.Count == 0)
{
Console.WriteLine("No connections found for selected components.");
return empty;
}
Console.WriteLine($"Number of nodes/connections found: {selectedObjects.Count}/{connections.Count}");
return connections;
}
using (var connector = new Neo4jConnector("neo4j+s://916f7f37.databases.neo4j.io", "neo4j", "_GjWi91K3QZkkGg3hA7Itrp-U9dlvzH80JnFsnvvW6I"))
{
var tuples = GetSelectedTuples();
if (tuples.Count == 0)
return;
var generator = new QueryGenerator(connector);
var records = await generator.QueryFromTuples(tuples);
var documentData = await generator.QueryDocumentDataFromComponent(records);
Console.WriteLine($"Found {documentData.Count} document versions");
foreach (var data in documentData)
{
Console.WriteLine($"File {data.Item1}, PivotX {data.Item2}, PivotY {data.Item3}");
}
var filePaths = documentData.Select(d => d.Item1).ToList();
// Use the predefined library path
string libraryPath = @"C:\Users\13679\source\repos\2025 AECtech Hack\Neo4j-20251115T173152Z-1-001\Neo4j\ShapeDiver Demomodels\DemoModels";
// Show file selector dialog and get user selection, replacing prefix if needed
var selectedFiles = FileSelector.SelectFiles(filePaths, replaceDemoModelPrefix: true, libraryPath: libraryPath);
if (selectedFiles.Count > 0)
{
Console.WriteLine("User selected:");
foreach (var f in selectedFiles)
Console.WriteLine(f);
// You can now use selectedFiles as needed
}
else
{
Console.WriteLine("No file selected.");
}
}