Skip to content

Commit baec39c

Browse files
committed
fix: analysis package
1 parent b3466d9 commit baec39c

34 files changed

+5624
-5456
lines changed

README.md

Lines changed: 33 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -184,59 +184,27 @@ gograph analyze . \
184184

185185
### 3. Essential Graph Visualization Queries
186186

187-
Once connected, try these queries to explore your codebase:
187+
Once connected to Neo4j Browser, you can explore your codebase using Cypher queries.
188188

189-
#### **Complete Project Overview**
189+
**📚 For a comprehensive collection of queries, see [docs/QUERIES.md](docs/QUERIES.md)**
190+
191+
Here are a few essential queries to get you started:
190192

191193
```cypher
192194
// View the entire project structure
193195
MATCH (n)
194196
WHERE n.project_id = 'my-awesome-project'
195197
RETURN n
196198
LIMIT 500
197-
```
198-
199-
#### **Package Dependencies**
200-
201-
```cypher
202-
// Visualize package import relationships
203-
MATCH (p1:Package)-[:IMPORTS]->(p2:Package)
204-
WHERE p1.project_id = 'my-awesome-project'
205-
RETURN p1, p2
206-
```
207-
208-
#### **Function Call Graph**
209-
210-
```cypher
211-
// See function call relationships
212-
MATCH (f1:Function)-[:CALLS]->(f2:Function)
213-
WHERE f1.project_id = 'my-awesome-project'
214-
RETURN f1, f2
215-
LIMIT 100
216-
```
217199
218-
#### **Architecture Overview**
219-
220-
```cypher
221-
// High-level architecture view (packages and their relationships)
222-
MATCH path = (p1:Package)-[:IMPORTS*1..2]->(p2:Package)
223-
WHERE p1.project_id = 'my-awesome-project'
224-
AND p2.project_id = 'my-awesome-project'
225-
RETURN path
226-
LIMIT 50
227-
```
228-
229-
#### **Code Complexity Hotspots**
230-
231-
```cypher
232-
// Find the most connected functions (potential complexity hotspots)
200+
// Find the most connected functions
233201
MATCH (f:Function)
234202
WHERE f.project_id = 'my-awesome-project'
235-
WITH f,
236-
size((f)-[:CALLS]->()) as outgoing_calls,
237-
size((f)<-[:CALLS]-()) as incoming_calls
238-
RETURN f.name, f.package, outgoing_calls, incoming_calls,
239-
(outgoing_calls + incoming_calls) as total_connections
203+
OPTIONAL MATCH (f)-[:CALLS]->(called)
204+
WITH f, count(called) as outgoing_calls
205+
OPTIONAL MATCH (f)<-[:CALLS]-(caller)
206+
RETURN f.name, f.package, outgoing_calls, count(caller) as incoming_calls,
207+
(outgoing_calls + count(caller)) as total_connections
240208
ORDER BY total_connections DESC
241209
LIMIT 20
242210
```
@@ -253,63 +221,22 @@ LIMIT 20
253221

254222
**Useful Browser Commands:**
255223

256-
```cypher
257-
// Show node labels and relationship types
258-
CALL db.labels()
259-
CALL db.relationshipTypes()
260-
261-
// Count nodes by type
262-
MATCH (n)
263-
WHERE n.project_id = 'my-awesome-project'
264-
RETURN labels(n)[0] as NodeType, count(n) as Count
265-
ORDER BY Count DESC
266-
267-
// Find circular dependencies
268-
MATCH path = (p:Package)-[:IMPORTS*2..]->(p)
269-
WHERE p.project_id = 'my-awesome-project'
270-
RETURN path
271-
LIMIT 10
272-
```
224+
See [docs/QUERIES.md](docs/QUERIES.md) for more queries including:
225+
- Node and relationship type discovery
226+
- Circular dependency detection
227+
- Package dependency analysis
228+
- Test coverage analysis
229+
- And many more!
273230

274231
### 5. Advanced Analysis Examples
275232

