Warning: include_once(/var/www/html/pmwiki-2.2.86/cookbook/soap4pmwiki/soap4pmwiki.php): failed to open stream: No such file or directory in /var/www/html/fields/dbp13/local/config.php on line 4

Warning: include_once(): Failed opening '/var/www/html/pmwiki-2.2.86/cookbook/soap4pmwiki/soap4pmwiki.php' for inclusion (include_path='.:/opt/php/lib/php') in /var/www/html/fields/dbp13/local/config.php on line 4

Warning: Cannot modify header information - headers already sent by (output started at /var/www/html/fields/dbp13/local/config.php:4) in /var/www/html/pmwiki-2.2.86/pmwiki.php on line 1250
Datenbankpraktikum SS 2013 - Datenvisualisierung - Search-Objekt
?

Dokumentation: Funktionalität der Suche

Christoph Knauft, Niels Hellwig

Grundsätzlicher Ansatz zur Implementation einer "Restful" und ROA-basierten Suche

Um mit einer Suche den Grundsätzen einer Ressourcen-orientierten Architektur (ROA) gerecht zu werden, muss auch eine Suche als ein Objekt betrachtet werden. Eine Suche kann dementsprechend mit einem POST-Request über die create-Methode des SearchesController angelegt werden. Dieses Suchobjekt speichert mehrere Daten, die mit der Suche übergeben werden. Die übergebenen Daten entsprechen den suchbaren Attributen aus den übrigen Models im ORM. Außerdem lassen sich über die Search-Category und die (optionale) Search-Series die beiden Dimensionen für die Highcharts-Diagramme auswählen (Search-Category: x-Achse, Search-Series: y-Achse). Um den verschiedenen Anforderungen der Visualisierung für Highcharts, Google Maps und Globe gerecht zu werden, werden die beiden Methoden "results_for_highcharts" und "results_for_maps" im Search-Model implementiert, die aus dem SearchHelper zur Übergabe aufgerufen werden.

Anlage einer neuen Suche über die vom Scaffold- Generator erzeugte View

Dynamische Erzeugung von SQL-Abfragen

Um eine möglichst performante Suche zu ermöglichen, sollte die auszuwertende Datenmenge jeder Abfrage möglichst klein gehalten werden. Dafür werden die Joins und Where-Bedingungen generisch erzeugt und anschließend nach der Search Category und Search Series gruppiert. Die Ergebnisse werden in einer Hashmap abgelegt. In den Keys ist die Gruppierung widergespiegelt, die Values sind die zugehörigen gezählten Ergebnisse.

Der Quellcode für die Highcharts-Repräsentation wird in der folgenden Abbildung dargestellt.

Im ersten Schritt werden die in der Suche ausgefüllten Felder ausgewertet und die korrespondierenden Klassen gesucht (dabei wird die Methode fetch_all_searchable_elements des SearchesController verwendet, deren Funktionalität analog zur nachfolgend beschriebenen Methode im GroupingController ist). In gleicher Weise wird mit den für die x- und y-Achse ausgewählten Attributen verfahren. Der GroupingController stellt alle für die Achsen auswählbaren Attribute als Hash zur Verfügung (mit der Methode fetch_all_groupable_elements), indem er auf die Controller der einzelnen Modellklassen (z. B. CountriesController) zugreift. Diese liefern alle gruppierbaren Elemente der eigenen Modellklasse. Die Einträge in der Hash haben folgende Form: Attribut: zugehörige Klasse. Aus diesen Klassen wird mit der Methode join_classes ein Relationsobjekt erstellt. Mit einer weiteren Methode add_where_clauses_to_relation werden die where-Bedingungen entsprechend der eingetragenen Suchfilter an die Relation hinzugefügt. Vervollständigt wird die Relation durch Anwendung der group-Methode mit den für die x- und y-Achse ausgewählten Attributen als Argumenten. Als SQL-Abfrage ausgewertet wird die Relation, indem die count-Methode aufgerufen wird. Gezählt wird jeweils die Anzahl der zur Gruppierung gehörenden Studenten (COUNT(students.id)). Dies liefert je nach Suchanfrage als Ergebnis die Anzahl an zutreffenden Studienköpfen, Studiengängen oder Studienfällen (siehe ER-Diagramm: Studienköpfe, Studiengänge, Studienfälle). Um der Highcharts- Visualisierung eine übersichtlichere Anzeige zu ermöglichen, wird die Relation vor dem Gruppieren noch nach der Anzahl der Suchergebnisse sortiert. Als Ergebnis liefert die Methode eine Hashmap zurück, die jeder möglichen Gruppierung aus x- und y-Achse die Studentenzahl zuordnet.

