Min/Max auf mehrwertigem Feld für Funktionen & Sortierung
Eine der neuen Funktionen, die in Solr 5.3 hinzugefügt wurden, ist die Möglichkeit, festzulegen, dass Solr entweder den Minimal- oder den Maximalwert eines mehrwertigen numerischen Feldes verwenden soll – entweder zur direkten Verwendung (z.B. als Sortierung) oder zur Einbindung in eine größere, komplexere Funktion.
Ein Beispiel: Nehmen wir an, Sie sammeln regelmäßig Temperaturmesswerte von verschiedenen Geräten und indizieren diese in Solr. Einige Geräte haben vielleicht nur einen einzigen Temperatursensor und liefern einen einzigen Messwert. Andere Geräte haben vielleicht 2, 3 oder 50 verschiedene Sensoren, von denen jeder einen anderen Temperaturwert liefert. Wenn alle Geräte identisch wären – oder zumindest identische Sensoren hätten – könnten Sie für jeden Sensor ein eigenes Feld anlegen. Wenn die Geräte und ihre Sensoren jedoch nicht homogen sind und Sie sich im Allgemeinen nur für die zurückgegebene „maximale“ Temperatur interessieren, könnten Sie es einfacher finden, alle Sensormesswerte zu einem bestimmten Zeitpunkt in ein einziges mehrwertiges Feld „all_sensor_temps“ zu übertragen und den „maximalen“ Wert in ein eigenes Feld „max_sensor_temp“ zu indizieren. (Sie könnten sogar die MaxFieldValueUpdateProcessorFactory
verwenden, damit Solr dies automatisch für Sie erledigt, wenn Sie ein Dokument hinzufügen).
Mit dieser Art von Einrichtung können Sie verschiedene Arten von Solr-Optionen verwenden, um viele interessante Fragen zu beantworten, wie z.B.:
- Für einen bestimmten Zeitraum können Sie mit
sort=max_sensor_temp desc
auf einen Blick sehen, bei welchen Geräten die Sensoren während dieses Zeitraums die heißesten Temperaturen gemeldet haben. - Verwenden Sie
fq={!frange l=100}max_sensor_temp
, um Ihre Ergebnisse auf Situationen zu beschränken, in denen mindestens ein Sensor eines Geräts „Überhitzung“ meldet.
…usw. Was aber, wenn Sie eines Tages entscheiden, dass Sie wirklich wissen möchten, welche Geräte Sensoren haben, die die niedrigste Temperatur melden? Oder Messwerte, die jemals unter einem Schwellenwert lagen, der so niedrig ist, dass es sich um einen Gerätefehler handeln muss?
Da Sie kein min_sensor_temp
Feld in Ihrem Index erstellt haben, bevor Sie all diese Dokumente hinzugefügt haben, gab es in der Vergangenheit keine einfache Möglichkeit, diese Art von Fragen zu beantworten, ohne entweder den Index komplett neu zu erstellen oder einen Cursor zu verwenden, um alle übereinstimmenden Dokumente abzurufen und den „Mindestwert“ des all_sensor_temps
Feldes selbst zu bestimmen.
In Solr 5.3 ist dies nun viel einfacher, da DocValues unterstützt wird. Mit der Funktion field(...)
können Sie jetzt den Namen eines numerischen Feldes mit mehreren Werten (konfiguriert mit docValues="true"
) zusammen mit entweder min
oder max
angeben, um anzugeben, welcher Wert ausgewählt werden soll.
Zum Beispiel:
- Sortieren:
sort=field(all_sensor_temps,min) asc
- Filtern:
fq={!frange u=0}field(all_sensor_temps,min)
- Als Pseudo-Feld:
fl=device_id,min_temp:field(all_sensor_temps,min),max_temp:field(all_sensor_temps,max)
- In komplexen Funktionen: facet.query={!frange key=num_docs_with_large_sensor_range u=50}sub(field(all_sensor_temps,max),field(all_sensor_temps,min))
Leistung?
Eine der ersten Fragen, die ich mir stellte, als ich begann, Solr mit Code auszustatten, um die zugrunde liegende DocValues-Funktionalität zu nutzen, die dies alles möglich macht, war: „Wie langsam wird die Suche nach dem Minimum und Maximum jedes Dokuments sein? „Wie langsam wird es sein, das Minimum/Maximum jedes Dokuments zur Abfragezeit zu finden?“ Die Antwort lautet überraschenderweise: „Überhaupt nicht sehr langsam.“
Der Grund, warum die Suche nach diesen Min-/Max-Werten zur Abfragezeit keine größeren Leistungseinbußen mit sich bringt, ist die Tatsache, dass die DocValues-Implementierung die Mehrfachwerte zur Indexzeit sortiert, wenn sie auf die Festplatte geschrieben werden. Zur Abfragezeit sind sie über SortedSetDocValues
. Die Suche nach dem „Minimum“ ist so einfach wie der Zugriff auf den ersten Wert in der Menge, während die Suche nach dem „Maximum“ nur geringfügig schwieriger ist: ein Methodenaufruf, um nach der „Anzahl“ der Werte in der (sortierten) Menge zu fragen und dann nach dem „letzten“ (d.h.: count-1
).
Die Theorie war stichhaltig, aber ich wollte ein schnelles Benchmarking durchführen, um es mir selbst zu beweisen. Also habe ich ein paar Skripte erstellt, um einige Testdaten zu generieren und eine Reihe von Abfragen mit verschiedenen Sortieroptionen auszuführen und die durchschnittliche Antwortzeit der verschiedenen gleichwertigen Sortierungen zu vergleichen. Die Grundidee hinter diesen Skripten ist folgende:
- Generieren Sie 10 Millionen Zufallsdokumente, die eine zufällige Anzahl von numerischen „langen“ Werten in einem
multi_l
Feld enthalten.- Jeder Arzt hatte eine 10%ige Chance, überhaupt keine Werte zu haben.
- Der Rest der Dokumente hat mindestens 1 und höchstens 13 zufällige Werte
- Indizieren Sie diese Dokumente mit einer solrconfig.xml, die die CloneFieldUpdateProcessorFactory mit der MinFieldValueUpdateProcessorFactory und der MaxFieldValueUpdateProcessorFactory kombiniert, um sicherzustellen, dass auch die einwertigen Felder
min_l
undmax_l
entsprechend mit den richtigen Werten aufgefüllt werden. - Erzeugen Sie 500 zufällige Bereichsabfragen gegen das Feld uniqueKey, so dass es genau eine Abfrage gibt, die zu jedem Vielfachen von 200 Dokumenten bis zu 100.000 Dokumenten passt, und so, dass die Reihenfolge der Abfragen fest, aber zufällig ist
- Für jede Sortieroption von Interesse:
- Solr starten
- Schleife über alle Abfragen, die diese bestimmte Sortieroption verwenden, und führen Sie diese aus
- Wiederholen Sie die Schleife über alle Abfragen insgesamt 10 Mal, um das Rauschen zu eliminieren und eine mittlere/stddev Antwortzeit zu ermitteln
- Solr herunterfahren
- Stellen Sie die Antwortzeit für jeden Satz vergleichbarer Sortieroptionen im Verhältnis zur Anzahl der übereinstimmenden Dokumente dar, die in dieser Anfrage sortiert wurden
Bevor ich mir die Ergebnisse ansehe, möchte ich Sie an ein paar wichtige Vorbehalte erinnern:
- Ich habe diese Tests auf meinem Laptop durchgeführt, während andere Anwendungen liefen und die CPU beanspruchten
- Während des Tests gab es nur einen einzigen Client, der Solr mit Abfragen versorgte
- Die Daten in diesen Tests waren zufällig und sehr synthetisch. Sie stellen keine typische Verteilung dar.
- Die Abfragen selbst sind sehr synthetisch und darauf ausgelegt, die Gesamtzeit zu minimieren, die Solr für die Bearbeitung einer Anfrage benötigt, um die verschiedenen Ergebnisse zu sortieren.
Trotz dieser Vorbehalte sollten die Tests – und die daraus resultierenden Diagramme – dennoch nützlich sein, um die Leistung der Sortierung nach einem einwertigen Feld mit der Sortierung nach der neuen Funktion field(multivaluedfield,minmax)
zu vergleichen. Beginnen wir zum Beispiel mit einem Vergleich der relativen Zeit, die für die Sortierung von Dokumenten mit sort=max_l asc
gegenüber field(multi_l,max) asc
benötigt wird…
Wir können sehen, dass beide Sortieroptionen ziemlich konsistente und ziemlich flache Graphen der durchschnittlichen Anfragezeit im Verhältnis zur Anzahl der zu sortierenden Dokumente aufweisen. Selbst wenn wir nur den linken Rand des Diagramms betrachten (Anfragen, die mit höchstens 10.000 Dokumenten übereinstimmen), sehen wir, dass die Diagramme zwar nicht so flach sind, aber immer noch ziemlich konsistent in Bezug auf die relative Antwortzeit…
Diese Konsistenz ist (ähem) in allen getesteten Vergleichen konsistent – Sie können die Links in der Tabelle unten verwenden, um sich die Diagramme anzuschauen, die Sie interessieren.
Einzelsortierung | Mehrfachsortierung | Richtung | ||
---|---|---|---|---|
max_l |
field(multi_l,max) |
asc |
Ergebnisse | gezoomt |
max_l |
field(multi_l,max) |
desc |
Ergebnisse | gezoomt |
min_l |
field(multi_l,min) |
asc |
Ergebnisse | gezoomt |
min_l |
field(multi_l,min) |
desc |
Ergebnisse | gezoomt |
sum(min_l,max_l) |
sum(def(field(multi_l,min),0),def(field(multi_l,max),0)) |
asc |
Ergebnisse | gezoomt |
sum(min_l,max_l) |
sum(def(field(multi_l,min),0),def(field(multi_l,max),0)) |
desc |
Ergebnisse | gezoomt |