276-
```bash
277-
# Find unused functions
278-
gograph query "
279-
MATCH (f:Function)
280-
WHERE f.project_id = 'my-awesome-project'
281-
AND NOT (f)<-[:CALLS]-()
282-
AND NOT f.name = 'main'
283-
RETURN f.name, f.package, f.file
284-
ORDER BY f.package, f.name
285-
"
286-
287-
# Analyze test coverage by package
288-
gograph query "
289-
MATCH (p:Package)
290-
WHERE p.project_id = 'my-awesome-project'
291-
OPTIONAL MATCH (p)-[:CONTAINS]->(f:File)
292-
WHERE f.name ENDS WITH '_test.go'
293-
WITH p, count(f) as test_files
294-
OPTIONAL MATCH (p)-[:CONTAINS]->(f2:File)
295-
WHERE NOT f2.name ENDS WITH '_test.go'
296-
RETURN p.name, count(f2) as source_files, test_files,
297-
CASE WHEN count(f2) > 0
298-
THEN round(100.0 * test_files / count(f2), 2)
299-
ELSE 0 END as test_ratio
300-
ORDER BY test_ratio DESC
301-
"
302-
303-
# Find interface implementations
304-
gograph query "
305-
MATCH (s:Struct)-[:IMPLEMENTS]->(i:Interface)
306-
WHERE s.project_id = 'my-awesome-project'
307-
RETURN i.name as Interface,
308-
collect(s.name) as Implementations,
309-
count(s) as ImplementationCount
310-
ORDER BY ImplementationCount DESC
311-
"
312-
```
233+
For advanced analysis queries, see [docs/QUERIES.md](docs/QUERIES.md) which includes:
234+
- Finding unused functions
235+
- Analyzing test coverage by package
236+
- Finding interface implementations
237+
- Detecting circular dependencies
238+
- Identifying code complexity hotspots
239+
- And many more analysis patterns!
313240

314241
### 6. Export and Share Results
315242

@@ -569,33 +496,26 @@ export GOGRAPH_MCP_PORT=8080
569496
| `IMPLEMENTS` | Struct implements interface |
570497
| `HAS_METHOD` | Struct/interface has method |
571498
| `DEPENDS_ON` | File depends on another file |
572-
| `DECLARES` | File declares function/struct/interface |
499+
| `DEFINES` | File defines function/struct/interface |
573500
| `USES` | Function uses variable/constant |
574501

575502
### Example Queries
576503