Beispiel: Kombination aus Geschlecht und Abschlussart

 ["W", "Kein Abschluss"]: 10360 
 ["M", "Diplom"]: 9105 
 ["M", "Kein Abschluss"]: 8266 
 ["W", "Bachelor"]: 7930 
 ["W", "Lehramt"]: 7786 
 ["W", "Diplom"]: 7578 
 ["M", "Bachelor"]: 5524 
 ["W", "Promotion"]: 5412 
 ["M", "Promotion"]: 5152 
 ["W", "Master"]: 2791 
 ["M", "Lehramt"]: 2760 
 ["M", "Master"]: 1572

Generische Joins

Die generischen Joins werden in der Methode join_classes durchgeführt.

Dabei liegt folgende Idee zugrunde: Die students sind Ausgangspunkt für jeden weiteren Join. Aus diesem Grund werden die filtered_result zunächst mit der Studentenklasse initialisiert. Jeder Controller, der zu einem Model gehört, kennt die von sich aus selbst joinbaren Klassen (seine direkten "Nachbarn") sowie alle nachfolgend joinbaren Klassen.

Der Algorithmus fragt zunächst von der aktuell betrachteten Klasse aus seine "Nachbarn", ob in dessen nachfolgenden Klassen die zu joinende Klasse vorhanden ist. Sollte diese Klasse in der Aufzählung vorhanden sein oder der "Nachbar" selbst die Klasse sein, wird der "Nachbar" an die Relation gejoint. Dabei wird die Information, dass dieser Join bereits erfolgt ist, gespeichert, um doppelte Joins zu verhindern. Auf diese Weise werden sukzessive die benötigten Klassen gejoint, bis die zu joinende Klasse erreicht wurde. Dieses Verfahren wird für alle zu joinenden Klassen wiederholt. Folgende Abbildung zeigt die konkrete Umsetzung des beschriebenen Algorithmus im Search-Modell:

Where-Bedingungen

In der Methode add_where_clauses_to_relation werden die Where-Bedingungen zur Relation hinzugefügt.Im einfachsten Fall wird aus der Menge der übergebenen Attribute für jedes Attribut eingetragene Wert ausgelesen (mittels der send-Methode) und als Where-Bedingung mit "=" hinzugefügt (der else-Zweig in der folgenden Abbildung). Bei einigen Attributen wird die Möglichkeit einer Mehrfachauswahl angeboten, diese werden im Array multiple_selectable_attributes gespeichert. Die Auswertung in der SQL-Abfrage erfolgt durch das Schlüsselwort in. Ein Sonderfall ergibt sich mit der Altersabfrage, die nach einem Mindest- und einem Maximalalter fragt. In der Datenbank wurde nur das Geburtsjahr abgelegt, daher werden hierfür zwei Where-Statements benötigt.

Der komplexeste Fall ist die Mehrfachauswahl für Studienfächer. Das letztendliche Kreuzprodukt der Relation würde durch die Verknüpfungstabelle von disciplines und studies sehr groß werden. Dies wird mit einem Algorithmus umgangen. Die Idee dahinter ist, nur die notwendigen Study-IDs für die Abfrage vorher auszufiltern. An dem nachfolgenden Beispiel wird der zugrunde liegende Algorithmus verdeutlicht.

Beispiel: Selektiere alle Studenten, die I und II studieren

Ausgangslage:

StudentStudyDiscipline
A1I
A1II
B2I
B2II
B3I
C4II
C4III

Schritt 1: Entferne alle Datensätze, die nicht I oder II enthalten

StudentStudyDiscipline
A1I
A1II
B2I
B2II
B3I
C4II

Schritt 2: Gruppiere nach Studies und zähle die Anzahl der vorhandenen Zeilen

StudentStudyAnzahl
A12
B22
B31
C41

Schritt 3: Entferne aller Zeilen, deren Spalte Anzahl einen niedrigeren Wert aufweist als die Summe der selektierten Studienfächer

StudentStudyAnzahl
A12
B22

Die so erhaltenen studies.ids werden in einem Array gespeichert, welches in einer Where-Anfrage an die Relation gehangen wird.

Group and Count

Das Gruppieren erfolgt mit der group-Methode an der Relation. Als Argumente werden die ausgewählten Attribute für die x- und y-Achsen verwendet. Mit der order-Methode wird eine Sortierung in absteigender Reihenfolge gewährleistet. Die count-Methode führt die Relation als Abfrage aus und speichert die Ergebnisse in einer HashMap ab.

Abweichungen bei der Erstellung von Maps-/Globe-Repräsentationen

Die Erstellung von Highcharts- und Maps-Repräsentationen ist sehr ähnlich. Der Algorithmus für die Maps-Repräsentation ist kürzer, es werden keine Attribute mehr für die x- und y-Achse benötigt. Stattdessen besteht die Gruppierung immer aus Land, Bundesland und Stadt. Aus diesem Grund müssen die zugehörigen Klassen immer gejoint werden. Speziell für die Bundesländer wird ein Outer Join zwischen Locations und FederalStates benötigt, da sonst nur deutsche Orte in der Ergebnismenge enthalten wären.


Page last modified on August 23, 2013, at 06:59 PM