Accueil du site > SPIP > Utiliser CouchDB avec SPIP

Utiliser CouchDB avec SPIP

La boucle (WIKILEAKS)

samedi 8 janvier 2011, par Fil

CouchDB est un gestionnaire de base de données orienté documents, faisant partie du mouvement NoSQL : au lieu d’être ordonnée en lignes et en colonnes, la base est constituée d’une collection de documents au format JSON. On l’interroge par des requêtes HTTP. Ci-dessous, un exemple d’utilisation avec SPIP.

HTTP et JSON : la boucle (DATA) de SPIP peut nativement parler à CouchDB. Comme exemple d’application, voici un squelette permettant d’afficher les « câbles » diplomatiques américains révélés par Wikileaks.

(Pour en savoir plus sur CouchDB : http://couchdb.apache.org/)

La base de données

Le 6 décembre 2010, Benoît Chesneau annonçait qu’il mettait les cablegate à disposition dans une base CouchDB :

You can now replicate cables in your couchdb via https https://upondata.com/cablesgate , more mirroring soon. #cablesgate #wikileaks

On peut explorer cette base de données en utilisant la superbe interface Futon, associée à CouchDB : https://upondata.com/_utils/

PNG - 66.3 ko
Futon

Sous CouchDB, la liste des documents disponibles se trouve derrière une adresse du type URL_BASE_DONNEES/_all_docs, et les résultats sont renvoyés sous forme d’un tableau au format JSON, dont l’élément rows contient les résultats.

Une simple boucle permet de vérifier rapidement le contenu de cette base :

<BOUCLE_liste(DATA)
{source json, https://upondata.com/cablesgate/_all_docs}
{datapath rows}
{pagination 10}
>
[(#VALEUR|print)<br />]
</BOUCLE_liste>

(On a ajouté la pagination car la base contient déjà plus de 1500 documents).

Résultat :

00HARARE5461, 00HARARE5461, 1-ab2f3b839699e38a0ade9a69580d3807
00HARARE6677, 00HARARE6677, 1-ff69b744322ed2ad40684a3049438a76
01PRETORIA1173, 01PRETORIA1173, 1-17ae5eba0e6fa54f756e11c33db3c12c
01VATICAN1261, 01VATICAN1261, 1-5b2bb5179171b8fdb6cf5c49e70f7efc
01VATICAN3507, 01VATICAN3507, 1-cf8f30b56ae3b022a9a13c18e9861fdf
02BRASILIA4227, 02BRASILIA4227, 1-618545c581de8c36034e69125ab91b91
02ROME1196, 02ROME1196, 1-c3886574e19883f1710941c5306f58c8
02VATICAN819, 02VATICAN819, 1-118b2bb09f1aeb02771368f88ec03439
03ABUDHABI2641, 03ABUDHABI2641, 1-9253471e5f4f776c89032acb245cab44
03BRASILIA3122, 03BRASILIA3122, 1-03f664271441db15b2a72730af841c11

Ces valeurs sont la clé, l’identifiant (dans ce cas, égal à la clé), et le numéro de révision de chaque document.

Pour aller chercher un document particulier il suffit de solliciter l’URL correspondant à son identifiant : par exemple https://upondata.com/cablesgate/00H... ; le document s’affiche alors au format JSON.

Une deuxième boucle (DATA) permet d’afficher proprement les champs du document :

<BOUCLE_cable(DATA)
{source json, https://upondata.com/cablesgate/00HARARE5461}
>
[<dt>#CLE</dt>
<dd><kbd>(#VALEUR|print)</kbd></dd>]
</BOUCLE_cable>

Résultat :

_id
00HARARE5461
_rev
1-ab2f3b839699e38a0ade9a69580d3807
origin
CONFIDENTIAL
date_time
2000-09-27 14:02
body
C O N F I D E N T I A L SECTION 01 OF 02 HARARE 005461 SIPDIS NSC FOR SENIOR AFRICA DIRECTOR GAYLE SMITH LONDON FOR CHARLES GURNEY PARIS FOR BISA WILLIAMS PASS USTR FOR ROSA WHITAKER EO 12958 DECL : 09/21/10 TAGS PGOV, PINS, ZI, MDC, ZANU-PF (...)
classification
2010-12-18 21:09
header
This record is a partial extract of the original cable. The full text of the original cable is not available. (...)
refererence_id
00HARARE5461

Remarque : à l’inverse des boucles standard de SPIP, basées sur SQL, on voit donc qu’il faut ici appeler des services différents pour obtenir soit un document, soit une liste de documents.

Ajoutons maintenant quelques bidouilles décoratives :
— pagination
— passage de l’identifiant du câble par la variable #ID
— utilisation du critère {si #ID} pour ne lancer la <BOUCLE_cable ...> qu’en présence d’un identifiant
— gestion des sauts de ligne, marqués dans le champ body par la séquence &#x000A;

Et nous obtenons, en deux boucles, une première version de notre application :

<B_liste>
[<p class="pagination">(#PAGINATION)</p>]
<ul>
<BOUCLE_liste(DATA)
{source json, https://upondata.com/cablesgate/_all_docs}
{datapath rows}
{pagination 10}
>
<li><a href="[(#SELF|parametre_url{id,#VALEUR{id}})]"
 >#VALEUR{id}</a></li>
</BOUCLE_liste>
</ul>
</B_liste>

<B_cable>
<h2>#ID</h2>
<BOUCLE_cable(DATA)
{source json, https://upondata.com/cablesgate/#ID}
{si #ID}
>
[<dt>#CLE</dt>
<dd><kbd>(#VALEUR|print|replace{&#x000A;,<br />})</kbd></dd>]
</BOUCLE_cable>

Trier par date, afficher le sujet

Pour aller plus loin, on veut pouvoir afficher notre liste de documents non pas dans l’ordre de stockage dans la base de données, mais selon leur date de publication (donnée dans le champ classification de la base de Benoît... on dirait qu’il s’est un peu emmêlé les pinceaux dans ses nommages...) ; on veut aussi afficher à côté le sujet du câble, qu’il va falloir extraire du champ body de chaque document.

Pour cela, il faut ajouter une vue à la base de données. Comme celle-ci est gérée par Benoît Chesneau, nous n’y avons pas accès en écriture, et ne pouvons donc rien y ajouter. Nous allons donc créer une base CouchDB nous appartenant, dans laquelle on va recopier (« replicate » en langage CouchDB) celle de Benoît, puis ajouter notre vue.

Deux méthodes :
— soit on installe CouchDB sur notre serveur http://wiki.apache.org/couchdb/Inst...
— soit on crée un compte hébergé sur www.couchone.com

Dans les deux cas, l’interface Futon permet de naviguer facilement dans les données. Une fois notre base créée, on utilise le menu Replicator pour lui dire de recopier la base source https://upondata.com/cablesgate dans la nôtre.

PNG - 29.9 ko
Replicator

Nous ajoutons ensuite une vue (view) :

PNG - 43.5 ko
View

Voici le code de notre vue ; c’est un JSON décrivant une fonction map écrite en javascript.

{
  "classification": {
      "map": "function (doc) {
        if (doc.body && (doc.subject = doc.body.match(/SUBJECT:(.*?)&#x000A;&#x000A;/))) {
          doc.subject = doc.subject[1].substr(0,300);
        }
        else {
          doc.subject = 'Untitled';
        }
        emit(doc.classification, {
          date_time:doc.date_time,
          subject:doc.subject,
          origin:doc.origin||null
        });
      }"
  }
}

Comme on l’a vu avec la boucle ci-dessus, chaque document contient un champ classification avec la date de mise en ligne du document.

Notre fonction map: va donc émettre, pour chaque document, une clé correspondant à cette valeur doc.classification, ce qui permettra de trier les résultats selon cette valeur. On émet aussi, en valeur, un sujet extrait à coup d’expression rationnelle du doc.body (quand c’est possible), ainsi que les champs date_time et origin du document.

Dès lors, si on appelle l’URL /_design/sort/_view/classification sur notre base CouchDB, on obtient la liste des câbles triés par date de mise en ligne, ainsi que leur sujet et niveau de classification.

{
"id":"04ANKARA348",
"key":"2010-11-28 18:06",
"value":{
 "date_time":"2004-01-20 12:12",
 "subject":"Untitled",
 "origin":"CONFIDENTIAL"
}
},
{
"id":"04ANKARA7211",
"key":"2010-11-28 18:06",
"value":{
 "date_time":"2004-12-30 05:05",
 "subject":"Untitled",
 "origin":"SECRET"
}
},
{
"id":"05ABUDHABI2178",
"key":"2010-11-28 18:06",
"value":{
 "date_time":"2005-05-16 09:09",
 "subject":"Untitled",
 "origin":"SECRET"
}
},
{
"id":"05ANKARA1730",
"key":"2010-11-28 18:06",
"value":{
 "date_time":"2005-03-25 09:09",
 "subject":" TURKEY ADRIFT",
 "origin":"CONFIDENTIAL"
}
},
...

Pour les obtenir dans l’ordre inverse (les plus récents en premier), on utilise l’URL /_design/sort/_view/classification?descending=true.

Notre application complète

Voici au final le squelette de notre application complète. Ici, on a installé CouchDB en local, l’URL de notre base est http://127.0.0.1:5984/cablegate

#SET{base,http://127.0.0.1:5984/cablegate}

<B_liste>
[<p class="pagination">(#PAGINATION)</p>]
<ul>
<BOUCLE_liste(DATA)
{source json, #GET{base}/_design/sort/_view/classification?descending=true}
{datapath rows}
{pagination 5}
>
<li><a href="[(#SELF|parametre_url{id,#VALEUR{id}})]"
 >#VALEUR{id}</a><br />
<small>#VALEUR{value/subject}<br />
[(#VALEUR{value/date_time})] | #VALEUR{value/origin}
</small>
</li>
</BOUCLE_liste>
</ul>
</B_liste>

<B_cable>
<h2>#ID</h2>
<BOUCLE_cable(DATA)
{source json, #GET{base}/#ID}
{si #ID}
>
[<dt>#CLE</dt>
<dd><kbd>(#VALEUR|print|replace{&#x000A;,<br />})</kbd></dd>]
</BOUCLE_cable>

Résultat :

0 | 5 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | ...

  • 09BRASILIA1239
    HANDLING VISA REQUEST FROM BRAZILIAN INVOLVED IN THE 1969 KIDNAPPING OF THE U.S.
    2009-10-15 17:05 | SECRET//NOFORN
  • 04BRASILIA2803
    Untitled
    2004-11-12 16:04 | UNCLASSIFIED//FOR OFFICIAL USE ONLY
  • 10SANTIAGO25
    Meet Chile's President-Elect, Sebastian Pinera
    2010-01-22 20:08 | CONFIDENTIAL
  • 09SANTIAGO867
    CHILE: Conservatives Beat Back Skeleton in Pinera's Closet
    2009-10-09 15:03 | CONFIDENTIAL
  • 08SANTIAGO249
    CHILE'S "NEXT PRESIDENT" WILL PROPOSE A "NEW DEAL": A TAD EARLY FOR COMPARISONS TO FDR
    2008-03-17 14:02 | CONFIDENTIAL

04BRASILIA2803

_id
04BRASILIA2803
_rev
1-385f6e7e62f2249fb1877e04b9819021
origin
UNCLASSIFIED//FOR OFFICIAL USE ONLY
date_time
2004-11-12 16:04
body
UNCLAS BRASILIA 002803

SIPDIS

SENSITIVE

PORT-AU-PRINCE FOR AMB. FOLEY

E.O. 12958 : N/A
TAGS : PREL PGOV BR HA POL MIL

¶ 1. (SBU) On November 9, Brazilian Federal Deputy Maninha paid a courtesy call on Ambassador Danilovich. Maninha is the current president of COPA (Confederation of the Parliaments of the Americas), and this week she...
classification
2010-12-28 00:12
header
This record is a partial extract of the original cable. The full text of the original cable is not available.
refererence_id
04BRASILIA2803

La démo est visible ici : http://zzz.rezo.net/cablegate/.

* * *

J’espère que cet exemple vous aura donné envie de jouer avec SPIP et CouchDB. N’hésitez pas à utiliser le forum ci-dessous pour partager vos expériences.