Die String-Methode ist unsicher, da die Daten nicht bereinigt werden und so SQL-Injections möglich sind.
Die Array-Methode ist sicher und erinnert syntaktisch stark an Prepared Statements.
Die Hash-Methode ist ebenfalls sicher, aber typisch Rails, weil einfach: lediglich ein Hash wird übergeben. Der Schlüssel (key) als Symbol entspricht der Spaltenbezeichnungen der Tabelle und der Wert (value) definiert die Bedingung.
Das Schöne an der Hash-Methode ist neben der Sicherheit, dass das Hash wesentlich einfacher dynamisch berechnet werden kann (als die Array-Methode):
[code lang=“ruby“]def all_by_employee_or_manager(employee_id=nil, manager_id=nil)
condition = Hash.new
condition[:employee_id] = employee_id unless employee_id.blank?
condition[:manager_id] = manager_id unless manager_id.blank?
where(condition)
end[/code]
Das Problem: NoMethodError
Sobald jedoch in der Abfrage gejoint wird, bekommt man bei einer Hash-Bedingung wie
[code lang=“ruby“]joins(:employees).where(:employees.name => name)[/code]
folgenden Fehler:
[code]NoMethodError: undefined method `name‘ for :employees:Symbol[/code]
Das Problem ist, dass das Symbol :employees.name nicht komplett als Symbol interpretiert wird, sondern nur bis zum Punkt.
Bei der Internetrecherche habe ich für gejointe Abfragen nur Konditionen im String- oder Array-Format finden können, was in meinem Anwendungsfall aber nicht praktikabel war, denn die Konditionen mussten dynamisch berechnet werden.
Die Lösung
Um Konditionen in gejointen Abfragen mit der Hash-Methode anzugeben, muss Ruby klar gemacht werden, dass employees.name komplett als Symbol verstanden werden soll. Das geht so:
[code lang=“ruby“]joins(:employees).where(:"employees.name" => name)[/code]
Einfach, wenn gewusst wie!
Die Sortierung von Nachrichten nach Neuigkeit oder Popularität ist trivial und ohne größeren Einsatz von Gehirnschmalz zu bewerkstelligen: etwa mit dem SQL-Statement ORDER BY date DESC bzw. ORDER BY likes DESC. Möchte man jedoch die Themen ausgeben, die momentan populär sind, kommt man mit einfachen Statements nicht mehr weiter. Im Folgenden werden drei Algorithmen von Hacker News, Reddit und delicio.us vorgestellt, die auch hier auf Englisch zusammengefasst sind.
Intuitiver Hacker News-Ansatz
Der intuitive Ansatz für die Problemstellung ist, solche Themen anzuzeigen, deren Bewertung hoch ist und deren Veröffentlichungstermin noch nicht lange her ist. Sei p die Anzahl der positiven Bewertungen (jeder Benutzer kann bei Hacker News nur eine positive Bewertung pro Thema abgeben) und t die Anzahl der Stunden, seitdem das Thema veröffentlicht wurde:
Je länger also der Veröffentlichungszeitpunkt her ist, desto größer wird der Nenner. Da der Nenner mit der Potenz von 1,5 pro Stunde wächst, müssen die Votes ebenfalls potenziell mit der Zeit zunehmen, um f_h(p, t) auf gleichem Niveau zu halten.
Auch wenn keine Votes für das Thema eingehen, muss die Bewertungsfunktion f_h(p, t) jede Stunde neu berechnet werden. Dieses Problem hat der Reddit-Algorithmus nicht.
Reddit-Algorithmus
Anstatt die Bewertungen mit der Zeit exponentiell zu senken, bleibt bei Reddit die Bewertung über die Zeit gleich. Neue Themen erhalten stattdessen einen höheren Summanden, sodass ihre Bewertung höher ist. Der Reddit-Algorithmus ist etwas umfangreicher und passt nicht in eine einfache Formel. Zunächst definieren wir t_s als Differenz der Sekunden zwischen dem 08.12.2005 07:46:43 und dem Veröffentlichungsdatum des Themas:
Warum Reddit gerade den 08.12.2005 gewählt hat, ist nicht bekannt..
Da man bei Reddit sowohl positiv als auch negativ bewerten kann, wird x als Differenz der positiven (U) und negativen (D) Bewertungen definiert:
Wir definieren y als Signum- oder Vorzeichenfunktion von x. Wenn x > 0, ist y = +1. Wenn x = 0, ist y =0. Wenn x < 0, ist y = -1:
Und schließlich definieren wir z wie folgt
Ist der Betrag von x = 0, wird 1 genommen, sonst der Betrag von x.
Kommen wir zur eigentlichen Bewertungsfunktion. Mit dieser Funktion werden die Themen bewertet, sodass jene Themen mit der höchsten Bewertung die „heißen Themen“ darstellen:
Funktionsgraph des Zehnerlogarithmus (Quelle:Wikipedia)
Der erste Summand ist der „Bewertungssummand“. Wir sehen anhand des Funktionsgraph des Zehnerlogarithmus rechts, warum z so gewählt ist, dass er mindestens 1 ist. Der Summand ist somit immer 0 oder positiv sein.
Der zweite Summand ist der „Zeitsummand“. y gibt aufgrund positiver/negativer/neutraler Bewertungen an, ob der Zeitsummand addiert, subtrahiert oder gar nicht berücksichtigt werden soll.
t_s ist die Zeitdifferenz in Sekunden. Schauen wir uns ein paar Beispiele an und stellen (Oh, Wunder!) fest, dass t_s mit der Zeit immer größer wird:
t_s wird noch durch 45.000 geteilt. 45.000 Sekunden sind 12,5 Stunden. Der Zeitsummand erhöht sich also alle 12,5 Stunden um 1. Das bedeutet, dass innerhalb von 12,5 Stunden der Bewertungssummand um 1 zulegen müsste, um das höhere Alter des Beitrags zu kompensieren. Der Bewertungssummand steigt um 1, wenn die Differenz zwischen positiven und negativen Bewertungen, um die Zehnerpotenz zunimmt. Von 1 auf 10 ist ja noch easy, aber es wird immer härter: von 10 auf 100, von 100 auf 1000, von 1000 auf 10.000 etc.
Ein Problem an dem Reddit-Algorithmus ist, dass die Bewertungen aufgrund des „Zeitsummands“ immer weiter steigt und man irgendwann an Grenzen von Speichergrößen stoßen wird. Wann dieser Zeitpunkt erreicht ist, habe ich nicht errechnet.. Vielleicht liefert ja ein Leser das Datum nach? ;)
HOT & delicio.us
Generell muss man sich die Frage stellen, ob die zwei genannten Algorithmen wirklich das liefern, was gerade populär ist. Auf meinem Blog beobachte ich nämlich zwei verschiedene Besucher- und Aktivitätsverläufe. Zum Einen den Blockbuster-Verlauf, sprich: gerade einen Artikel veröffentlicht und sofort Kommentare und Besucher. Und zum Anderen den Sleeper-Verlauf: der Artikel wird veröffentlicht und erst nach Monaten oder Jahren von den Besuchern entdeckt. Auch wenn der Sleeper-Verlauf seltener auftritt, bekommen „Hacker News“- und Reddit-Besucher nichts davon mit, weil das Thema, obwohl es gerade „hot“ ist, zu alt ist, um in den „heißen Themen“ zu landen.
Bei aktuellen Nachrichten ist der Sleeper-Verlauf vernachlässigbar, aber bei Bookmarks wie bei delicio.us, kann es sehr wohl relevant sein, dass ein „alter Bookmark“ gerade wieder „hot“ ist. delicio.us hat daher einen einfachen Ansatz, um heiße Bookmarks zu erkennen:
Welche Links wurden in der vergangenen Stunde am häufigsten gebookmarkt?
Fazit
Es gibt verschiedene Ansätze „heiße Themen“ zu erhalten.
Der intuitive Ansatz von Hacker News ist mit höherem Aufwand verbunden, da sich jede Stunde die Bewertung ändert und neu berechnet werden muss. Der Sleeper-Verlauf wird zudem nicht erkannt.
Der Reddit-Algorithmus bedeutet weniger Aufwand, allerdings erkennt er den Sleeper-Verlauf nicht.
Der delicio.us Ansatz erkennt zwar Blockbuster und Sleeper-Verlauf, allerdings kann nicht garantiert werden, dass auf kleinen Plattformen mit wenig User-Aktionen ausreichend Bewertungen vorhanden sind. Wenn keine oder wenig Bewertungen in dem Zeitraum abgegeben wurden, können keine „heiße Themen“ angezeigt werden.
Vermutlich kommt jeder in gewissen Abständen an einen Punkt, an dem man absolutes Neuland betritt, da man mit seinem bisherigen Wissen nicht weiter kommt.
Das Problem
war, dass viele Datensätze (6 Mio) per PreparedStatements in eine Datenbank importiert werden müssen. An sich kann es kein Problem sein, 6 Mio Datensätze zu importieren, aber unser Server machte da nicht mit
war das Wiki kurz nach Beginn des Datenimports nicht mehr erreichbar
einige Zeit später machte der komplette Server die Grätsche
Wir mutmaßten, dass die Wiki-Datenbank auf irgendeine Weise mit dem Importvorgang in einen Konflikt gerät, da diese ja als erstes ausfiel. Dieser Verdacht war falsch, da lediglich die InnoDB der Wiki-Datenbank zum Ausfall führte. Nachdem das Wiki auf MyISAM umgestellt war, funktionierte das Wiki auch während des Imports problemlos.
Das zweite Problem blieb aber bestehen. Nach einigen Versuchen und der Installation von Diagnosetools stand fest, dass die Festplatte durch den pdflush Daemon so stark beansprucht wurde, dass es für andere Prozesse nicht mehr möglich war auf die Festplatte zuzugreifen.
Wir überlegten, die Einstellungen für den pdflush Daemon zu ändern, mir fiel aber vorher auf, dass mir bei der Programmierung des Imports ein Fehler unterlaufen war: Anstatt vor dem Import die Schlüssel zu deaktivieren
und nach dem Import die Schlüssel wieder zu aktivieren
[code lang=’sql‘]ALTER TABLE … ENABLE KEYS[/code]
waren durch einen Zeilenverrutscher die Schlüssel für eine Tabelle schon vor dem Import wieder aktiviert worden und genau das verursachte das Zusammenbrechen des Servers, weil die Indizes während des Imports neu berechnet werden mussten.