Creazione di visteMapReduce)

Panoramica

Le viste possono essere strutture di dati secondarie in IBM® Cloudant® for IBM Cloud®, che memorizzano coppie chiave/valore derivate dagli attributi del documento. Possono essere utilizzati per interrogare e aggregare le proiezioni dei documenti.

Hanno due scopi principali:

  • Indicizzazione (proiezione): Utilizzate le viste solo su mappa per proiettare i documenti in nuovi spazi chiave. Ciò consente di effettuare ricerche e ordinamenti efficienti in base a campi diversi dall'ID del documento (ad esempio, e-mail, data e ora o categoria).
  • Aggregazione e analisi: Utilizzate le viste di MapReduce per emettere e aggregare i dati tra i documenti, ad esempio contando i documenti per tipo, sommando i valori o calcolando le medie.

Come funzionano le visualizzazioni

Le viste sono definite nei documenti di progettazione e sono costituite da:

  • Una funzione mappa ( JavaScript ): Eseguita su ogni documento per determinare quali attributi costituiscono la chiave e il valore della vista. Una funzione mappa può emettere zero, una o molte righe per documento.
  • Una funzione di riduzione opzionale: Utilizzata per aggregare i valori emessi, supportando operazioni come il conteggio, la somma o la media.

Una volta costruite, le viste vengono mantenute e aggiornate automaticamente da IBM Cloudant in base alle modifiche dei documenti.

Per i database partizionati, le viste possono operare su una singola partizione quando options.partitioned è impostato su true nel documento di progettazione.

Quando utilizzare le viste

Le viste sono ideali per:

  • Ricerche efficienti e query di intervallo su attributi del documento diversi dall'ID (ad esempio, ricerca di documenti in base all'e-mail del cliente o allo stato dell'ordine).
  • Indici di copertura: Query che possono essere soddisfatte utilizzando solo i dati chiave/valore della vista, evitando di recuperare documenti completi.
  • Riepiloghi aggregati: Generare totali, medie o conteggi raggruppati per chiavi (ad esempio, vendite per anno/mese/giorno).
  • Indici parziali: Includono solo un sottoinsieme di documenti (ad esempio, un elenco dei soli ordini di e-commerce completati).

Quando non usare le viste

Evitare le visualizzazioni per:

  • Quesiti ad hoc: utilizzare invece Cloudant Search.
  • Ricerche a testo libero o con caratteri jolly: utilizzare invece Cloudant Search.

Una visione semplice

La forma più semplice di visualizzazione è una funzione mappa. La funzione map produce dati di output che rappresentano un'analisi (una mappatura) dei documenti memorizzati nel database.

Ad esempio, si potrebbe voler scoprire quale utente ha completato la registrazione online e ha un'e-mail verificata da contattare. È possibile trovare queste informazioni ispezionando ciascun documento e cercando un campo nel documento chiamato "email_verified" e ottenendo il valore di "email". Se il campo è presente e ha il valore true, significa che l'utente ha completato la registrazione e può essere contattato via e-mail. Se il campo non è presente o ha un valore diverso da true, l'utente non ha completato la registrazione.

L'uso della funzione emit in una funzione di vista consente di produrre facilmente un elenco in risposta all'esecuzione di una query utilizzando la vista. L'elenco è composto da coppie di chiavi e valori, dove la chiave aiuta a identificare il documento specifico e il valore fornisce solo i dettagli desiderati. L'elenco include anche metadati come il numero di coppie key:value restituite.

Il documento _id viene automaticamente incluso in ciascuno dei record dei risultati della coppia key:value Il documento _id è incluso per facilitare il lavoro del cliente con i risultati.

Si veda un esempio di una semplice vista utilizzando una funzione mappa:

function(user) {
  if(user.email_verified === true) {
    emit(user.email, {name: user.name, email_verified: user.email_verified, joined: user.joined});
  }
}

Vedere i dati di esempio per dimostrare l'esempio di vista semplice:

[
    {
        "_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"
    }
]

