Ansichten erstellen (MapReduce)
Übersicht
Ansichten können sekundäre Datenstrukturen in IBM® Cloudant® for IBM Cloud® sein, die Schlüssel/Wertpaare speichern, die von Dokumentattributen abgeleitet sind. Sie können zur Abfrage und Aggregation von Dokumentenprojektionen verwendet werden.
Sie dienen in erster Linie zwei Zwecken:
- Indizierung (Projektion): Verwenden Sie reine Kartenansichten, um Dokumente in neue Schlüsselbereiche zu projizieren. Dies ermöglicht ein effizientes Nachschlagen und Sortieren nach anderen Feldern als der Dokument-ID (z. B. E-Mail, Zeitstempel oder Kategorie).
- Aggregation und Analyse: Verwenden Sie die Ansichten von MapReduce, um Daten über Dokumente hinweg auszugeben und zu aggregieren, z. B. um Dokumente nach Typ zu zählen, Werte zu summieren oder Durchschnittswerte zu berechnen.
Wie Ansichten funktionieren
Ansichten werden in Entwurfsdokumenten definiert und bestehen aus:
- Eine Map-Funktion ( JavaScript ): Wird für jedes Dokument ausgeführt, um festzustellen, welche Attribute den Schlüssel und den Wert der Ansicht bilden. Eine Map-Funktion kann null, eine oder viele Zeilen pro Dokument ausgeben.
- Eine optionale Reduzierfunktion: Dient zum Aggregieren der ausgegebenen Werte und unterstützt Operationen wie Zählen, Summieren oder Mittelwertbildung.
Einmal erstellte Ansichten werden automatisch von IBM Cloudant gepflegt und aktualisiert, wenn sich Dokumente ändern.
Bei partitionierten Datenbanken können Ansichten auf einer einzigen Partition arbeiten, wenn options.partitioned im Entwurfsdokument auf true eingestellt ist.
Wann werden Ansichten verwendet?
Die Ansichten sind ideal für:
- Effiziente Nachschlagewerke und Bereichsabfragen für andere Dokumentattribute als die ID (z. B. Suche nach Dokumenten nach Kunden-E-Mail oder Bestellstatus).
- Abdeckende Indizes: Abfragen, die nur mit den Schlüssel-/Wertdaten der Ansicht erfüllt werden können, ohne dass vollständige Dokumente abgerufen werden müssen.
- Aggregierte Zusammenfassungen: Generieren Sie Summen, Durchschnittswerte oder Zählungen, die nach Schlüsseln gruppiert sind (z. B. Umsatz nach Jahr/Monat/Tag).
- Partielle Indizes: Enthält nur eine Teilmenge von Dokumenten (z. B. eine Liste nur der abgeschlossenen E-Commerce-Bestellungen).
Wann man Ansichten nicht verwenden sollte
Vermeiden Sie Ansichten für:
- Ad-hoc-Abfragen- verwenden Sie stattdessen Cloudant Search.
- Freitext- oder Platzhaltersuche- verwenden Sie stattdessen Cloudant Search.
Einfache Ansicht
Die einfachste Form einer Ansicht ist eine Zuordnungsfunktion ('map'-Funktion). Die Zuordnungsfunktion generiert Ausgabedaten, die eine Analyse (Zuordnung) der Dokumente darstellen, die in der Datenbank gespeichert sind.
Sie können beispielsweise herausfinden, welcher Benutzer die Onlineregistrierung ausgeführt hat und über eine verifizierte E-Mail für den Kontakt verfügt. Sie können diese Informationen finden, indem Sie jedes Dokument überprüfen und im Dokument
nach einem Feld namens "email_verified" suchen und den Wert von "email" abrufen. Wenn das Feld vorhanden ist und den Wert „ true “ enthält, bedeutet dies, dass der Nutzer die Registrierung abgeschlossen hat
und Sie ihn per E-Mail kontaktieren können. Wenn das Feld nicht vorhanden ist oder einen anderen Wert als true hat, hat der Benutzer die Registrierung nicht abgeschlossen.
Mithilfe der Funktion emit in einer Ansichtsfunktion ist es leicht, eine Liste als Antwort auf die Ausführung einer Abfrage unter Verwendung der Ansicht zu generieren. Die Liste besteht aus Schlüssel/Wert-Paaren, wobei der Schlüssel
hilft, das jeweilige Dokument zu identifizieren, und der Wert jeweils die genaue Detailinformation liefert, die Sie wünschen. Die Liste enthält auch Metadaten wie die Anzahl der zurückgegebenen key:value-Paare.
Die Dokument-ID (_id) wird automatisch in die Ergebnisdatensätze für die Schlüssel/Wert-Paare (key:value) eingeschlossen. Die Dokument-ID (_id) wird eingeschlossen, um dem Client die Arbeit mit den Ergebnissen
zu erleichtern.
Im folgenden Beispiel für eine einfache Ansicht wird eine Zuordnungsfunktion verwendet:
function(user) {
if(user.email_verified === true) {
emit(user.email, {name: user.name, email_verified: user.email_verified, joined: user.joined});
}
}
Beispieldaten zur Veranschaulichung des Beispiels für eine einfache Ansicht:
[
{
"_id":"abc123",
"name": "Bob Smith",
"email": "bob.smith@aol.com",
"email_verified": true,
"joined": "2019-01-24T10:42:59.000Z"
},
{
"_id":"abc125",
"name": "Amelie Smith",
"email": "amelie.smith@aol.com",
"email_verified": true,
"joined": "2020-04-24T10:42:59.000Z"
}
]
Beispielantwort für die Ausführung der Abfrage mit der einfachen Ansicht:
{
"total_rows": 2,
"offset": 0,
"rows": [
{
"id": "abc125",
"key": "amelie.smith@aol.com",
"value": {
"name": "Amelie Smith",
"email_verified": true,
"joined": "2020-04-24T10:42:59.000Z"
}
},
{
"id": "abc123",
"key": "bob.smith@aol.com",
"value": {
"name": "Bob Smith",
"email_verified": true,
"joined": "2019-01-24T10:42:59.000Z"
}
}
]
}
Beispiele für Zuordnungsfunktionen
Die Definition einer Ansicht in einem Entwurfsdokument erstellt auch einen Index auf der Basis der Schlüsselinformationen. Die Erstellung und Verwendung des Index erhöht die Geschwindigkeit des Zugriffs und der Suche oder der Auswahl von Dokumenten in der Ansicht erheblich.
In den folgenden Abschnitten wird die Indexierung mit einfachen und komplexen Schlüsseln sowie die Reduzierung von Funktionen beschrieben.
Ihre Indexierungsfunktionen funktionieren in einer Umgebung mit begrenztem Speicher, in der das Dokument einen Teil des in der Umgebung verwendeten Speichers darstellt. Die Größe des Stack und des Dokuments des Codes dürfen die Speichergröße nicht überschreiten. Die maximale Größe eines Dokuments beträgt 64 MB.
Feld indexieren
Die folgende Zuordnungsfunktion prüft, ob das Objekt ein Feld name enthält, und gibt in diesem Fall den Wert dieses Felds aus. Mit dieser Prüfung können Sie den Wert des Felds name abfragen.
Beispiel für die Indexierung eines Felds:
function(doc) {
if (doc.name) {
emit("name", doc.name);
}
}
Index für eine Eins-zu-viele-Beziehung
Wenn das Objekt, das an emit übergeben wird, ein Feld _id enthält, enthält eine Ansichtsabfrage, für die include_docs auf true gesetzt ist, das Dokument mit der bestimmten ID.
Beispiel für die Indexierung einer Eins-zu-viele-Beziehung:
function(doc) {
if (doc.friends) {
for (friend in doc.friends) {
emit(doc._id, { "_id": friend });
}
}
}
Komplexe Schlüssel
Schlüssel sind nicht auf einfache Werte beschränkt. Sie können beliebige JSON-Werte zur Beeinflussung der Sortierung verwenden.
Wenn der Schlüssel ein Array ist, können die Ansichtsergebnisse nach einem Unterabschnitt des Schlüssels gruppiert werden. Wenn Schlüssel zum Beispiel die Form [year, month, day] haben, können die Ergebnisse auf einen einzigen
Wert oder nach Jahr, Monat oder Tag reduziert werden.
Weitere Informationen finden Sie unter Ansichten verwenden.
Nutzung des Wertes
Der zweite Parameter der Funktion emit einer Definition von MapReduce ist der "Wert", der zusammen mit dem Schlüssel im resultierenden Index gespeichert wird. Der Wert hat zwei Verwendungszwecke:
- Bei reinen Auswahlansichten kann der Wert verwendet werden, um eine Teilmenge des Dokuments zu speichern, damit bei der Abfrage nicht
?include_docs=trueverwendet werden muss. Dies kann zu einer Leistungssteigerung bei der Abfrage führen, allerdings auf Kosten eines größeren Indexes. - Bei Ansichten, die einen Reduzierer verwenden, enthält der Wert in der Regel eine einzelne numerische Größe, ein kleines Objekt mit festen Schlüsseln und numerischen Werten oder ein kurzes Array von Zahlen. Die numerischen Daten werden summiert
(mit dem Reduzierer
_sum) oder erzeugen statistische Daten mit dem Reduzierer_stats.
Einige Beispiele:
// create a view to allow selection of orders by year/month/day,
// where a subset of the document is projected into the view's value.
function(doc) {
if (doc.type === 'order') {
const minidoc = {
customer_id: doc.customer_id,
date: doc.date,
status: doc.status
}
emit([doc.year, doc.month, doc.day], minidoc)
}
}
// create a view, designed for the _sum reducer which contains
// one row per order, in year/month/day order where the value
// is the order's total in USD. This can be summed at query-time
// with optional grouping by year, year/month or year/month/day.
function(doc) {
emit([doc.year, doc.month, doc.day], doc.order_total_usd)
}
// create a view, designed for the _sum reducer which contains
// one row per order, in year/month/day order where the value
// contains three numeric quantities (order total, tax and shipping)
// which will be summed at query-time with optional grouping by year,
// year/month or year/month/day.
function(doc) {
const value = {
total: doc.order_total_usd,
tax: doc.tax_usd,
shipping: doc.shipping_usd
}
emit([doc.year, doc.month, doc.day], value)
}
// create a view, designed for the _sum reducer which contains
// one row per order, with customer_id as the key. The numeric
// quantities are in array (order total and tax) which will be
// summed at query-time with optional grouping by customer_id
function(doc) {
emit(doc.customer_id, [doc.order_total_usd, doc.tax_usd])
}
Geben Sie keine Schlüssel mit hoher Kardinalität in den Wert einer Ansicht ein, wie z. B. order_id oder
customer_id da dies zu einer Erweiterung des reduzierten Wertes einer Ansicht führt, anstatt zu einer Reduzierung. Solche Abfragen können sich verzögern oder von der Datenbank zurückgewiesen werden IBM Cloudant
Dienstleistung. Bei numerischen Reduzierern sind Daten mit hoher Kardinalität in der Regel eine Komponente des "Schlüssels" einer Ansicht, während der "Wert" für numerische Daten reserviert ist.
Reduktionsfunktionen
Entwurfsdokumente, in denen options.partitioned auf den Wert true gesetzt ist, können keine angepassten JavaScript-Reduktionsfunktionen enthalten. Nur integrierte Reduktionsfunktionen sind zulässig.
Ohne Reduktion
Es ist zulässig, dass eine Ansichtsdefinition in einem Entwurfsdokument kein Attribut 'reduce' enthält, um anzugeben, dass bei der Abfrage keine Aggregierung durchgeführt wird.
{
"views": {
"getVerifiedEmails": {
"map": "function(user) { if(user.email_verified === true) { emit(user.email); } }"
}
}
}
Die obige Zuordnungsfunktion ('map') generiert einen sekundären Index, der sich nur zum Auswählen eignet. Der Index wird immer nach dem Schlüssel sortiert (erster Parameter der Ausgabefunktion) -in diesem Fall user.email. Diese
Ansicht ist ideal für das Abrufen von Dokumenten über eine bekannte Benutzer-E-Mail oder Bereiche von Benutzer-E-Mail-Adressen.
Integrierte Reduktionsfunktionen
Aus Leistungsgründen steht eine Reihe einfacher Reduktionsfunktionen integriert zur Verfügung. Sie sollten nach Möglichkeit eine dieser Funktionen verwenden, anstatt eine eigene Funktion zu schreiben.
Um eine der integrierten Funktionen zu verwenden, tragen Sie den Namen des Reducers in das Feld „ reduce “ des View-Objekts in Ihrem Design-Dokument ein.
Zählreduzierer
Der _count reducer zählt die Zeilen einer MapReduce Ansicht und gruppiert die Zählungen optional nach eindeutigen Schlüsseln.
{
"views": {
"teamCount": {
"map": "function(doc) { if (doc.email_verified === true) { emit(doc.team, doc.name); } }",
"reduce": "_count"
}
}
}
Die vorherige Ansicht MapReduce erstellt einen Index, der auf die team verweist, zu der der Benutzer gehört, aber nur diejenigen mit einer verifizierten E-Mail-Adresse enthält. Da der Reduzierer _count ist, gibt
die Ansicht die Anzahl der Zeilen in der Ansicht aus, z. B. die Anzahl der verifizierten Benutzer in der Datenbank.
{"rows":[
{"key":null,"value":10010}
]}
Durch Hinzufügen von ?group=true werden die Zählungen nach eindeutigen Schlüsseln gruppiert, so dass die Datenbank die Zählungen nach Teamzugehörigkeit ausgibt:
{"rows":[
{"key":"blue","value":1409},
{"key":"green","value":1439},
{"key":"indigo","value":1425},
{"key":"orange","value":1432},
{"key":"red","value":1414},
{"key":"violet","value":1443},
{"key":"yellow","value":1448}
]}
Wenn Sie den Reduzierer ausschalten, können Sie dieselbe Ansicht für die Auswahl der Mitglieder eines einzelnen Teams verwenden ?key="orange"&reduce=false&limit=5:
{"total_rows":10010,"offset":4273,"rows":[
{"id":"783173e102613c78d02a2b3304001642","key":"orange","value":"Bethel Lusk"},
{"id":"783173e102613c78d02a2b3304001b40","key":"orange","value":"Ethyl Dionne"},
{"id":"783173e102613c78d02a2b3304009c66","key":"orange","value":"Fredda Hendrix"},
{"id":"783173e102613c78d02a2b330401800d","key":"orange","value":"Bibi Page"},
{"id":"783173e102613c78d02a2b3304018ae7","key":"orange","value":"Marylou Lavender-Milton"}
]}
Summenreduzierer
Der _sum reducer summiert die ausgegebenen numerischen Werte einer MapReduce Ansicht. Der Wert der Ansicht kann eine Zahl, ein Array von Zahlen oder ein Objekt mit numerischen Werten sein. Betrachten Sie die folgende MapReduce
Definition für eine Produktdatenbank:
{
"views": {
"productPrices": {
"map": "function(doc) { emit(doc.type, { price: doc.price, tax: doc.tax }); }",
"reduce": "_sum"
}
}
}
Die Ansicht ist auf den Produkttyp ausgerichtet, und der Wert ist ein Objekt, das zwei Werte enthält: „ price “ und „ tax “. Die Reduzierfunktion _sum berechnet die Summen für die Werte price und tax in der gesamten Ansicht:
{"rows":[
{"key":null,"value":{"price":144.97, "tax":7.32}}
]}
Durch Hinzufügen von ?group=true bei der Abfrage der Ansicht wird die Ausgabe gruppiert und nach einem eindeutigen Schlüssel summiert, in diesem Fall nach der Produktart:
{"rows":[
{"key":"kitchen","value":{"price":14.99,"tax":1.14}},
{"key":"garden","value":{"price":129.98,"tax":6.18}}
]}
Statistik-Reduzierer
Wie der _sum reducer arbeitet auch der _stats reducer mit Zahlen, Objekten mit numerischen Werten oder Zahlenreihen und gibt Zählungen, Summen, Minimal- und Maximalwerte sowie eine Summe des Quadrats der Werte zurück,
was für Berechnungen von Varianz oder Standardabweichung nützlich ist:
{
"views": {
"salesByDate": {
"map": "function(doc) { emit(doc.date, [doc.price, doc.tax]); }",
"reduce": "_stats"
}
}
}
Die vorstehende Definition berechnet Statistiken zu den numerischen Werten, die sie in dem Array findet, das als Wert des Indexes ausgegeben wird. Die Werte werden als Array in derselben Reihenfolge zurückgegeben, die in der Zuordnungsfunktion ('map') angegeben wurde:
{"rows":[
{"key":"2025-01-01","value":[
{"sum":14.99,"count":1,"min":14.99,"max":14.99,"sumsqr":224.7001},
{"sum":1.14,"count":1,"min":1.14,"max":1.14,"sumsqr":1.2995}
]},
{"key":"2025-01-02","value":[
{"sum":129.98,"count":2,"min":29.99,"max":99.99,"sumsqr":10897.4002},
{"sum":6.18,"count":2,"min":1.62,"max":4.56,"sumsqr":23.418}
]}
]}
Die ungefähre Anzahl verschiedener Abschwächer
Im Gegensatz zu den numerischen Reduzierern _sum und _stats, die auf den Wert des Indexes wirken, verwendet der Reduzierer _approx_count_distinct den Schlüssel der Ansicht. Er schätzt die Anzahl
der eindeutigen Schlüssel, die in der Ansicht MapReduce gefunden wurden, und verwendet dabei einen Algorithmus, der weit weniger Speicherplatz verbraucht als ein Algorithmus mit exakter Zählung der eindeutigen Schlüssel:
{
"views": {
"estimateIpCount": {
"map": "function (doc) {\n emit(doc.ip, 1);\n}",
"reduce": "_approx_count_distinct"
}
}
}
Die vorherige Definition der Ansicht zielt darauf ab, die Anzahl der verschiedenen IP-Adressen in einer Datenbank mit Serverprotokollen zu schätzen. Die ip des Dokuments wird als Schlüssel des Indexes ausgegeben, so
dass der _approx_count_distinct reducer die Anzahl der eindeutigen Schlüssel schätzen kann:
{"rows":[
{"key":null,"value":100528}
]}
Die Reduzierstücke oben/unten
Die Reduzierer _top_x und _bottom_x (wobei x eine Zahl zwischen 1 und 100 ist) geben ein Array der obersten x- bzw. untersten x-Werte in einer Ansichtsgruppierung zurück. In einer Spieleanwendung
kann beispielsweise eine Ansicht erstellt werden, die auf die Benutzerkennung verschlüsselt ist und deren Wert die vom Benutzer erreichte Punktzahl ist. Diese Ansicht kann verwendet werden, um eine Anzeigetafel mit den besten oder schlechtesten
Ergebnissen zu erstellen:
{
"views": {
"bestScores": {
"map": "function(doc) { emit(doc.user_id, doc.score); }",
"reduce": "_top_3"
}
}
}
Wenn wir die Ansicht ohne Parameter abfragen, werden die drei besten Ergebnisse der gesamten Ansicht zurückgegeben:
{"rows":[
{"key":null,"value":[99,98,97]}
]}
Bei der Gruppierung (?group=true) werden die drei besten Ergebnisse jedes einzelnen Nutzers zurückgegeben:
{"rows":[
{"key":"user082","value":[99,98,97]},
{"key":"user291","value":[85,72,42]},
{"key":"user452","value":[55,51,30]}
]}
Die ersten/letzten Reduktionsmittel
Die Reduzierer _first/_last geben den Wert des ersten bzw. letzten Schlüssels in einer Ansichtsgruppierung zurück. Wenn wir eine Anwendung IoT haben, die regelmäßig Messwerte von vielen Geräten
speichert, können wir eine Ansicht erstellen, die auf die Geräte-ID und die Zeit der Messwerterfassung verschlüsselt ist. Der Wert der Ansicht ist das gesamte Dokument:
{
"views": {
"latestReading": {
"map": "function(doc) { emit([doc.deviceid, doc.timestamp], doc); }",
"reduce": "_last"
}
}
}
Diese Ansicht erzeugt Schlüssel und Werte in dieser Form, wobei die Ansicht nach deviceid und timestamp sortiert ist. Die Zeilen mit den "ersten" und "letzten" Messwerten für jedes Gerät sind
hervorgehoben:
| Schlüssel | Wert | Erste Lesung ( group_level=1 ) | Letzte Lesung ( group_level=1 ) |
|---|---|---|---|
| ["A00", "2025-01-01T10:00:00.000Z"] | {"_id": "A00:5000","reading": 65,"timestamp":2025-01-01T10:00:00.000Z","deviceid":"A00"} | x | |
| ["A00", "2025-01-01T10:01:00.000Z"] | {"_id": "A00:5001","reading": 64,"timestamp":2025-01-01T10:01:00.000Z","deviceid":"A00"} | ||
| ["A00", "2025-01-01T10:02:00.000Z"] | {"_id": "A00:5002","reading": 59,"timestamp":2025-01-01T10:02:00.000Z","deviceid":"A00"} | x | |
| ["A01", "2025-01-01T10:00:00.000Z"] | {"_id": "A01:8000","reading": 12,"timestamp":2025-01-01T10:00:00.000Z","deviceid":"A01"} | x | |
| ["A01", "2025-01-01T10:01:00.000Z"] | {"_id": "A01:8001","reading": 15,"timestamp":2025-01-01T10:01:00.000Z","deviceid":"A01"} | ||
| ["A01", "2025-01-01T10:02:00.000Z"] | {"_id": "A01:8002","reading": 19,"timestamp":2025-01-01T10:02:00.000Z","deviceid":"A01"} | x | |
| ["A02", "2025-01-01T10:00:00.000Z"] | {"_id": "A02:4000","reading": 55,"timestamp":2025-01-01T10:00:00.000Z","deviceid":"A02"} | x | |
| ["A02", "2025-01-01T10:01:00.000Z"] | {"_id": "A02:4001","reading": 54,"timestamp":2025-01-01T10:01:00.000Z","deviceid":"A02"} | ||
| ["A02", "2025-01-01T10:02:00.000Z"] | {"_id": "A01:4002","reading": 56,"timestamp":2025-01-01T10:02:00.000Z","deviceid":"A02"} | x |
Die Abfrage der Ansicht mit group_level=1 unter Verwendung des Reduzierers _last liefert den neuesten Messwert für jede device_id in der Datenbank:
{"rows":[
{"key":["A00"],"value":{"_id":"93117567370d41d091b8dd160a3adf3f","_rev":"1-bc05e93e592d5a5a18e240240b581a55","deviceid":"A00","reading":13.8986,"timestamp":"2025-03-26T04:44:08.917Z","status":"red"}},
{"key":["A01"],"value":{"_id":"c9f53ac9e4a8444487ed0eaa11dc1c78","_rev":"1-1fcf121c73db03e49bac4c1981518b19","deviceid":"A01","reading":59.8453,"timestamp":"2025-04-01T01:52:34.254Z","status":"green"}},
{"key":["A02"],"value":{"_id":"577b7108a8a1458f9a17194ed1da398a","_rev":"1-71d345a773ab31f35ceb998f3c107c41","deviceid":"A02","reading":2.6208,"timestamp":"2025-03-31T00:22:17.175Z","status":"green"}},
{"key":["A03"],"value":{"_id":"150839b1d363427496a4f4e2917b8b1d","_rev":"1-860a2ed5f1f48aa642495dfb21dff3ce","deviceid":"A03","reading":55.8677,"timestamp":"2025-03-22T10:15:57.890Z","status":"red"}},
{"key":["A04"],"value":{"_id":"d3317bdc1f7b4466ae6bf7a30ca9e328","_rev":"1-a58762532f6980ca5b06a8d01d113814","deviceid":"A04","reading":44.1822,"timestamp":"2025-03-23T10:56:10.639Z","status":"green"}},
{"key":["A05"],"value":{"_id":"946754d3762f44e297ca20d13bbceb5e","_rev":"1-41fa2bb3ef8782c90b28ee44628abeab","deviceid":"A05","reading":13.2874,"timestamp":"2025-03-27T13:59:04.723Z","status":"blue"}},
{"key":["A06"],"value":{"_id":"bbcd8a5c0ae948baae713a9fcb5262d5","_rev":"1-66883211ee20a0772374672aa175dc50","deviceid":"A06","reading":7.9525,"timestamp":"2025-04-01T15:13:04.305Z","status":"blue"}},
{"key":["A07"],"value":{"_id":"a600caccdb82400698b158ecebfaa6f2","_rev":"1-68975a8d7133a13c6cc8ae34d28ea1c6","deviceid":"A07","reading":89.4818,"timestamp":"2025-03-07T02:12:59.934Z","status":"blue"}},
{"key":["A08"],"value":{"_id":"be20bae911db4da685f891fd01e07d3a","_rev":"1-d69b559627cf01e80fe85ea2f54d2f4b","deviceid":"A08","reading":97.6739,"timestamp":"2025-03-29T13:46:31.689Z","status":"green"}},
{"key":["A09"],"value":{"_id":"96526e80f2ff48e89e9e42aa47abae24","_rev":"1-85e56901f7113d7d6ff8ba573c535eb3","deviceid":"A09","reading":26.1597,"timestamp":"2025-03-18T03:22:19.848Z","status":"blue"}}
]}
Zusammenfassung der eingebauten Reduktionsmittel
| Funktion | Beschreibung |
|---|---|
_count |
Liefert die Zeilenanzahl für einen bestimmten Schlüssel. Die Werte können beliebige JSON-Daten sein. |
_stats |
Erzeugt eine JSON-Struktur, die die Summe, die Anzahl, den Minimalwert, den Maximalwert und die Quadratsummenwerte enthält. Alle Werte müssen numerisch sein. |
_sum |
Erzeugt die Summer aller Werte für einen Schlüssel. Die Werte müssen numerisch sein. |
_approx_count_distinct |
Schätzt die Anzahl der unterschiedlichen Schlüssel in einem View-Index mithilfe einer Variante des HyperLogLog Algorithmus. |
_top_x/_bottom_x |
Gibt ein Array der oberen x- oder unteren x-Werte in der Ansichtsgruppierung als Array zurück, wobei x eine Zahl zwischen 1 und 100 ist. |
_first/_last |
Gibt die Werte des niedrigsten bzw. höchsten Sortierschlüssels für jede Ansichtsgruppe zurück. |
Angepasste Reduktionsfunktionen
Die meisten Kunden stellen fest, dass die integrierten Reduktionsfunktionen ausreichen, um Aggregationen für Schlüssel/Wert-Paare (key-value) von Ansichten durchzuführen, die von Zuordnungsfunktionen ausgegeben werden. In außergewöhnlichen
Anwendungsfällen kann jedoch eine JavaScript-Reduktionsfunktion anstelle des Namens einer der integrierten Reduktionsfunktionen angegeben werden.
Benutzerdefinierte Reduktionsfunktionen sind viel langsamer und schwieriger zu warten als integrierte Reduktionsfunktionen. Prüfen Sie daher, ob ein Anwendungsfall mit einer integrierten Reduktionsfunktion erfüllt werden kann, bevor Sie eine benutzerdefinierte Funktion schreiben.
An Reduktionsfunktionen werden drei Argumente in der folgenden Reihenfolge übergeben:
keysvaluesrereduce
Wenn eine Ansicht eine angepasste JavaScript-Reduktionsfunktion enthält, wird diese zum Erstellen von Aggregatergebnissen für die Ansicht verwendet. An eine Reduktionsfunktion wird ein Satz von Zwischenwerten übergeben, die von der Funktion zu einem Einzelwert kombiniert werden. Eine Reduktionsfunktion muss als Eingabe Ergebnisse akzeptieren, die von der entsprechenden Zuordnungsfunktion ausgegeben werden, und Ergebnisse akzeptieren, die von der Reduktionsfunktion selbst zurückgegeben werden. Der letztere Fall wird als Rereduktion ("rereduce") bezeichnet.
Eine Beschreibung der Reduktionsfunktionen wird im folgenden Beispiel gezeigt.
Sehen Sie sich das folgende Beispiel einer angepassten Reduzierungsfunktion an:
function (keys, values, rereduce) {
return sum(values);
}
Reduktionsfunktionen müssen zwei Fälle behandeln:
-
Wenn
rereduceden Wert 'false' hat:keysist ein Array, dessen Elemente Arrays im Format[key, id]sind. Dabei istkeyein Schlüssel, der von der Zuordnungsfunktion ausgegeben wird, undidgibt das Dokument an, aus dem der Schlüssel generiert wurde.valuesist ein Array der Werte, die für die entsprechenden Elemente inkeysausgegeben werden, z. B.reduce([ [key1,id1], [key2,id2], [key3,id3] ], [value1,value2,value3], false).
-
Wenn
rereduceden Wert 'true' hat:keysistnull.- Bei
valueshandelt es sich um ein Array von Werten, die von vorherigen Aufrufen der Reduktionsfunktion zurückgegeben wurden, wie zum Beispiel:reduce(null, [intermediate1,intermediate2,intermediate3], true).
Reduktionsfunktionen müssen einen einzelnen Wert zurückgeben, der sich sowohl für das Feld value der endgültigen Ansicht als auch als Element des Arrays values, das an die Reduktionsfunktion übergeben wird, eignet.
Reduktionsfunktionen können oft so geschrieben werden, dass sie Reduktionsaufrufe ohne zusätzlichen Code bewältigen können, wie die Summenfunktion im vorherigen Beispiel. In solchen Fällen kann das Argument rereduce ignoriert werden.
Durch die Wiederübergabe der Ergebnisse der Funktion reduce an die Funktion reduce kann MapReduce die Analyse sehr großer Datasets in diskrete, parallele Tasks aufteilen, die wesentlich schneller ausgeführt werden können.
Wenn Sie die integrierte Reduktionsfunktion verwenden und die Eingabe ungültig ist, wird der Fehler builtin_reduce_error zurückgegeben. Ausführlichere Informationen zu dem Fehler werden im Feld reason zur Verfügung
gestellt. Die ursprünglichen Daten, die den Fehler verursacht haben, werden im Feld caused_by zurückgegeben.
Beispiel für eine Antwort:
{
"rows": [
{
"key": null,
"value": {
"error": "builtin_reduce_error",
"reason": "The _sum function requires that map values be numbers, arrays of numbers, or objects. Objects can't be mixed with other data structures. Objects can be arbitrarily nested, if the values for all fields are themselves numbers, arrays of numbers, or objects.",
"caused_by": [
{
"a": 1
},
{
"a": 2
},
{
"a": 3
},
{
"a": 4
}
]
}
}
]
}
Einschränkungen für Map- und Reduce-Funktionen
Im Folgenden werden Einschränkungen für Map- und Reduce-Funktionen beschrieben.
Referenzielle Transparenz
Die Zuordnungsfunktion ('map'-Funktion) muss referenziell transparent sein. Unter referenzieller Transparenz ist zu verstehen, dass ein Ausdruck durch den entsprechenden Wert ersetzt werden kann, ohne dass sich das Ergebnis ändert, in diesem
Fall zum Beispiel ein Dokument und ein Schlüssel/Wert-Paar (key-value). Aufgrund der referenziellen Transparenz können IBM Cloudant-Ansichten inkrementell aktualisiert und die erneute Indexierung nur für die letzte Aktualisierung
durchgeführt werden.
Kommutative und assoziative Eigenschaften
Neben der referenziellen Transparenz muss die Reduktionsfunktion auch kommutative und assoziative Eigenschaften für die Eingabe haben. Durch diese Eigenschaften kann die MapReduce-Funktion die eigene Ausgabe reduzieren und die gleiche Antwort erzeugen. Beispiel:
f(Key, Values) == f(Key, [ f(Key, Values) ] )
Im Ergebnis kann IBM Cloudant Zwischenergebnisse in den inneren Knoten der B-Tree-Indizes speichern. Diese Einschränkungen machen es außerdem möglich, dass Indizes auf mehrere Maschinen verteilt und zur Abfragezeit reduziert werden können.
Dokumentpartitionierung
Aufgrund des Shardings gibt es keine Garantie seitens IBM Cloudant, dass die Ausgaben von jeweils zwei bestimmten Zuordnungsfunktionen an dieselbe Instanz eines Reduktionsaufrufs übergeben werden. Sie dürfen sich nicht auf eine Reihenfolge
verlassen. Die von Ihnen verwendete Reduktionsfunktion muss alle Werte, die an sie übergeben werden, berücksichtigen und die richtige Antwort unabhängig von der Reihenfolge zurückgeben. Außerdem ruft IBM Cloudant Ihre Reduktionsfunktion
garantiert mit rereduce=true bei der Abfrage auf, selbst wenn dies bei der Erstellung des Index nicht erforderlich war. Es ist entscheidend, dass Ihre Funktionen in diesem Fall korrekt funktionieren. (Die Angabe rereduce=true bedeutet, dass der Parameter 'keys' null null ist und dass das Werte-Array mit den Ergebnissen aus vorherigen Aufrufen der Reduktionsfunktion gefüllt wird).
Größe reduzierter Werte
IBM Cloudant berechnet Ansichtsindizes und die entsprechenden Reduktionswerte und speichert diese Werte in den einzelnen Knotenzeigern der B-Tree-Struktur. Nachfolgend kann IBM Cloudant die reduzierten Werte bei Aktualisierungen der B-Tree-Struktur wiederverwenden. Sie müssen auf die Menge der Daten achten, die aus Reduktionsfunktionen zurückgegeben werden.
Es ist am besten, wenn die Größe Ihres zurückgegebenen Datasets klein bleibt und nicht schneller als log(num_rows_processed) anwächst. Wenn Sie diese Einschränkung ignorieren, löst IBM Cloudant nicht automatisch einen Fehler aus.
Stattdessen verschlechtert sich Leistung der B-Tree-Struktur erheblich. Wenn Ihre Ansicht mit kleinen Datasets korrekt funktioniert, jedoch nach Hinzufügen weiterer Daten nicht mehr funktioniert, verstößt Ihre Ansicht möglicherweise gegen
die Einschränkung für das Merkmal der Wachstumsrate.
Ausführungsumgebung
Ihre Indexierungsfunktionen funktionieren in einer Umgebung mit begrenztem Speicher, in der das Dokument einen Teil des in der Umgebung verwendeten Speichers darstellt. Die Größe des Stack und des Dokuments des Codes dürfen die Speichergröße nicht überschreiten. Die maximale Größe eines Dokuments beträgt 64 MB.
Keine JavaScript-Reduktionsfunktionen, wenn options.partitioned = true
Entwurfsdokumente, in denen options.partitioned auf den Wert true gesetzt ist, können keine JavaScript-Reduktionsfunktionen, sondern nur integrierte Erlang-Reduktionsfunktionen wie _stats enthalten.
Ansichtendefinition speichern
Jede Ansicht ist eine JavaScript-Funktion. Ansichten werden in Entwurfsdokumenten gespeichert. Wenn Sie also eine Ansicht speichern wollen, speichert IBM Cloudant sie einfach in der Funktionsdefinition in einem Entwurfsdokument. Ein Entwurfsdokument kann wie jedes andere Dokument erstellt oder aktualisiert werden.
Um eine Ansichtsdefinition zu speichern,
PUT den Inhalt der Ansichtsdefinition in ein _design Dokument.
Im folgenden Beispiel ist die Ansicht getVerifiedEmails als Mapfunktion definiert und im Feld views des Entwurfsdokuments verfügbar.
Verwenden Sie die Methode PUT, um eine Ansicht zu einem Entwurfsdokument hinzuzufügen:
PUT $SERVICE_URL/$DATABASE/_design/$DDOC HTTP/1.1
Content-Type: application/json
Das folgende Beispiel fügt eine neue getVerifiedEmails benannte View-Funktion zum allusers Entwurfsdokument mit View-Definition hinzu:
{
"views": {
"getVerifiedEmails": {
"map": "function(user) { if(user.email_verified === true){ emit(doc.email, {name: user.name, email_verified: user.email_verified, joined: user.joined}) }} "
}
}
}
Sehen Sie sich die Anforderungsbeispiele an:
curl -X PUT "$SERVICE_URL/users/_design/allusers" --data '{
"views": {
"getVerifiedEmails": {
"map": "function(user) { if(user.email_verified === true){ emit(doc.email, {name: user.name, email_verified: user.email_verified, joined: user.joined}) }}"
}
}
}'
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.DesignDocument;
import com.ibm.cloud.cloudant.v1.model.DesignDocumentViewsMapReduce;
import com.ibm.cloud.cloudant.v1.model.DocumentResult;
import com.ibm.cloud.cloudant.v1.model.PutDesignDocumentOptions;
import java.util.Collections;
Cloudant service = Cloudant.newInstance();
DesignDocumentViewsMapReduce emailViewMapReduce =
new DesignDocumentViewsMapReduce.Builder()
.map("function(user) { if(user.email_verified === true){ emit(doc.email,{name: user.name, email_verified: user.email_verified, joined: user.joined}) }")
.build();
DesignDocument designDocument = new DesignDocument();
designDocument.setViews(
Collections.singletonMap("getVerifiedEmails", emailViewMapReduce));
PutDesignDocumentOptions designDocumentOptions =
new PutDesignDocumentOptions.Builder()
.db("users")
.designDocument(designDocument)
.ddoc("allusers")
.build();
DocumentResult response =
service.putDesignDocument(designDocumentOptions).execute()
.getResult();
System.out.println(response);
import { CloudantV1 } from '@ibm-cloud/cloudant';
const service = CloudantV1.newInstance({});
const emailViewMapReduce: CloudantV1.DesignDocumentViewsMapReduce = {
map: 'function(user) { if(user.email_verified === true){ emit(doc.email, {name: user.name, email_verified: user.email_verified, joined: user.joined}) }}'
}
const designDocument: CloudantV1.DesignDocument = {
views: {'getVerifiedEmails': emailViewMapReduce}
}
service.putDesignDocument({
db: 'users',
designDocument: designDocument,
ddoc: 'allusers'
}).then(response => {
console.log(response.result);
});
from ibmcloudant.cloudant_v1 import CloudantV1
service = CloudantV1.new_instance()
email_view_map_reduce = DesignDocumentViewsMapReduce(
map='function(user) { if(user.email_verified === true){ emit(doc.email, {name: user.name, email_verified: user.email_verified, joined: user.joined}) }}'
)
design_document = DesignDocument(
views={'getVerifiedEmails': email_view_map_reduce}
)
response = service.put_design_document(
db='users',
design_document=design_document,
ddoc='allusers'
).get_result()
print(response)
emailViewMapReduce, err := service.NewDesignDocumentViewsMapReduce(
"function(user) { if(user.email_verified === true){ emit(doc.email, {name: user.name, email_verified: user.email_verified, joined: user.joined}) }}",
)
if err != nil {
panic(err)
}
designDocument := &cloudantv1.DesignDocument{
Views: map[string]cloudantv1.DesignDocumentViewsMapReduce{
"getVerifiedEmails": *emailViewMapReduce,
},
}
putDesignDocumentOptions := service.NewPutDesignDocumentOptions(
"users",
"allusers",
designDocument,
)
documentResult, _, err := service.PutDesignDocument(putDesignDocumentOptions)
if err != nil {
panic(err)
}
b, _ := json.MarshalIndent(documentResult, "", " ")
fmt.Println(string(b))
Das vorherige Go-Beispiel erfordert den folgenden Importblock:
import (
"encoding/json"
"fmt"
"github.com/IBM/cloudant-go-sdk/cloudantv1"
)
Für alle Go-Beispiele muss das Objekt service initialisiert sein. Weitere Informationen finden Sie in den Beispielen im Abschnitt 'Authentifizierung' in der API-Dokumentation.