socio-graph
Socio-technical graph of software evolution — GitHub to Neo4j.
Swipe right to explore the queries. Click any query block to put it in the editor, then press the run button.
Commit is the joint between the technical graph
(Package, File, FilePart) and the social graph
(Person, MergeRequest, Review,
WorkItem, Activity).
This database holds several repos. Pick a repo first (change the name if needed),
because queries 3–14 and 16–21 use the $repo parameter:
:param repo => 'pallets/click'
MATCH (r:Repo) RETURN r.name AS repo, r.status AS status ORDER BY repo
CALL db.schema.visualization()
1. Node count per label
MATCH (n) RETURN labels(n)[0] AS label, count(*) AS n ORDER BY label
2. Relationship count per type
MATCH ()-[r]->() RETURN type(r) AS rel, count(*) AS n ORDER BY rel
3. Socio-technical joint evidence: files touched by the most distinct people
MATCH (p:Person)-[:AUTHORED]->(:Commit)-[:MODIFIES]->(f:File {repo: $repo})
RETURN f.repo AS repo, f.path AS file, count(DISTINCT p) AS people
ORDER BY people DESC, file
LIMIT 10
4. Temporal evidence: commits per month, last 24 months
MATCH (c:Commit)-[:IN_REPO]->(:Repo {name: $repo})
WHERE c.authored_ts >= datetime() - duration({months: 24})
RETURN date.truncate('month', c.authored_ts) AS month, count(*) AS commits
ORDER BY month
5. Identity bridge coverage: how many git commits have an author resolved to a GitHub account
MATCH (c:Commit)-[:IN_REPO]->(:Repo {name: $repo})
WHERE c.authored_ts IS NOT NULL
OPTIONAL MATCH (p:Person)-[:AUTHORED]->(c)
RETURN count(c) AS git_commits, count(p) AS resolved_authors
6. One merge request with its commits, files, opener, and reviewers (for Neo4j Browser screenshots)
MATCH (m:MergeRequest {repo: $repo})
WHERE EXISTS { (m)-[:CONTAINS]->(:Commit)-[:MODIFIES]->(:File {repo: $repo}) } AND EXISTS { (:Review)-[:ON]->(m) }
WITH m ORDER BY m.number DESC LIMIT 1
MATCH changes = (m)-[:CONTAINS]->(:Commit)-[:MODIFIES]->(:File {repo: $repo})
OPTIONAL MATCH review = (:Person)-[:GAVE]->(:Review)-[:ON]->(m)
OPTIONAL MATCH opened = (:Person)-[:OPENED]->(m)
RETURN changes, review, opened
7. Socio-technical joints at Package level: directories touched by the most distinct people
MATCH (p:Person)-[:AUTHORED]->(:Commit)-[:MODIFIES]->(:File)<-[:CONTAINS]-(pkg:Package {repo: $repo})
RETURN pkg.repo AS repo, pkg.path AS package, count(DISTINCT p) AS people
ORDER BY people DESC, package
LIMIT 10
8. Work Item coverage: how many merged merge requests link to an issue
MATCH (m:MergeRequest {repo: $repo})
WHERE m.state = 'MERGED'
RETURN count(m) AS merged_mrs,
count(CASE WHEN EXISTS { (m)-[:RELATES_TO {kind: 'closes'}]->(:WorkItem) } THEN 1 END) AS closes_issue,
count(CASE WHEN EXISTS { (m)-[:RELATES_TO]->(:WorkItem) } THEN 1 END) AS relates_to_issue
9. Activity distribution: count per kind, and how many come from bots
MATCH (a:Activity {repo: $repo})
OPTIONAL MATCH (p:Person)-[:PERFORMED]->(a)
RETURN a.kind AS kind, coalesce(a.event, '-') AS event,
count(*) AS n, count(CASE WHEN p.is_bot THEN 1 END) AS by_bot
ORDER BY n DESC, kind, event
LIMIT 15
10. Most active discussants (issue comments + review comments), bots separated
MATCH (p:Person)-[:PERFORMED]->(a:Activity {repo: $repo})
WHERE a.kind IN ['comment', 'review_comment']
RETURN p.login AS person, coalesce(p.is_bot, false) AS is_bot, count(*) AS comments
ORDER BY comments DESC, person
LIMIT 10
11. Function-level socio-technical joints: file parts touched by the most distinct people
MATCH (p:Person)-[:AUTHORED]->(:Commit)-[:MODIFIES]->(fp:FilePart {repo: $repo})
RETURN fp.repo AS repo, fp.id AS part, fp.kind AS kind, count(DISTINCT p) AS people
ORDER BY people DESC, part
LIMIT 10
12. FilePart coverage: how many Python files have file parts, and how many parts per kind
MATCH (fp:FilePart {repo: $repo})
WITH count(*) AS parts, count(DISTINCT fp.path) AS files_with_parts
MATCH (f:File {repo: $repo}) WHERE f.path ENDS WITH '.py'
RETURN parts, files_with_parts, count(f) AS python_files
13. Titled sections touched by the most distinct people (e.g. release sections in CHANGES)
MATCH (p:Person)-[:AUTHORED]->(:Commit)-[:MODIFIES]->(ts:TitledSection {repo: $repo})
RETURN ts.repo AS repo, ts.path AS file, ts.qualname AS section, count(DISTINCT p) AS people
ORDER BY people DESC, file, section
LIMIT 10
14. Who modified each class
MATCH (p:Person)-[:AUTHORED]->(c:Commit)-[:MODIFIES]->(fp:FilePart {repo: $repo, kind: 'class'})
RETURN fp.repo AS repo, fp.id AS class, collect(DISTINCT p.login) AS people,
count(DISTINCT p) AS n_people, count(DISTINCT c) AS commits
ORDER BY n_people DESC, commits DESC, class
LIMIT 20
15. People who contributed to more than one repo (cross-repo)
MATCH (p:Person)-[:AUTHORED]->(:Commit)-[:IN_REPO]->(r:Repo) WITH DISTINCT p, r.name AS repo ORDER BY repo WITH p, collect(repo) AS repos WHERE size(repos) > 1 RETURN p.login AS person, repos, size(repos) AS n_repos ORDER BY n_repos DESC, person LIMIT 20
16. Graph: contributors of the class touched by the most distinct people, via their commits
MATCH (c:Class {repo: $repo})<-[:MODIFIES]-(:Commit)<-[:AUTHORED]-(p:Person)
WITH c, count(DISTINCT p) AS people ORDER BY people DESC LIMIT 1
MATCH path = (:Person)-[:AUTHORED]->(:Commit)-[:MODIFIES]->(c)
RETURN path
LIMIT 100
17. Graph: structure of the file with the most parts (package, file, class/function, method)
MATCH (f:File {repo: $repo})-[:CONTAINS]->(:FilePart)
WITH f, count(*) AS parts ORDER BY parts DESC LIMIT 1
MATCH path = (:Package {repo: $repo})-[:CONTAINS]->(f)-[:CONTAINS*1..2]->(:FilePart)
RETURN path
LIMIT 150
18. Graph: collaborators of the top contributor (other people who changed the same files)
MATCH (p:Person)-[:AUTHORED]->(:Commit)-[:IN_REPO]->(:Repo {name: $repo})
WITH p, count(*) AS commits ORDER BY commits DESC LIMIT 1
MATCH path = (p)-[:AUTHORED]->(:Commit)-[:MODIFIES]->(:File {repo: $repo})<-[:MODIFIES]-(:Commit)<-[:AUTHORED]-(other:Person)
WHERE other <> p
RETURN path
LIMIT 40
19. Graph: issues closed by a merge request, down to the changed files
MATCH path = (w:WorkItem {repo: $repo})<-[:RELATES_TO {kind: 'closes'}]-(:MergeRequest)-[:CONTAINS]->(:Commit)-[:MODIFIES]->(:File)
WITH path, w ORDER BY w.number DESC
RETURN path
LIMIT 25
20. Graph: discussion on the most-commented merge request
MATCH (m:MergeRequest {repo: $repo})<-[:ON]-(a:Activity)
WHERE a.kind IN ['comment', 'review_comment']
WITH m, count(a) AS comments ORDER BY comments DESC LIMIT 1
MATCH path = (:Person)-[:PERFORMED]->(a:Activity)-[:ON]->(m)
WHERE a.kind IN ['comment', 'review_comment']
RETURN path
LIMIT 100
21. Graph: identity bridge, people with more than one git identity
MATCH (g:GitIdentity)-[:IDENTIFIES]->(p:Person)
WHERE EXISTS { (p)-[:AUTHORED]->(:Commit)-[:IN_REPO]->(:Repo {name: $repo}) }
WITH p, count(g) AS identities WHERE identities > 1
MATCH path = (:GitIdentity)-[:IDENTIFIES]->(p)
RETURN path
LIMIT 60