Si veda un esempio di risposta ottenuta dall'esecuzione della query di visualizzazione semplice:

{
  "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"
      }
    }
  ]
}

Esempi di funzioni mappa

La definizione di una vista all'interno di un documento di progettazione crea anche un indice basato sulle informazioni chiave. La produzione e l'uso dell'indice aumentano significativamente la velocità di accesso e di ricerca o di selezione dei documenti dalla visualizzazione.

Le sezioni seguenti descrivono l'indicizzazione con chiavi semplici e complesse e le funzioni di riduzione.

Le funzioni di indicizzazione lavorano in un ambiente a memoria limitata, dove il documento fa parte della memoria utilizzata nell'ambiente. Lo stack e il documento del codice devono rientrare nella memoria. Limitiamo i documenti a una dimensione massima di 64 MB.

Indicizzazione di un campo

La seguente funzione map controlla se l'oggetto ha un campo name e in caso affermativo emette il valore di questo campo. Con questo controllo, è possibile eseguire una query sul valore del campo name.

Vedere un esempio di indicizzazione di un campo:

function(doc) {
  if (doc.name) {
    emit("name", doc.name);
  }
}

Un indice per una relazione uno-a-molti

Se l'oggetto passato a emit ha un campo _id, una query di visualizzazione con include_docs impostato su true contiene il documento con l'ID specifico.

Vedere un esempio di indicizzazione di una relazione uno-a-molti:

function(doc) {
  if (doc.friends) {
    for (friend in doc.friends) {
      emit(doc._id, { "_id": friend });
    }
  }
}

Chiavi complesse

Le chiavi non sono limitate a semplici valori. È possibile utilizzare valori JSON arbitrari per influenzare l'ordinamento.

Quando la chiave è un array, i risultati della vista possono essere raggruppati in base a una sottosezione della chiave. Ad esempio, se le chiavi hanno la forma [year, month, day], i risultati possono essere ridotti a un singolo valore o per anno, mese, o giorno.

Per ulteriori informazioni, vedere Uso delle viste.

Utilizzo del valore

Il secondo parametro della funzione emit di una definizione MapReduce è il "valore", che viene memorizzato insieme alla chiave nell'indice risultante. Il valore ha due usi:

  1. Per le viste di sola selezione, il valore può essere usato per memorizzare un sottoinsieme del documento, per evitare di dover usare ?include_docs=true al momento della query. In questo modo si può ottenere un aumento delle prestazioni in tempo di query, a scapito di un indice più grande.
  2. Per le viste che utilizzano un riduttore, il valore conterrà generalmente una singola quantità numerica, un piccolo oggetto con chiavi fisse e valori numerici o un breve array di numeri. I dati numerici vengono sommati (con il riduttore _sum) o producono dati statistici con il riduttore _stats.

Alcuni esempi:

// 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])
}

Non inserire chiavi ad alta cardinalità nel valore di una vista, come order_id o customer_id in quanto ciò porterà a un' espansione del valore ridotto di una vista piuttosto che a una sua riduzione. Tali interrogazioni possono andare in time out o essere rifiutate dal sistema IBM Cloudant servizio. Per i riduttori numerici, i dati ad alta cardinalità sono di solito un componente della "chiave" di una vista, mentre il "valore" è riservato ai dati numerici.

Ridurre le funzioni

I documenti di progettazione con options.partitioned impostato su true non possono contenere funzioni di riduzione personalizzate JavaScript. Sono consentite solo le riduzioni integrate.

Nessun riduttore

Una definizione di vista all'interno di un documento di progettazione può non avere l'attributo reduce, a indicare che non viene eseguita alcuna aggregazione a tempo di query.

{
    "views": {
        "getVerifiedEmails": {
            "map": "function(user) { if(user.email_verified === true) { emit(user.email); } }"
        }
    }
}

La funzione di mappa precedente genera un indice secondario adatto solo alla selezione. L'indice è sempre ordinato dalla chiave (il primo parametro della funzione emit), in questo caso user.email. Questa vista è ideale per recuperare i documenti in base all'e-mail di un utente noto o a intervalli di indirizzi e-mail di utenti.

