Durchsuchen der Datenbank
Ziel dieser Suche ist es, die Datensätze zu filtern, sodass nur Bücher angezeigt werden, bei denen der Suchstring in einem der relevanten Textfelder eines Buchs oder Autors vorkommt. Wenn der Suchstring in einem Feld eines Authors-Datensatzes - und nicht in einem Feld eines Books-Datensatzes - vorkommt, so sollen alle Bücher dieses Autors angezeigt werden.
In der Abbildung unten sehen Sie die Ergebnisse einer Suche nach dem String "dark". Es werden mit dieser Suche zwei Bücher gefunden. In beiden Fällen kommt der Suchstring im Feld Title des Buchs vor.
Lokale Suche im Gegensatz zur Datenbanksuche
Die Suche kann in den aus der Datenbank heruntergeladenen Daten lokal auf dem Mobilgerät oder in der Datenbank auf dem Server durchgeführt werden. Jede dieser Methoden hat, wie unten erläutert, Vorteile gegenüber der anderen Methode.
•Wenn die Suche lokal durchgeführt wird, müssen alle Datensätze der Datenbank in den Arbeitsspeicher geladen und durchsucht werden. Dies geht schneller als bei Durchführung der Suche in der Datenbank, beansprucht aber v.a. bei großen Datenmengen mehr Arbeitsspeicher, da alle Datensätze in den Arbeitsspeicher geladen werden müssen.
•Wenn die Suche durch Senden eines SQL-Request an die Datenbank durchgeführt wird, dauern die Datenbanktransaktionen länger. Da jedoch nur die gefundenen Datensätze retourniert werden, wird weniger Arbeitsspeicher für die Speicherung der Daten benötigt.
Wägen Sie daher die Vor- und Nachteile ab, bevor Sie sich für eine bestimmte Methode entscheiden.
Methoden
Die Suche kann auf verschiedene Arten implementiert werden. Bei der Methode, die wir verwendet haben, wird eine SQL-Datenbanksuche mit einer lokalen MobileTogether-spezifischen Methode kombiniert. Sie ist unten in groben Zügen beschrieben.
1.Der Suchstring wird in ein Bearbeitungsfeld eingegeben.
2.Da das Bearbeitungsfeld mit dem Node SearchText der Seitenquelle $PERSISTENT verknüpft ist, wird der Suchtext automatisch an diesen Node übergeben, wenn der Suchtext in das Bearbeitungsfeld eingegeben wird (siehe markierter Node in der Abbildung oben)
3.Bei Klick auf die Schaltfläche Find werden der Reihe nach zwei Aktionen ausgeführt: (i) die Seitenquelle $BookCatalog wird mit nur denjenigen Authors-Datensätzen neu geladen, die den Suchstring entweder in den Daten des Autors oder in einem seiner Buch-Child-Elemente enthalten (siehe Punkt 4 weiter unten); (ii) mit Hilfe einer Node(s) löschen-Aktion werden diejenigen Bücher (des ausgewählten Autors) entfernt, die den Suchstring in keinem seiner Felder enthalten, so dass nur die Bucher dieses Autors, die den Suchstring enthalten, übrig bleiben (siehe Punkt 5 weiter unten).
4.Durch das Neuladen der Seitenquelle wird mittels einer SQL-Anweisung eine Suche nach dem Suchstring in der Datenbank ausgelöst. Die Seitenquelle $BookCatalog wird infolgedessen nur mit den relevanten Autoren (bei denen der Suchstring entweder in den Autorendaten oder in den Daten mindestens eines Buchs des Autors gefunden wurde) neu geladen. Nähere Informationen dazu finden Sie im Abschnitt SQL-Suche in der Datenbank weiter unten.
5.Da das Ergebnis der SQL-Suche in der Datenbank Autoren mit allen ihren Büchern sind, werden die Bücher, in denen der Suchstring nicht vorkommt, gelöscht. Nähere Informationen dazu finden Sie im Abschnitt SQL-Suche in der Datenbank weiter unten.
Der Suchstring
Es wurde ein Bearbeitungsfeld erstellt, in das der Benutzer den Suchstring eingeben kann. Das Bearbeitungsfeld wurde mit dem Node $PERSISTENT/Root/SearchText verknüpft (durch Ziehen des Node auf das Bearbeitungsfeld). Dadurch wird der Suchtext in diesem Node gespeichert. Von hier aus kann er anschließend für Aktionen aufgerufen werden.
Die Schaltfläche "Find"
Die Suche wird bei Klick auf die Schaltfläche Find gestartet. Die Aktionen des BeiSchaltflächenklick-Ereignisses der Schaltfläche (Abbildung unten) sind (i) das Neuladen der Seitenquelle $BookCatalog und (ii) die Aktion Node(s) löschen. Beide Aktionen werden weiter unten näher beschrieben. Im Moment genügt es zu wissen, dass die Aktion Neu laden für die Seitenquelle $BookCatalog die Suche in der Datenbank startet. Siehe nächster Abschnitt SQL-Suche in der Datenbank. Die Aktion Node(s) löschen wird im Abschnitt Entfernen nicht übereinstimmender Bücher beschrieben
SQL-Suche in der Datenbank
Bei Auslösung der Neu laden-Aktion für $BookCatalog wählt eine SQL-Anweisung (in der Datenauswahldefinition der Datenbank-Seitenquelle) alle Zeilen der Tabelle Authors aus, die (entweder im Authors-Datensatz selbst oder in einem der mit dem Datensatz verknüpften Books-Datensätze) ein Feld haben, das den Suchtext enthält. Dies wird folgendermaßen erreicht.
Konfiguration der Seitenquelle $BookCatalog
Die Seitenquelle $BookCatalog wurde ursprünglich so konfiguriert, dass alle Datensätze der Tabelle "Authors" ausgewählt werden, wobei jeder Authors-Datensatz damit verknüpfte Books-Datensätze als Children enthält. Wir fügen nun zur SQL-Anweisung eine WHERE-Klausel hinzu, um die Authors-Datensätze nach denjenigen zu filtern, in denen der Suchbegriff vorkommt. Da die WHERE-Klausel mit Hilfe eines komplexen XPath-Ausdrucks erstellt werden muss, verwenden wir zum Implementieren des XPath-Ausdrucks eine Funktion. Die Funktion hat den Namen DBSQLSearch() (siehe Abbildung unten). Sie sehen die SQL-Anweisung in der Abbildung im Bereich am unteren Rand des Fensters. In dieser SQL-Anweisung gibt die WHERE-Klausel den Wert der Funktion DBSQLSearch() zurück.
Anmerkung: | Um die Konfiguration der Seitenquelle $BookCatalog anzuzeigen oder zu bearbeiten, klicken Sie im Fenster "Seitenquellen" auf das Konfigurationssymbol von $BookCatalog. |
Funktionen für die Suche
Wir erstellen für die Suche zwei Funktionen: DBSQLSearch() und DBFieldsSearch(). Das Dialogfeld "XPath-Funktionen" zum Erstellen dieser Funktionen wird über den Menübefehl Projekt | XPath/XQuery-Funktionen aufgerufen und ist in der Abbildung unten zu sehen.
declare function DBSQLSearch() { let $search-text := $PERSISTENT/Root/SearchText return if ($search-text != '' ) then DBFieldsSearch( 'Authors', $search-text ) || ' OR Author_ID IN (SELECT AuthorID FROM Books WHERE ' || DBFieldsSearch( 'Books', $search-text ) || ')' else '' }
|
declare function DBFieldsSearch($table-name, $search-text) { string-join($SearchFields($table-name) ! ( . || ' LIKE ''%' || $search-text || '%''' ), ' OR ' ) }
|
map { 'Authors' : ('AuthorName', 'Website', 'Country'), 'Books' : ('Title', 'ISBN', 'Publisher', 'Year', 'Genre', 'Price') }
|
Die Funktion DBSQLSearch() führt folgende Schritte aus:
•Wenn es sich beim Suchtext nicht um einen leeren String handelt, wird eine SELECT-Anweisung erstellt, für die die Funktion DBFieldsSearch() zwei Mal verwendet wird: das erste Mal, um die Felder der Tabelle Authors zu durchsuchen, das zweite Mal, um die Felder der Tabelle Books zu durchsuchen.
•Mit Hilfe der Funktion DBFieldsSearch() wird die WHERE-Klausel erstellt, um jede Tabelle (Authors und Books) durch Referenzierung der $SearchFields-Zuordnung der einzelnen Spalten der Datenbanktabelle zu durchsuchen. Die $SearchFields-Zuordnung wird in Form einer benutzerdefinierten Variablen, die mit dem Menübefehl Projekt | Globale Variablen aufgerufen wird, gespeichert.
•Wenn es sich beim Suchtext um den leeren String handelt, so ist andernfalls, wie in der Funktion DBSQLSearch() definiert, die WHERE-Klausel der SQL-Anweisung für $BookCatalog der leere String (siehe DBSQLSearch()-Funktion oben). Tatsächlich hat die SQL-Anweisung in diesem Fall keine WHERE-Klausel und es werden alle Authors-Datensätze zurückgegeben.
Die SELECT-Anweisung, die erstellt wird, wenn der Suchstring nicht leer ist, hat die folgende Struktur:
SELECT <AuthorsFields-1 to AuthorsFields-Z> FROM "Authors"
WHERE AF1 LIKE '%SearchText%' OR AF2 LIKE '%SearchText%' ... OR AFZ LIKE '%SearchText%'
OR Author_ID IN (SELECT AuthorID FROM Books
WHERE BF1 LIKE '%SearchText%' OR BF2 LIKE '%SearchText%' ... OR BFZ LIKE '%SearchText%')
Mit diesem Ausdruck werden Authors-Datensätze ausgewählt, die entweder (i) irgendein Authors-Feld haben, das mit dem Suchtext übereinstimmt oder (ii) irgendeinen damit verknüpften Books-Datensatz mit einem Feld, das dem Suchtext entspricht, haben. Beachten Sie Folgendes: Wenn bei der Suche in den verknüpften Books-Datensätzen eine Entsprechung gefunden wird, so wird der übergeordnete Authors-Datensatz ausgewählt.
Wichtig dabei ist Folgendes: Da im Endeffekt die Authors-Datensätze ausgewählt werden, werden diese Authors-Datensätze mit allen ihren verknüpften Books-Datensätzen ausgewählt - selbst wenn einer oder mehrere dieser Books-Datensätze den Suchtext nicht enthalten. Eine Beschreibung dazu, wie nur die Bücher angezeigt werden, die den Suchtext enthalten, finden Sie im nächsten Abschnitt, Entfernen nicht übereinstimmender Bücher.
Entfernen nicht übereinstimmender Bücher
Da die ausgewählten Authors-Datensätze mit allen damit verknüpften Books-Child-Datensätzen zurückgegeben werden (siehe Erläuterung oben), gibt es die folgenden Möglichkeiten:
•Der Suchtext wird in der Tabelle Authors gefunden. In diesem Fall können wir die Autorinformationen zusammen mit allen damit verknüpften Books-Datensätzen dieses Autors anzeigen.
•Der Suchtext wird in einer der damit verknüpften Books-Tabellen eines Authors-Datensatzes gefunden. In diesem Fall sollten die Autorinformationen und die Informationen nur zu dem gefundenen Buch/den gefundenen Büchern angezeigt werden. Eine Methode dafür wäre, die nicht übereinstimmenden Bücher aus der Seitenquelle $BookCatalog zu entfernen. In unserem Beispiel haben wir zu diesem Zweck eine Node(s) löschen-Aktion zur Find-Schaltfläche hinzugefügt, nachdem die Seitenquelle neu geladen wurde (siehe Abbildung unten).
for $text in lower-case($PERSISTENT/Root/SearchText), $author in $BookCatalog/DB/RowSet/Row return $author/Books/Row [not(contains(lower-case(@Title), $text))] [not(contains(lower-case(@ISBN), $text))] [not(contains(lower-case(@Publisher), $text))] [not(contains(lower-case(@Genre), $text))] [not(contains(@Year, $text))] [not(contains(@Price, $text))] [not(contains(lower-case(../../@AuthorName), $text))] [not(contains(lower-case(../../@Website), $text))] [not(contains(lower-case(../../@Country), $text))]
|
Der XPath-Ausdruck der Aktion Node(s) löschen funktioniert folgendermaßen:
•Die Variable $text enthält eine in Kleinbuchstaben geschriebene Variante des Suchstrings. Dadurch wird die Groß- und Kleinschreibung bei der Suche nicht beachtet.
•Die für die Löschung retournierten Bücher werden aus den untergeordneten Books-Datensätzen des aktuellen Autors ausgewählt.
•Die für die Löschung ausgewählten Bücher dürfen den Suchstring in keinem der relevanten Books-Felder enthalten. Zu diesem Zweck wird eine Sequenz von Prädikat-Filtern erstellt, wobei jedes Prädikat in eckige Klammern gesetzt wird. Die Auswertung aller Prädikate muss für das Buch true ergeben, damit das Buch für die Löschung qualifiziert ist. Wenn die Auswertung auch nur eines einzigen Prädikats false ergibt (dies wäre der Fall, wenn der Suchstring in dem in diesem Prädikat überprüften Feld vorhanden ist), so wird der aktuelle Books-Datensatz für die Löschung nicht ausgewählt und es wird das nächste Buch überprüft.
•Beachten Sie, dass mit den Prädikat-Filtern nicht nur die Felder der Books-Datensätze, sondern auch die der übergeordneten Authors-Datensätze überprüft werden (siehe letzte drei Prädikate).
Löschen des Suchstrings
Nach Durchführung einer Suche enthält die Seitenquelle $BookCatalog nur die bei der oben beschriebenen Suche gefundenen Authors- und Books-Datensätze. Mit der Schaltfläche Clear (siehe erste Abbildung in diesem Kapitel) wird der Suchstring gelöscht und die Seitenquelle $BookCatalog neu geladen, sodass diese alle Authors-Datensätze enthält. Die Aktionen der Schaltfläche Clear sind in der Abbildung unten zu sehen.
Die Aktion Node(s) aktualisieren ändert den Node $PERSISTENT/Root/SearchText, sodass er den leeren String enthält. Da dieser Node mit dem Bearbeitungsfeld, in das der Suchtext eingegeben wird, verknüpft ist, wird im Bearbeitungsfeld der leere Stringwert angezeigt, d.h. der Inhalt des Bearbeitungsfelds wird gelöscht (siehe Abschnitt "Der Suchstring" oben). Wenn die Seitenquelle $BookCatalog neu geladen wird, wobei der Node $PERSISTENT/Root/SearchText auf den leeren String gesetzt ist, werden alle Authors-Datensätze in die Seitenquelle geladen (siehe Beschreibung der DBSQLSearch()-Funktion oben).