Die Realisierung einer Volltextdatenbank mit MySQL und Java ist einfach. Ein Teilnehmer meines Seminars „MySQL – Vom Anfänger zum Experten“ hat mich auf die Idee gebracht. Der Teilnehmer wünschte sich eine Datenbank, die ihm hilft, die Videos zu bestimmten Aspekten des Seminars schnell wiederfinden zu können. Bei 151 Lektionen kann der Überblick, in welcher Lektion was genau behandelt wurde, tatsächlich schnell verloren gehen.
Nun, ich habe mir diesen geäußerten Wunsch zu eigen gemacht. Ich skizziere hier, wie man sich eine Volltextdatenbank mit MySQL und Java zur Stichwortsuche schnell und einfach implementieren kann. Das hier vorgestellte Projekt stellt keine vollumfängliche Anwendung dar. Der Code mag vielmehr Anregung für alle Interessierten sein, sich eine eigene Anwendung, ganz nach den persönlichen Bedürfnissen, zu bauen.
Anwendungsanforderungen
Es soll eine Datenbank entwickelt werden, die
- Stichworte,
- Erläuterungen und einen
- Verweis auf die Seminarlektion
enthält. Die Datenbank soll mittels Volltextsuche durchsucht werden können. Das Suchen soll von der Kommandozeile initiiert werden können, ohne erst ein anderes Datenbankwerkzeug starten zu müssen.
Das sind die Minimalanforderungen. Ohne viel Phantasie, kann sich jeder ausmalen, die gespeicherten Daten auszuweiten. So wäre es durchaus sinnvoll, die Lehrinhalte verschiedener Seminare in der Datenbank zu speichern. Ein entsprechender Hinweis auf das Seminar wäre dann wünschenswert. Ebenfalls denkbar ist es, Bücher, Zeitschriftenartikel mit entsprechenden Quellenverweisen zu integrieren. – Der Phantasie sind da kaum Grenzen gesetzt.
An dieser Stelle werde ich lediglich ein einfaches Client-Programm vorstellen, mit dem die Daten aus der Datenbank abgefragt werden können. Auch dieses mag ausgebaut werden. Es könnte dahingehend ausgestaltet werden, auch Datensätze darüber pflegen zu können und eine grafische Benutzeroberfläche wäre auch schön. Ebenfalls verlockend können verschiedene Ausgabeformate sein, wie beispielsweise bibtex, um Literaturangaben in LaTeX übernehmen zu können.
Systemanforderungen
Um eine Stichwortdatenbank mit MySQL und Java erstellen zu können, brauchen wir mindestens
- Zugang zu einem MySQL-Server
- und eine Java-Entwicklungsumgebung (JDK).
Den MySQL-Server können wir uns als Community-Edition von der MySQL-Homepage herunterladen. Dort finden wir ebenso den Java-Connector für MySQL (Connector/J). Weil ein Terminalprogramm seine Parameter üblicherweise in Form von Kommandozeilenargumenten empfängt, müssen wir in der Lage sein, die Kommandozeile zu parsen. Dazu habe ich hier commons-cli von Apache verwendet. Zu alledem brauchen wir natürlich auch noch das JDK.
Hier ist die Zusammenstellung der Links, zu den benötigten Ingredienzien:
Statt des hier verlinkten Oracle JDK kann natürlich auch OpenJDK verwendet werden.
Wenn nicht bereits geschehen, sind der MySQL-Server und auch das JDK zu installieren. Der Java-Connector beziehungsweise der Kommandozeilen-Perser sind im Klassensuchpfad für Java aufzunehmen. Unter macOS/Linux sieht das so aus:
1 |
export CLASSPATH=".:$HOME/Lib/Prog/mysql-connector.jar:$HOME/Lib/Prog/commons-cli.jar" |
Die angegebenen Namen sind bei mir Links auf die echten Archivdateien, deren Namen etwas länger sind. Bei macOS sollte dies in der .bash_profile
, unter Linux in der .profile
stehen. Windows-Anwender setzen den Klassenpfad unter den Umgebungsvariablen in der Systemsteuerung.
Datenbank anlegen
Die Anforderungen an unsere Stichwort-Datenbank sind (im Moment) sehr gering. Es reicht eine einzige Tabelle. Um sie jedoch von anderen Projekten abzugrenzen, soll dieser Tabelle eine eigene Datenbank spendiert werden.
Wird das Projekt weiter ausgebaut, wird es im Mehrbenutzerbetrieb mit unterschiedlichen Berechtigungen für die Anwender verwendet, mag eine dedizierte Datenbank tatsächlich wünschenswert sein.
Hier nun das SQL-Skript. Es erzeugt eine Datenbank, legt die (derzeit einzige) Tabelle und einen Beispielanwender namens stwsucher an. Dieser Anwender kann und darf lediglich Daten abfragen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
DROP DATABASE IF EXISTS stichworte; -- ACHTUNG: bestehende Datenbank wird gelöscht!!! CREATE DATABASE stichworte CHARACTER SET utf8 COLLATE utf8_german2_ci; USE stichworte; DROP TABLE IF EXISTS stichworte; CREATE TABLE stichworte ( FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY, stichwort VARCHAR(100) NOT NULL, beschreibung VARCHAR(2000), lektion INT UNSIGNED ); CREATE FULLTEXT INDEX ft_idx ON stichworte (stichwort, beschreibung); -- ein paar Beispieldatensätze INSERT INTO stichworte (stichwort, beschreibung, lektion) VALUES ('create database', 'Anlegen einer Datenbank', 8), ('create table', 'Tabelle erstellen', 10), ('create table', 'Tabelle mit mehreren Spalten erstellen', 11), ('null', 'NULL-Werte', 12); -- Beispielanwender anlegen DROP USER IF EXISTS 'stwsucher'@'localhost'; CREATE USER 'stwsucher'@'localhost' IDENTIFIED BY '1234'; GRANT SELECT ON stichworte.stichworte TO 'stwsucher'@'localhost'; |
Dieses Skript beherbergt wenig Geheimnisse. Nennenswert ist jedoch das Attribut FTS_DOC_ID
. Das Attribut wird von MySQL benötigt, um eine Volltextsuche zu gestatten beziehungsweise einen Volltextindex zu erstellen. Fehlt es, wird es von MySQL automatisch angelegt. Die Erzeugung des Volltextindizes wirft dann eine Warnung aus. Der Volltextindex wird wie jeder andere Index auch erzeugt. Allerdings wird das Schlüsselwort FULLTEXT
verwendet. Im Beispiel werden die Spalten stichwort
und beschreibung
indiziert.
MySQL unterstützt verschiedene Formen der Volltextsuche. Diese lassen sich in der MySQL-Dokumentation unter der Überschrift Full-Text Search Functions nachlesen. Das hier vorgestellte Java-Programm verwendet lediglich die Modi NATURAL LANGUAGE und BOOLEAN. Bei der booleschen Suche stehen die folgende Operatoren zur Verfügung:
+
bedeutetAND
-
bedeuteNOT
- [kein Operator] bedeutet
OR
Damit lassen sich dann zum Beispiel folgende Abfragen formulieren:
1 2 3 4 |
SELECT stichwort, beschreibung, lektion FROM stichworte WHERE MATCH (stichwort, beschreibung) AGAINST ('Tabelle viele Spalten' IN NATURAL LANGUAGE MODE) |
stichwort | beschreibung | lektion |
---|---|---|
create table | Tabelle mit mehreren Spalten erstellen | 11 |
create table | Tabelle erstellen | 10 |
1 2 3 |
SELECT stichwort, beschreibung, lektion FROM stichworte WHERE MATCH (stichwort, beschreibung) AGAINST ('+Tabelle viele +Spalten' IN BOOLEAN MODE); |
stichwort | beschreibung | lektion |
---|---|---|
create table | Tabelle mit mehreren Spalten erstellen | 11 |
Die zweite Abfrage ist konkreter, weil sie zwingend das gemeinsame Vorkommen zweier Worte fordert.
Java-Client
Der folgende Programmcode zeigt beispielhaft den grundsätzlichen Aufbau eines Java-Programms, welches auf eine MySQL-Datenbank zugreift. Es verbleibt noch viel Luft nach oben. Fest im Programmcode verankerte Benutzerdaten, und andere Dinge mehr, sind kein guter Stil. Zur Verdeutlichung mag der Code aber ausreichen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
/* Keyword - einfaches Suchprogramm fuer Stichworte ... kein perfektes Programm, mehr eine Anregung fuer all diejenigen, die Lust und Laune haben mehr daraus zu machen! Hinweis: MySQL-Datenbank und Anwender muessen bestehen (stichworte.sql) Autor..: Karsten Brodmann (kb@punkt-akademie.de) Version: 0.1 Datum..: 2017-11-01 */ import java.sql.*; import org.apache.commons.cli.*; public class Keyword { // JDBC-Treiber Verbindungsdaten static final String JDBCDRIVER = "com.mysql.jdbc.Driver"; static final String DBURL = "jdbc:mysql://localhost/stichworte?useSSL=false"; static final String USER = "stwsucher"; static final String PASS = "1234"; // Datenbankverbindung Connection conn = null; Statement stmt = null; ResultSet rs = null; // Programmparameter String searchfor; boolean booleanmode = false; // Benutzerhinweise private static void printUsage(Options opts) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("Keyword", opts); System.exit(1); } // DB-Verbindung herstellen private void getConnection() throws ClassNotFoundException, SQLException { Class.forName(JDBCDRIVER); conn = DriverManager.getConnection(DBURL,USER,PASS); } // Programmoptionen auslesen, im Fehlerfall Programm beenden // gültige Optionen: -s --search (Suchstring) // -b --bool (boolesche Suche) private void getOptions(String[] args) { Options options = new Options(); Option searchstring = new Option("s", "search", true, "searchstring"); searchstring.setRequired(true); options.addOption(searchstring); Option booleansearch = new Option("b", "bool", false, "boolean search"); booleansearch.setRequired(false); options.addOption(booleansearch); CommandLineParser parser = new DefaultParser(); CommandLine cmd = null; try { cmd = parser.parse(options, args); } catch (ParseException e) { System.out.println(e.getMessage()); printUsage(options); // beendet Programm } searchfor = cmd.getOptionValue("s"); if(cmd.hasOption("b")) { booleanmode = true; } } // SQL-Statement basteln private String createSQL(String searchfor, boolean booleanmode) { String sql; sql = "SELECT stichwort, beschreibung, lektion "; sql = sql + "FROM stichworte "; sql = sql + "WHERE MATCH (stichwort, beschreibung) "; sql = sql + "AGAINST ('" + searchfor; if (booleanmode) { sql = sql + "' IN BOOLEAN MODE)"; } else { sql = sql + "' IN NATURAL LANGUAGE MODE)"; } return sql; } // Ergebnisse ausgeben private void printResult(int no, String kw, String desc, String lec) { String hline = "----------------------------------------"; if (no == 1) { System.out.println(hline); } System.out.println("Treffer.....: " + no); System.out.println("Stichwort...: " + kw); System.out.println("Beschreibung: " + desc); System.out.println("Lektion.....: " + lec); System.out.println(hline); } // Ergebnisse aus DB abrufen private void getResults() throws SQLException { stmt = conn.createStatement(); rs = stmt.executeQuery(createSQL(searchfor, booleanmode)); int no = 0; while(rs.next()) { no++; printResult(no, rs.getString("stichwort"), rs.getString("beschreibung"), rs.getString("lektion")); } System.out.println("(" + no + " Treffer insgesamt)" ); rs.close(); } // Aufraeumarbeiten private void cleanup() { try { if (stmt!=null) stmt.close(); } catch(SQLException e) { e.printStackTrace(); } try { if (conn!=null) conn.close(); } catch(SQLException e) { e.printStackTrace(); } } // Hauptprogramm public static void main(String[] args) { Keyword kw = new Keyword(); kw.getOptions(args); try { kw.getConnection(); kw.getResults(); kw.cleanup(); } catch(SQLException e) { // JDBC-Fehler abfangen e.printStackTrace(); } catch(Exception e) { // andere Fehler abfangen e.printStackTrace(); } finally { // auf jeden Fall aufraeumen kw.cleanup(); } } } |
Das Programm zeigt ganz rudimentär die erforderlichen Schritte, sich mit einer MySQL-Datenbank zu verbinden und Daten abzufragen.
Eine Beispielsitzung unter macOS oder Linux könnte so ausschauen:
1 2 3 4 5 6 7 8 9 |
$ java Keyword --search '+Tabelle viele +Spalten' --bool ---------------------------------------- Treffer.....: 1 Stichwort...: create table Beschreibung: Tabelle mit mehreren Spalten erstellen Lektion.....: 11 ---------------------------------------- (1 Treffer insgesamt) $ _ |
Fazit
Der Bedarf an Optimierungen und Erweiterungen ist unübersehbar. Das Prinzip, wie eine Stichwortdatenbank mit MySQL und Java aufgebaut und implementiert werden kann, sollte aber offensichtlich sein. – Es ist nicht schwierig.
Laden Sie hier den Beispielcode herunter.
Auf Basis der oben gezeigten Grundlagen können leicht weitere Features implementiert werden, so dass das Beispiel schnell zu einer „ausgewachsenen Anwendung“ wachsen kann. – Viel Spaß dabei!
Karsten Brodmann