Funzioni di riduzione integrate

Per motivi di prestazioni, sono state inserite alcune semplici funzioni di riduzione. Ogni volta che è possibile, è necessario utilizzare una di queste funzioni invece di scriverne una propria.

Per utilizzare una delle funzioni integrate, inserire il nome del riduttore nel campo reduce dell'oggetto vista nel documento di progettazione.

Riduttore di conteggio

Il riduttore _count conta le righe di una vista MapReduce e, facoltativamente, raggruppa i conteggi per chiavi distinte.

{
    "views": {
        "teamCount": {
            "map": "function(doc) { if (doc.email_verified === true) { emit(doc.team, doc.name); } }",
            "reduce": "_count"
        }
    }
}

La precedente vista MapReduce crea un indice basato sulle chiavi di team a cui l'utente appartiene, ma include solo quelli con un indirizzo e-mail verificato. Poiché il riduttore è _count, la vista fornisce il numero di righe nella vista, ad esempio il numero di utenti verificati nel database.

{"rows":[
{"key":null,"value":10010}
]}

Aggiungendo ?group=true, i conteggi vengono raggruppati per chiavi distinte, in modo che il database fornisca i conteggi in base all'appartenenza alla squadra:

{"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}
]}

La disattivazione del riduttore consente di utilizzare la stessa vista per la selezione dei membri di una singola squadra ?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"}
]}

Riduttore di somma

Il riduttore _sum totalizza i valori numerici emessi da una vista MapReduce. Il valore della vista può essere un numero, un array di numeri o un oggetto contenente valori numerici. Si consideri la seguente definizione di MapReduce su un database di prodotti:

{
    "views": {
        "productPrices": {
            "map": "function(doc) {  emit(doc.type, { price: doc.price, tax: doc.tax }); }",
            "reduce": "_sum"
        }
    }
}

La vista è basata sul tipo di prodotto e il valore è un oggetto che contiene due valori: price e tax. Il riduttore _sum calcola i totali dei valori di price e tax in tutta la vista:

{"rows":[
    {"key":null,"value":{"price":144.97, "tax":7.32}}
]}

Aggiungendo ?group=true durante l'interrogazione della vista, l'output viene raggruppato e sommato in base a una chiave univoca, in questo caso il tipo di prodotto:

{"rows":[
    {"key":"kitchen","value":{"price":14.99,"tax":1.14}},
    {"key":"garden","value":{"price":129.98,"tax":6.18}}
]}

Riduttore di statistiche

Come il riduttore _sum, il riduttore _stats lavora su numeri, oggetti con valori numerici o array di numeri, restituendo conteggi, somme, valori minimi e massimi e una somma del quadrato dei valori, utile per i calcoli di varianza o deviazione standard:

{
    "views": {
        "salesByDate": {
            "map": "function(doc) { emit(doc.date, [doc.price, doc.tax]); }",
            "reduce": "_stats"
        }
    }
}

La definizione precedente calcola le statistiche sui valori numerici trovati nell'array che viene emesso come valore dell'indice. I valori vengono restituiti come array nello stesso ordine in cui sono stati forniti nella funzione map:

{"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}
    ]}
]}

Il numero approssimativo di riduttori distinti

A differenza dei riduttori numerici _sum e _stats, che agiscono sul valore dell'indice, il riduttore _approx_count_distinct utilizza la chiave della vista. Stima il numero di chiavi distinte trovate nella vista MapReduce utilizzando un algoritmo che utilizza molta meno memoria di quella che consumerebbe un algoritmo di conteggio esatto delle chiavi distinte:

{
  "views": {
    "estimateIpCount": {
      "map": "function (doc) {\n  emit(doc.ip, 1);\n}",
      "reduce": "_approx_count_distinct"
    }
  }
}

La precedente definizione di vista mira a stimare il numero di indirizzi IP distinti in un database di registri di server. Il documento ip viene emesso come chiave dell'indice, in modo che il riduttore _approx_count_distinct possa stimare il numero di chiavi distinte:

{"rows":[
{"key":null,"value":100528}
]}

I riduttori superiore/inferiore

I riduttori _top_x e _bottom_x (dove x è un numero compreso tra 1 e 100) restituiscono un array dei valori x superiori o x inferiori di un raggruppamento di viste, rispettivamente. Ad esempio, in un'applicazione di gioco, è possibile creare una vista basata sull'id utente e il cui valore è il punteggio ottenuto dall'utente. Questa vista può essere utilizzata per creare un tabellone dei punteggi migliori o peggiori:

{
    "views": {
        "bestScores": {
            "map": "function(doc) { emit(doc.user_id, doc.score); }",
            "reduce": "_top_3"
        }
    }
}

Se si interroga la vista senza alcun parametro, vengono restituiti i primi tre punteggi dell'intera vista:

{"rows":[
  {"key":null,"value":[99,98,97]}
]}

Con il raggruppamento (?group=true), vengono restituiti i primi tre punteggi di ciascun utente:

{"rows":[
  {"key":"user082","value":[99,98,97]},
  {"key":"user291","value":[85,72,42]},
  {"key":"user452","value":[55,51,30]}
]}

I primi/ultimi riduttori

I riduttori _first/_last restituiscono rispettivamente il valore della prima o dell'ultima chiave di un raggruppamento di viste. Se abbiamo un'applicazione IoT che memorizza periodicamente le letture di molti dispositivi, possiamo creare una vista basata sull'id del dispositivo e sull'ora in cui è stata effettuata la lettura. Il valore della vista è l'intero documento:

{
    "views": {
        "latestReading": {
            "map": "function(doc) { emit([doc.deviceid, doc.timestamp], doc); }",
            "reduce": "_last"
        }
    }
}

Questa vista produce chiavi e valori di questa forma, con la vista ordinata per deviceid e timestamp. Le righe che considerano le "prime" e le "ultime" letture di ciascun dispositivo sono evidenziate:

Esempio di coppie chiave-valore per la vista latestReading
chiave valore Prima lettura ( group_level=1 ) Ultima lettura ( group_level=1 )
["A00", "2025-01-01T10:00:00.000Z"] {"_id": "A00:5000","lettura": 65,"timestamp":2025-01-01T10:00:00.000Z","id_dispositivo":"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","lettura": 55,"timestamp":2025-01-01T10:00:00.000Z","id dispositivo":"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","lettura": 56,"timestamp":2025-01-01T10:02:00.000Z","id_dispositivo":"A02"} x

Interrogando la vista con group_level=1, utilizzando il riduttore _last, si otterrà la lettura più recente per ogni device_id nel database:

{"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"}}
]}

Il riepilogo dei riduttori incorporati

Funzioni di riduzione integrate
Funzione Descrizione
_count Produce il conteggio delle righe per una chiave specifica. I valori possono essere qualsiasi JSON valido.
_stats Produce una struttura JSON contenente i valori di somma, conteggio, minimo, massimo e somma quadratica. Tutti i valori devono essere numerici.
_sum Produce la somma di tutti i valori di una chiave. I valori devono essere numerici.
_approx_count_distinct Approssima il numero di chiavi distinte in un indice di vista utilizzando una variante dell'algoritmo HyperLogLog.
_top_x/_bottom_x Restituisce una matrice dei valori x superiore o x inferiore nel raggruppamento di viste come una matrice, dove x è un numero compreso tra 1 e 100.
_first/_last Restituisce i valori della chiave di ordinamento più bassa o più alta, rispettivamente, per ciascun gruppo di viste.

Funzioni di riduzione personalizzate

La maggior parte dei clienti ritiene che i riduttori integrati siano sufficienti per eseguire aggregazioni sulle coppie di viste key-value emesse dalle loro funzioni Map. Tuttavia, per casi d'uso insoliti, è possibile fornire una funzione di riduzione JavaScript invece del nome di uno dei riduttori incorporati.