577-
```cypher
578-
-- Find all functions in a package
579-
MATCH (p:Package {name: "main"})-[:CONTAINS]->(f:File)-[:DECLARES]->(fn:Function)
580-
RETURN fn.name, fn.signature
581-
582-
-- Find circular dependencies
583-
MATCH path=(p1:Package)-[:IMPORTS*]->(p1)
584-
RETURN path
504+
For a comprehensive collection of Cypher queries organized by use case, see [docs/QUERIES.md](docs/QUERIES.md).
585505

506+
Quick examples:
507+
```cypher
586508
-- Find most called functions
587509
MATCH (f:Function)<-[:CALLS]-(caller)
510+
WHERE f.project_id = 'my-project'
588511
RETURN f.name, count(caller) as call_count
589512
ORDER BY call_count DESC
513+
LIMIT 10
590514
591-
-- Find unused functions
592-
MATCH (f:Function)
593-
WHERE NOT (f)<-[:CALLS]-()
594-
RETURN f.name, f.package
595-
596-
-- Find interface implementations
515+
-- Find interface implementations
597516
MATCH (s:Struct)-[:IMPLEMENTS]->(i:Interface)
598-
RETURN s.name, i.name
517+
WHERE s.project_id = 'my-project'
518+
RETURN i.name as Interface, collect(s.name) as Implementations
599519
```
600520

601521
## 🤖 MCP Integration
@@ -817,6 +737,7 @@ Inspired by:
817737
## 🔗 Links
818738

819739
- [Documentation](docs/)
740+
- [Query Reference Guide](docs/QUERIES.md)
820741
- [MCP Integration Guide](docs/MCP_INTEGRATION.md)
821742
- [Issue Tracker](https://github.com/compozy/gograph/issues)
822743
- [Discussions](https://github.com/compozy/gograph/discussions)

cmd/gograph/commands/analyze.go

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,22 @@ The resulting graph allows you to:
8080

8181
// Initialize parser configuration from config
8282
parserConfig := &parser.Config{
83-
IgnoreDirs: cfg.Analysis.IgnoreDirs,
84-
IgnoreFiles: cfg.Analysis.IgnoreFiles,
85-
IncludeTests: cfg.Analysis.IncludeTests,
86-
IncludeVendor: cfg.Analysis.IncludeVendor,
87-
MaxConcurrency: cfg.Analysis.MaxConcurrency,
83+
IgnoreDirs: cfg.Analysis.IgnoreDirs,
84+
IgnoreFiles: cfg.Analysis.IgnoreFiles,
85+
IncludeTests: cfg.Analysis.IncludeTests,
86+
IncludeVendor: cfg.Analysis.IncludeVendor,
87+
EnableSSA: true,
88+
EnableCallGraph: true,
8889
}
8990

91+
// Initialize analyzer configuration with defaults
9092
// Initialize analyzer configuration with defaults
9193
analyzerConfig := analyzer.DefaultAnalyzerConfig()
9294

95+
// Enable SSA for better analysis
96+
parserConfig.EnableSSA = true
97+
parserConfig.EnableCallGraph = true
98+
9399
// Initialize Neo4j configuration from config with fallback to defaults
94100
neo4jURI := cfg.Neo4j.URI
95101
if neo4jURI == "" {
@@ -150,8 +156,16 @@ func runAnalysisWithoutProgress(
150156
if err != nil {
151157
return fmt.Errorf("failed to parse project: %w", err)
152158
}
159+
160+
// Count total files from packages
161+
totalFiles := 0
162+
for _, pkg := range parseResult.Packages {
163+
totalFiles += len(pkg.Files)
164+
}
165+
153166
logger.Info("parsing completed",
154-
"files", len(parseResult.Files),
167+
"packages", len(parseResult.Packages),
168+
"files", totalFiles,
155169
"duration_ms", parseResult.ParseTime)
156170

157171
// -----
@@ -160,8 +174,8 @@ func runAnalysisWithoutProgress(
160174
logger.Info("analyzing project structure")
161175
analyzerService := analyzer.NewAnalyzer(analyzerConfig)
162176
analysisInput := &analyzer.AnalysisInput{
163-
ProjectID: projectID.String(),
164-
Files: parseResult.Files,
177+
ProjectID: projectID.String(),
178+
ParseResult: parseResult,
165179
}
166180
report, err := analyzerService.AnalyzeProject(ctx, analysisInput)
167181
if err != nil {
@@ -255,9 +269,15 @@ func runAnalysisWithProgress(
255269
// Success with detailed statistics
256270
successMsg := "Analysis completed successfully!"
257271

272+
// Count total files from packages
273+
totalFiles := 0
274+
for _, pkg := range parseResult.Packages {
275+
totalFiles += len(pkg.Files)
276+
}
277+
258278
// Create detailed statistics
259279
stats := progress.AnalysisStats{
260-
Files: len(parseResult.Files),
280+
Files: totalFiles,
261281
Nodes: len(graphResult.Nodes),
262282
Relationships: len(graphResult.Relationships),
263283
Interfaces: len(report.InterfaceImplementations),
@@ -289,7 +309,17 @@ func runParsingPhase(
289309
progressIndicator.Error(fmt.Errorf("failed to parse project: %w", err))
290310
return nil, fmt.Errorf("failed to parse project: %w", err)
291311
}
292-
progressIndicator.UpdateProgress(0.25, fmt.Sprintf("Parsed %d files", len(parseResult.Files)))
312+
313+
// Count total files from packages
314+
totalFiles := 0
315+
for _, pkg := range parseResult.Packages {
316+
totalFiles += len(pkg.Files)
317+
}
318+
319+
progressIndicator.UpdateProgress(
320+
0.25,
321+
fmt.Sprintf("Parsed %d files in %d packages", totalFiles, len(parseResult.Packages)),
322+
)
293323
return parseResult, nil
294324
}
295325

@@ -305,8 +335,8 @@ func runAnalysisPhase(
305335

306336
analyzerService := analyzer.NewAnalyzer(analyzerConfig)
307337
analysisInput := &analyzer.AnalysisInput{
308-
ProjectID: projectID.String(),
309-
Files: parseResult.Files,
338+
ProjectID: projectID.String(),
339+
ParseResult: parseResult,
310340
}
311341
progressIndicator.UpdateProgress(0.4, "Analyzing structure and dependencies")
312342

0 commit comments

Comments
 (0)