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