Le funzioni di riduzione personalizzate sono molto più lente e difficili da mantenere rispetto ai riduttori integrati, quindi è bene verificare se un caso d'uso può essere soddisfatto con un riduttore integrato prima di scriverne uno personalizzato.

Alle funzioni di riduzione vengono passati tre argomenti nel seguente ordine:

  • keys
  • values
  • rereduce

Se una vista ha una funzione di riduzione JavaScript personalizzata, viene utilizzata per produrre risultati aggregati per quella vista. Una funzione di riduzione riceve un insieme di valori intermedi e li combina in un unico valore. Una funzione di riduzione deve accettare, come input, i risultati emessi dalla corrispondente funzione map, così come i risultati restituiti dalla funzione di riduzione stessa. Quest'ultimo caso viene definito "rereduce".

L'esempio seguente mostra una descrizione delle funzioni di riduzione.

Si veda l'esempio seguente di una funzione di riduzione personalizzata:

function (keys, values, rereduce) {
  return sum(values);
}

Le funzioni di riduzione devono gestire due casi:

  1. Quando rereduce è falso:

    • keys è un array i cui elementi sono array della forma [key, id], dove key è una chiave emessa dalla funzione map e id identifica il documento da cui è stata generata la chiave values è un array dei valori emessi per i rispettivi elementi in keys, ad esempio: reduce([ [key1,id1], [key2,id2], [key3,id3] ], [value1,value2,value3], false).
  2. Quando rereduce è vero:

    • keys è null.
    • values è un array di valori restituiti da precedenti chiamate alla funzione reduce, ad esempio: reduce(null, [intermediate1,intermediate2,intermediate3], true).

Le funzioni di riduzione devono restituire un singolo valore, adatto sia per il campo value della vista finale, sia come membro dell'array values passato alla funzione di riduzione.

Spesso, è possibile scrivere funzioni di riduzione per gestire le chiamate di rereduce senza alcun codice aggiuntivo, come la funzione di somma nell'esempio precedente. In questi casi, l'argomento rereduce può essere ignorato.

Inserendo i risultati delle reduce funzioni all'interno della reduce funzione, MapReduce può suddividere l'analisi di enormi insiemi di dati in compiti discreti, compiti paralleli, che possono essere completate molto più velocemente.

Quando si utilizza la funzione built-in reduce, se l'input non è valido, viene restituito l'errore builtin_reduce_error restituito l'errore. Nel campo reason vengono fornite informazioni più dettagliate sul guasto. I dati originali che hanno causato l'errore i dati originali che hanno causato l'errore vengono restituiti nel campo caused_by.

Vedere un esempio di risposta:

{
    "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
                    }
                ]
            }
        }
    ]
}

Restrizioni alle funzioni di map e reduce

Qui sono descritte le limitazioni delle funzioni Map e Riduce.

Trasparenza referenziale

La funzione mappa deve essere referenzialmente trasparente. La trasparenza referenziale significa che un'espressione può essere sostituita con lo stesso valore senza modificare il risultato, in questo caso, un documento e una coppia key-value. A causa della trasparenza referenziale, le visualizzazioni IBM Cloudant possono essere aggiornate in modo incrementale e reindicizzare solo il delta dall'ultimo aggiornamento.

Proprietà commutative e associative

Oltre alla trasparenza referenziale, la funzione di riduzione deve avere proprietà commutative e associative per l'input e associativa per l'input. Queste proprietà consentono alla funzione MapReducedi ridurre il proprio output e produrre la stessa risposta di ridurre il proprio output e produrre la stessa risposta, ad esempio:

f(Key, Values) == f(Key, [ f(Key, Values) ] )

Di conseguenza, IBM Cloudant può memorizzare i risultati intermedi risultati nei nodi interni degli indici Indici dell'albero B. Queste queste restrizioni rendono possibile la diffusione degli indici tra le macchine e la loro riduzione al momento dell'interrogazione al momento dell'interrogazione.

Partizione dei documenti

