Graphendatenbank: Flexible Datenabfragen mit Neo4j

Fazit

Auffinden technischer Konstrukte

Es ist häufig der Fall, dass für ein Refactoring spezielle Stellen im Anwendungscode zu identifizieren sind, die mit IDE-Mitteln nur schwer aufzufinden sind, da die Menge potenzieller Suchergebnisse zu groß ist. Als Szenario soll dafür beispielhaft ein Problem aus einem realen Java-EE-Projekt (Java Enterprise Edition) beschrieben werden. Im Code der Anwendung hatten die Entwickler an verschiedenen Stellen per @Inject-Annotationen Instanzen eines Dienstes MyService injiziert. Alle derartigen Injection-Points sollten um eine weitere Annotation (genau genommen einen Qualifier) ergänzt werden:

public class MyClient {
@Inject
private MyService myService; // Injection-Point
...
}

Die IDE bot keine Unterstützung für das Auffinden von Injection-Points, eine Suche nach der Verwendung des Typs MyService lieferte leider nicht nur die gewünschten, sondern auch alle anderen Stellen, an denen der Typ zum Einsatz kam. Die folgende Cypher-Abfrage hingegen enthält alle benötigten Kontextinformationen und kommt entsprechend treffsicher zum gewünschten Ergebnis:

match
(type:TYPE)-[:DECLARES]->(field:FIELD),
(field)-[:OF_TYPE]->(serviceType:TYPE),
(field)-[:ANNOTATED_BY]->(inject:ANNOTATION),
(inject)-[:OF_TYPE]->(injectType:TYPE)
where
serviceType.FQN = 'com.buschmais.shop.user.ejb.MyService'
and injectType.FQN = 'javax.inject.Inject'
return
type.FQN, field.NAME

Auch wenn sich diese Abfrage über mehrere Zeilen erstreckt, lässt sie sich doch in deutlich kürzerer Zeit formulieren und ausführen, als es dauern würde, ungenaue Suchergebnisse manuell zu filtern. Für die Exploration einer existierenden Code-Basis können Entwickler so recht schnell aussagekräftige Ergebnisse gewinnen. Ein weiteres Beispiel hierfür ist die Frage nach allen Packages, in denen sich JPA-Entitäten befinden:

match
(package:PACKAGE)-[:CONTAINS]->(entity:TYPE),
(entity)-[:ANNOTATED_BY]->(a:ANNOTATION)-[:OF_TYPE]->(annotationType:TYPE)
where
annotationType.FQN='javax.persistence.Entity'
return
package.FQN as EntityPackage, count(entity) as NumberOfEntities

Eine Frage wurde bisher nicht beantwortet: Wie kommen die Daten in der geschilderten Struktur in die Graphendatenbank? Diesen Weg eröffnet das Open-Source-Werkzeug jQAssistant, das manuell oder eingebunden im Build-Prozess die erzeugten Artefakte eines Java-Projekts (also Packages, Klassen, Deskriptoren usw.) einliest, in einer Neo4j-Datenbank ablegt und anschließend die beschriebenen Abfragen ermöglicht. Letztere lassen sich zur Anreicherung des Datenmodells um Konzepte im oben beschrieben Sinne und zur automatischen Validierung projektspezifischer Regeln (z. B. Namenskonventionen) verwenden.

Fazit

Für einen Entwickler, der zwangsläufig über strukturelle Kenntnisse der von ihm verwendeten Programmiersprache verfügt, ist das Modell von Softwarestrukturen in einer Graph-Repräsentation nahezu intuitiv zu verstehen. Mit der von Neo4j angebotenen, schnell zu erlernenden Abfragesprache Cypher kann er gezielt Informationen über Strukturen und Konstrukte innerhalb eines Softwareprojekts gewinnen. Die Möglichkeiten reichen dabei von einfachen Metriken bis hin zu stark vom vorliegenden Kontext abhängigen Informationen. (ane)

Dirk Mahler
ist Senior-Consultant der buschmais GbR, einem Beratungshaus mit Sitz in Dresden. Der Schwerpunkt seiner Tätigkeit liegt im Bereich Architektur für Java-Applikationen im Unternehmensumfeld.

Literatur
  • Michael Hunger, Peter Neubauer; Die vernetzte Welt; Abfragesprachen für Graphendatenbanken; Artikel auf heise Developer
  • Rudolf Jansen; Beziehungsmanagement; Neo4j – NoSQL-Datenbank mit graphentheoretischen Grundlagen; Artikel auf heise Developer