Création de vues (MapReduce)
Présentation
Les vues peuvent être des structures de données secondaires dans IBM® Cloudant® for IBM Cloud®, stockant des paires clé/valeur dérivées des attributs du document. Ils peuvent être utilisés pour interroger et agréger des projections de documents.
Ils servent deux objectifs principaux :
- Indexation (projection): Utiliser des vues uniquement cartographiques pour projeter les documents dans de nouveaux espaces clés. Cela permet d'effectuer des recherches et des tris efficaces sur la base de champs autres que l'identifiant du document (par exemple, le courriel, l'horodatage ou la catégorie).
- Agrégation et analyse: Utilisez les vues MapReduce pour émettre et agréger des données sur les documents, par exemple en comptant les documents par type, en additionnant des valeurs ou en calculant des moyennes.
Fonctionnement des vues
Les vues sont définies dans les documents de conception et consistent en
- Une fonction de carte ( JavaScript ): Exécutée sur chaque document pour déterminer les attributs qui forment la clé et la valeur de la vue. Une fonction map peut émettre zéro, une ou plusieurs lignes par document.
- Une fonction de réduction facultative : Elle permet d'agréger les valeurs émises et d'effectuer des opérations telles que le comptage, la sommation ou le calcul de la moyenne.
Une fois construites, les vues sont automatiquement maintenues et mises à jour par IBM Cloudant au fur et à mesure que les documents changent.
Pour les bases de données partitionnées, les vues peuvent fonctionner sur une seule partition lorsque options.partitioned est défini sur true dans le document de conception.
Quand utiliser les vues
Les vues sont idéales pour :
- Des recherches et des requêtes efficaces sur des attributs de documents autres que l'identifiant (par exemple, recherche de documents par courriel du client ou par statut de la commande).
- Index de couverture: Les requêtes qui peuvent être satisfaites en utilisant uniquement les données clé/valeur de la vue, évitant ainsi la nécessité d'extraire des documents complets.
- Sommaires agrégés: Générer des totaux, des moyennes ou des comptages groupés par clés (par exemple, les ventes par année/mois/jour).
- Index partiels: Ils ne comprennent qu'un sous-ensemble de documents (par exemple, une liste des seules commandes de commerce électronique achevées).
Quand ne pas utiliser les vues
Éviter les vues pour :
- Requêtes ad hoc- utilisez plutôt Cloudant Search.
- Recherches en texte libre ou avec caractères génériques: utilisez plutôt Cloudant Search.
Une vue simple
La forme la plus simple d'une vue est une fonction de mappe. La fonction de mappe génère des données de sortie représentant une analyse (un mappage) des documents qui sont stockés dans la base de données.
Par exemple, vous pouvez rechercher l'utilisateur ayant effectué l'enregistrement en ligne et avoir un courrier électronique vérifié à contacter. Vous pouvez trouver ces informations en inspectant chaque document et en recherchant une zone dans
le document appelé " email_vérifié " et en obtenant la valeur " email ". Si ce champ est présent et contient la valeur « true », cela signifie que l'utilisateur a terminé son inscription et que vous pouvez
le contacter par e-mail. Si la zone n'est pas présente ou a une valeur autre que true, l'utilisateur n'a pas effectué l'enregistrement.
L'utilisation de la fonction emit dans une fonction de vue facilite la génération d'une liste en réponse à l'exécution d'une requête à l'aide de la vue. La liste se compose de paires de clés et de valeurs, où la clé permet d'identifier
le document spécifique et la valeur fournit les détails précis dont vous avez besoin. La liste inclut également des métadonnées telles que le nombre de paires key:value renvoyées.
Le document _id est inclus automatiquement dans chaque enregistrement de résultat de paire key:value. Le document _id est inclus pour faciliter le travail du client avec les résultats obtenus.
Voici un exemple de vue simple avec une fonction de mappe :
function(user) {
if(user.email_verified === true) {
emit(user.email, {name: user.name, email_verified: user.email_verified, joined: user.joined});
}
}
Voici un exemple de données illustrant une vue simple :
[
{
"_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"
}
]
Voici un exemple de réponse à l'exécution d'une requête de vue simple :
{
"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"
}
}
]
}
Exemples de fonction de mappe
La définition d'une vue dans un document de conception crée également un index basé sur les informations clés. La production et l'utilisation de l'index augmentent considérablement la vitesse d'accès et de recherche ou de sélection de documents dans la vue.
Les sections suivantes décrivent l'indexation avec des clés simples et complexes et réduisent les fonctions.
Vos fonctions d'indexation fonctionnent dans un environnement à contraintes de mémoire où le document fait partie de la mémoire utilisée dans l'environnement. La pile et le document de votre code doivent s'insérer dans cette mémoire. Nous limitons les documents à une taille maximale de 64 Mo.
Indexation d'une zone
La fonction de mappe suivante vérifie si l'objet possède une zone name et, si tel est le cas, émet la valeur de cette zone. Avec cette vérification, vous pouvez interroger la valeur de la zone name.
Voici un exemple d'indexation de zone :
function(doc) {
if (doc.name) {
emit("name", doc.name);
}
}
Index pour une relation un à plusieurs
Si l'objet transmis à emit possède une zone _id, une requête de vue dans laquelle include_docs a pour valeur true contient le document associé à l'ID spécifique.
Voici un exemple d'indexation d'une relation un à plusieurs :
function(doc) {
if (doc.friends) {
for (friend in doc.friends) {
emit(doc._id, { "_id": friend });
}
}
}
Clés complexes
Les clés ne se limitent pas à des valeurs simples. Vous pouvez utiliser des valeurs JSON arbitraires pour influencer le tri.
Lorsque la clé est un tableau, les résultats de vue peuvent être regroupés par sous-section de clé. Par exemple, si des clés sont au format [year, month, day], les résultats peuvent être réduits à une valeur unique ou par année,
mois ou jour.
Pour plus d'informations, voir Utilisation des vues.
Utilisation de la valeur
Le deuxième paramètre de la fonction emit d'une définition de MapReduce est la "valeur", qui est stockée avec la clé dans l'index résultant. La valeur a deux usages :
- Pour les vues de sélection uniquement, la valeur peut être utilisée pour stocker un sous-ensemble du document afin d'éviter d'avoir à utiliser
?include_docs=trueau moment de la requête. Cela peut permettre d'améliorer les performances au moment de l'interrogation, au détriment d'un index plus volumineux. - Pour les vues qui utilisent un réducteur, la valeur contiendra généralement une seule quantité numérique, un petit objet avec des clés fixes et des valeurs numériques, ou un court tableau de nombres. Les données numériques sont additionnées
(avec le réducteur
_sum) ou produisent des données statistiques avec le réducteur_stats.
Quelques exemples :
// 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])
}
Ne mettez pas de clés à forte cardinalité dans la valeur d'une vue, comme order_id ou
customer_id car cela entraînera une expansion de la valeur réduite d'une vue plutôt qu'une réduction. De telles requêtes peuvent dépasser le temps imparti ou être rejetées par le IBM Cloudant service. Pour les
réducteurs numériques, les données à cardinalité élevée sont généralement un composant de la "clé" d'une vue, la "valeur" étant réservée aux données numériques.
Fonctions de réduction
Les documents de conception dans lesquels options.partitioned a pour valeur true ne peuvent pas contenir de fonctions de réduction JavaScript personnalisées. Seules les réductions intégrées sont autorisées.
Pas de réducteur
Une définition de vue dans un document de conception peut ne pas avoir d'attribut de réduction ; dans ce cas, aucune agrégation de temps de requête n'est effectuée.
{
"views": {
"getVerifiedEmails": {
"map": "function(user) { if(user.email_verified === true) { emit(user.email); } }"
}
}
}
La fonction de mappe précédente génère un index secondaire qui convient uniquement à la sélection. L'index est toujours commandé par la clé (le premier paramètre de la fonction emit) - dans ce cas user.email. Cette vue est idéale
pour l'extraction de documents par courrier électronique utilisateur connu ou par plages d'adresses électroniques des utilisateurs.
Fonctions de réduction intégrées
Pour des questions de performances, quelques fonctions de réduction simples sont intégrées. Dans la mesure du possible, vous devez utiliser l'une de ces fonctions au lieu d'écrire la vôtre.
Pour utiliser l'une des fonctions intégrées, indiquez le nom du réducteur dans le champ « reduce » de l'objet « view » de votre document de conception.
Réducteur de comptage
Le réducteur _count compte les lignes d'une vue MapReduce et regroupe éventuellement les comptes par clés distinctes.
{
"views": {
"teamCount": {
"map": "function(doc) { if (doc.email_verified === true) { emit(doc.team, doc.name); } }",
"reduce": "_count"
}
}
}
La vue MapReduce précédente crée un index basé sur l'adresse team à laquelle l'utilisateur appartient, mais n'inclut que les personnes dont l'adresse électronique a été vérifiée. Comme le réducteur est _count, la
vue indique le nombre de lignes qu'elle contient, par exemple le nombre d'utilisateurs vérifiés dans la base de données.
{"rows":[
{"key":null,"value":10010}
]}
En ajoutant ?group=true, les effectifs sont regroupés par clés distinctes, de sorte que la base de données produit des effectifs par appartenance à une équipe :
{"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 désactivation du réducteur permet d'utiliser la même vue pour la sélection des membres d'une seule équipe ?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"}
]}
Réducteur de somme
Le réducteur _sum totalise les valeurs numériques émises par une vue MapReduce. La valeur de la vue peut être un nombre, un tableau de nombres ou un objet contenant des valeurs numériques. Considérons la définition suivante
MapReduce sur une base de données de produits :
{
"views": {
"productPrices": {
"map": "function(doc) { emit(doc.type, { price: doc.price, tax: doc.tax }); }",
"reduce": "_sum"
}
}
}
La vue est définie en fonction du type du produit, et la valeur est un objet contenant deux valeurs : price et tax. Le réducteur _sum calcule les totaux des valeurs price et tax dans la vue :
{"rows":[
{"key":null,"value":{"price":144.97, "tax":7.32}}
]}
En ajoutant ?group=true lors de l'interrogation de la vue, le résultat est groupé et additionné par une clé unique, dans ce cas, le type de produit :
{"rows":[
{"key":"kitchen","value":{"price":14.99,"tax":1.14}},
{"key":"garden","value":{"price":129.98,"tax":6.18}}
]}
Réducteur de stats
Comme le réducteur _sum, le réducteur _stats travaille sur des nombres, des objets avec des valeurs numériques ou des tableaux de nombres, en renvoyant des comptes, des sommes, des valeurs minimales et maximales
et une somme du carré des valeurs, ce qui est utile pour les calculs de variance ou d'écart type :
{
"views": {
"salesByDate": {
"map": "function(doc) { emit(doc.date, [doc.price, doc.tax]); }",
"reduce": "_stats"
}
}
}
La définition précédente calcule des statistiques sur les valeurs numériques qu'elle trouve dans le tableau transmis en tant que valeur de l'index. Les valeurs sont renvoyées sous forme de tableau dans le même ordre que celui fourni dans la fonction de mappe :
{"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}
]}
]}
Le nombre approximatif de réducteurs distincts
Contrairement aux réducteurs numériques _sum et _stats qui agissent sur la valeur de l'index, le réducteur _approx_count_distinct utilise la clé de la vue. Il estime le nombre de clés distinctes
trouvées dans la vue MapReduce à l'aide d'un algorithme qui utilise beaucoup moins de mémoire qu'un algorithme de comptage exact distinct :
{
"views": {
"estimateIpCount": {
"map": "function (doc) {\n emit(doc.ip, 1);\n}",
"reduce": "_approx_count_distinct"
}
}
}
La définition précédente vise à estimer le nombre d'adresses IP distinctes dans une base de données de journaux de serveurs. L'adresse ip du document est émise en tant que clé de l'index afin que le réducteur _approx_count_distinct puisse estimer le nombre de clés distinctes :
{"rows":[
{"key":null,"value":100528}
]}
Les réducteurs haut/bas
Les réducteurs _top_x et _bottom_x (où x est un nombre compris entre 1 et 100) renvoient un tableau des valeurs x supérieures ou x inférieures d'un groupe de vues, respectivement. Par exemple,
dans une application de jeu, on peut créer une vue dont la clé est l'identifiant de l'utilisateur et dont la valeur est le score obtenu par l'utilisateur. Cette vue peut être utilisée pour créer un tableau d'affichage des meilleurs ou
des pires scores :
{
"views": {
"bestScores": {
"map": "function(doc) { emit(doc.user_id, doc.score); }",
"reduce": "_top_3"
}
}
}
Si nous interrogeons la vue sans aucun paramètre, les trois meilleurs scores de toute la vue sont renvoyés :
{"rows":[
{"key":null,"value":[99,98,97]}
]}
Avec le regroupement (?group=true), les trois meilleurs scores de chaque utilisateur distinct sont renvoyés :
{"rows":[
{"key":"user082","value":[99,98,97]},
{"key":"user291","value":[85,72,42]},
{"key":"user452","value":[55,51,30]}
]}
Les premiers/derniers réducteurs
Les réducteurs _first/_last renvoient la valeur de la première ou de la dernière clé d'un groupe de vues, respectivement. Si nous avons une application IoT qui stocke périodiquement les relevés
de nombreux appareils, nous pouvons créer une vue basée sur l'identifiant de l'appareil et l'heure à laquelle le relevé a été effectué. La valeur de la vue est le document entier :
{
"views": {
"latestReading": {
"map": "function(doc) { emit([doc.deviceid, doc.timestamp], doc); }",
"reduce": "_last"
}
}
}
Cette vue produit des clés et des valeurs de cette forme, la vue étant triée par deviceid et timestamp. Les lignes considérées comme les "premières" et "dernières" lectures pour chaque appareil
sont mises en évidence :
| clé | valeur | Première lecture ( group_level=1 ) | Dernière lecture ( 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 |
L'interrogation de la vue avec group_level=1, en utilisant le réducteur _last, renverra la lecture la plus récente pour chaque identifiant d'appareil dans la base de données :
{"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"}}
]}
Résumé des réducteurs intégrés
| Fonction | Description |
|---|---|
_count |
Génère le nombre de lignes pour une clé spécifique. Les valeurs peuvent être n'importe quel JSON valide. |
_stats |
Génère une structure JSON contenant la somme, le nombre, la valeur minimale, la valeur maximale et la valeur de somme des carrés. Toutes les valeurs doivent être numériques. |
_sum |
Génère la somme de toutes les valeurs pour une clé. Les valeurs doivent être numériques. |
_approx_count_distinct |
Estime le nombre de clés distinctes dans un index de vue à l'aide d'une variante de l'HyperLogLog algorithme. |
_top_x/_bottom_x |
Renvoie un tableau des valeurs x supérieures ou x inférieures du groupe de vues sous forme de tableau, où x est un nombre compris entre 1 et 100. |
_first/_last |
Renvoie les valeurs de la clé de tri la plus basse ou la plus haute, respectivement, pour chaque groupe de vues. |
Fonctions de réduction personnalisées
La plupart des clients estiment que les réducteurs intégrés sont suffisants pour effectuer des agrégations sur les paires key-value de vue émises à partir de leurs fonctions de mappe. Toutefois, dans certains cas inhabituels, une
fonction de réduction JavaScript peut être fournie à la place du nom de l'un des réducteurs intégrés.
Les fonctions de réduction personnalisées sont beaucoup plus lentes et plus difficiles à maintenir que les réducteurs intégrés. Il convient donc de vérifier si un cas d'utilisation peut être satisfait avec un réducteur intégré avant d'en écrire un personnalisé.
Les fonctions de réduction reçoivent trois arguments dans l'ordre suivant :
keysvaluesrereduce
Si une vue possède une fonction de réduction JavaScript personnalisée, celle-ci est utilisée pour générer des résultats agrégés pour la vue. Une fonction de réduction reçoit un ensemble de valeurs intermédiaires et les combine pour obtenir une valeur unique. Une fonction de réduction doit accepter en entrée les résultats émis par sa fonction de mappe correspondante, ainsi que les résultats renvoyés par la fonction de réduction elle-même. Ce dernier cas est appelé "rereduce".
L'exemple ci-dessous décrit les fonctions de réduction.
Voici un exemple de fonction de réduction personnalisée :
function (keys, values, rereduce) {
return sum(values);
}
Les fonctions de réduction doivent traiter deux cas :
-
Lorsque
rereducea pour valeur false :keysest un tableau dont les éléments sont des tableaux au format[key, id], oùkeyest une clé qui est émise par la fonction de mappe,ididentifie le document à partir duquel la clé a été générée etvaluesest un tableau des valeurs qui sont émises pour les éléments respectifs danskeys, par exemple :reduce([ [key1,id1], [key2,id2], [key3,id3] ], [value1,value2,value3], false).
-
Lorsque
rereducea pour valeur true :keysestnull.valuesest un tableau des valeurs renvoyées par les appels précédents de la fonction de réduction, par exemple :reduce(null, [intermediate1,intermediate2,intermediate3], true).
Les fonctions de réduction doivent renvoyer une valeur unique, qui convient pour la zone value de la vue finale et en tant que membre du tableau values qui est transmis à la fonction de réduction.
Souvent, des fonctions de réduction peuvent être écrites pour traiter les appels rereduce sans code supplémentaire, comme la fonction d'addition dans l'exemple précédent. Dans ce cas, l'argument rereduce peut être ignoré.
En alimentant la fonction reduce avec les résultats des fonctions reduce, MapReduce peut diviser l'analyse d'ensembles de données volumineux en tâches parallèles discrètes pouvant être exécutées beaucoup plus rapidement.
Lorsque vous utilisez la fonction de réduction intégrée, si l'entrée n'est pas valide, l'erreur builtin_reduce_error est renvoyée. Des informations plus détaillées sur l'échec figurent dans la zone reason. Les données
originales qui ont causé l'erreur sont renvoyées dans la zone caused_by.
Voici un exemple de réponse :
{
"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
}
]
}
}
]
}
Restrictions des fonctions de mappe et de réduction
Les restrictions des fonctions de mappe et de réduction sont décrites ici.
Transparence référentielle
La fonction de mappage doit être transparente au niveau référentiel. La transparence référentielle signifie qu'une expression peut être remplacée par la même valeur sans que le résultat ne soit modifié, en l'occurrence un document et une paire
key-value. En raison de la transparence référentielle, Les vues IBM Cloudant peuvent être mises à jour de façon incrémentielle et ré indexées uniquement le delta depuis la dernière mise à jour.
Propriétés commutatives et associatives
En plus de la transparence référentielle, la fonction de réduction doit également avoir des propriétés commutatives et associatives pour l'entrée. Ces propriétés permettent à la fonction MapReduce de réduire sa propre sortie et de générer la même réponse, par exemple :
f(Key, Values) == f(Key, [ f(Key, Values) ] )
Ainsi, IBM Cloudant peut stocker des résultats intermédiaires sur les noeuds internes des index d'arbre B. Ces restrictions permettent également de répartir les index sur les différentes machines et de diminuer le temps de requête.
Partitionnement de document
En raison de la fragmentation, IBM Cloudant ne garantit pas que la sortie de deux fonctions de mappe spécifiques sera transmise à la même instance d'un appel reduce. Vous ne devez pas compter sur un ordre précis. La fonction de réduction que
vous utilisez doit prendre en compte toutes les valeurs qui lui sont transmises et renvoyer la bonne réponse indépendamment de la commande. IBM Cloudant vous garanti également d'appeler votre fonction de réduction avec rereduce=true au moment de la requête, même si elle n'a pas besoin de le faire lorsqu'elle a créé l'index. Il est essentiel que vos fonctions marchent correctement dans ce cas (rereduce=true signifie que le paramètre des clés est null et que le tableau de valeurs est rempli avec les résultats des appels de fonction de réduction précédents).
Taille de valeur réduite
IBM Cloudant calcule les index de vue et les valeurs de réduction correspondantes, puis met en cache ces valeurs dans chaque pointeur de noeud d'arbre B. Maintenant, IBM Cloudant peut réutiliser des valeurs réduites lors de la mise à jour de l'arborescence B-tree. Vous devez faire attention à la quantité de données qui est renvoyée à partir des fonctions de réduction.
Il est préférable que la taille de l'ensemble de données renvoyé reste petite et n'augmente pas plus vite que log(num_rows_processed). Si vous ignorez cette restriction, IBM Cloudant ne lance pas automatiquement une erreur, mais
la performance de B-tree se dégrade radicalement. Si votre vue fonctionne correctement avec de petits ensembles de données, mais s'arrête de fonctionner lorsque davantage de données sont ajoutées, il se peut qu'elle enfreigne la restriction
relative au taux de croissance.
Environnement d'exécution
Vos fonctions d'indexation fonctionnent dans un environnement à contraintes de mémoire où le document fait partie de la mémoire utilisée dans l'environnement. La pile et le document de votre code doivent s'insérer dans cette mémoire. Nous limitons les documents à une taille maximale de 64 Mo.
Aucun réducteur JavaScript lorsque options.partitioned a pour valeur true
Les documents de conception dans lesquels options.partitioned a pour valeur true ne peuvent pas contenir de réducteurs JavaScript tels que _stats.
Stockage de la définition de vue
Chaque vue est une fonction JavaScript. Les vues sont stockées dans des documents de conception. Donc, pour stocker une vue, IBM Cloudant stocke simplement la définition de fonction dans un document de conception. Un document de conception peut être créé ou mis à jour à l'instar de n'importe quel autre document.
Pour enregistrer la définition d'une vue,
PUT le contenu de la définition de la vue dans un document _design.
Dans l'exemple suivant, la vue getVerifiedEmails est définie en tant que fonction de mappe et est disponible dans la zone views du document de conception.
Utilisez la méthode PUT pour ajouter une vue dans un document de conception :
PUT $SERVICE_URL/$DATABASE/_design/$DDOC HTTP/1.1
Content-Type: application/json
L'exemple suivant ajoute une nouvelle getVerifiedEmails fonction de vue nommée au allusers document de conception avec la définition de la vue :
{
"views": {
"getVerifiedEmails": {
"map": "function(user) { if(user.email_verified === true){ emit(doc.email, {name: user.name, email_verified: user.email_verified, joined: user.joined}) }} "
}
}
}
Voir les exemples de demande :
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))
L'exemple précédent de Go requiert le bloc d'importation suivant :
import (
"encoding/json"
"fmt"
"github.com/IBM/cloudant-go-sdk/cloudantv1"
)
Tous les exemples de Go nécessitent l'initialisation de l'objet service. Pour plus d'informations, reportez-vous à la documentation de l'API Section Authentification pour avoir des exemples.