A causa dello sharding, IBM Cloudant non offre alcuna garanzia che l'output di due specifiche funzioni map passi alla stessa istanza di una chiamata reduce alla stessa istanza di una chiamata di riduzione. Non si deve fare affidamento su alcun ordine. La funzione la funzione reduce utilizzata deve considerare tutti i valori che le vengono passati e restituire la risposta corretta, indipendentemente dall'ordine risposta corretta, indipendentemente dall'ordine. IBM Cloudant è anche garantito che chiami la funzione reduce con * al momento della query, anche se non ne ha avuto bisogno al momento dell'interrogazione con rereduce=true al momento della query, anche se non ne ha avuto bisogno quando ha costruito l'indice costruito l'indice. È essenziale che le funzioni funzionino correttamente in questo caso (*) rereduce=true significa che il parametro keys è null e che l'array values è riempito con i risultati delle precedenti chiamate alla funzione reduce).

Dimensioni ridotte del valore

IBM Cloudant calcola gli indici delle viste e i corrispondenti valori di riduzione, quindi memorizza nella cache i valori di corrispondenti valori di riduzione, quindi memorizza questi valori all'interno di ciascuno dei puntatori ai nodi dell'albero B. Adesso, IBM Cloudant può riutilizzare valori ridotti quando aggiorna l'albero B. È necessario prestare attenzione alla quantità di dati restituiti dalle funzioni di riduzione.

È meglio che la dimensione del set di dati restituito rimanga piccola e non cresca più velocemente di * più velocemente di log(num_rows_processed). Se si ignora questa restrizione, IBM Cloudant non lancia automaticamente un errore, ma le prestazioni dell'albero B degradano drammaticamente. Se la vista funziona correttamente con piccoli insiemi di dati, ma smette di funzionare quando si aggiungono altri dati, la vista potrebbe violare il tasso di crescita ma smette di funzionare quando vengono aggiunti altri dati, la vista potrebbe violare la restrizione della caratteristica di caratteristica.

Ambiente di esecuzione

Le funzioni di indicizzazione lavorano in un ambiente a memoria limitata, dove il documento fa parte della memoria utilizzata nell'ambiente. Lo stack e il documento del codice devono rientrare nella memoria. Limitiamo i documenti a una dimensione massima di 64 MB.

Nessun riduttore JavaScript quando options.partitioned è true

I documenti di progetto con options.partitioned impostato su true non possono contenere JavaScript funzioni di riduzione, ma solo riduttori Erlang integrati come _stats.

Memorizzazione della definizione di vista

Ogni vista è una funzione JavaScript. Le viste sono memorizzate nei documenti di progetto. Quindi, per memorizzare una vista, IBM Cloudant memorizza semplicemente la definizione della funzione all'interno di un documento di progettazione. Un documento di progettazione può essere creato o aggiornato come qualsiasi altro documento.

Per memorizzare una definizione di vista, PUT il contenuto della definizione della vista in un documento _design.

Nell'esempio seguente, la vista getVerifiedEmails è definita come funzione di mappa, ed è disponibile nel campo views del documento di progettazione.

Utilizzate il metodo PUT per aggiungere una vista in un documento di progettazione:

PUT $SERVICE_URL/$DATABASE/_design/$DDOC HTTP/1.1
Content-Type: application/json

L'esempio seguente aggiunge una nuova funzione getVerifiedEmails denominata vista al documento di progettazione con definizione di vista allusers

{
    "views": {
        "getVerifiedEmails": {
            "map": "function(user) { if(user.email_verified === true){ emit(doc.email, {name: user.name, email_verified: user.email_verified, joined: user.joined}) }}  "
        }
    }
}

Vedere gli esempi di richiesta:

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))

Il precedente esempio di Go richiede il seguente blocco di importazione:

import (
  "encoding/json"
  "fmt"
  "github.com/IBM/cloudant-go-sdk/cloudantv1"
)

Tutti gli esempi Go richiedono l'iniziazione dell'oggetto service. Per ulteriori informazioni, consultare la sezione Autenticazione della documentazione API per gli esempi.