Dans deux articles précédents, nous avons vu une introduction à CouchDB et l’utilisation de CouchDB avec Ruby.
Nous sommes donc maintenant capable d’ajouter des données dans notre base et de récupérer une liste de ceux-ci. Mais il peut parfois arriver d’avoir besoin de faire des requêtes plus complexes.

Prenons par exemple une table CouchDB générée par CouchREST. Vous y verrez, pour chacun de vos modèles, un document nommé « _design/Model ». Par exemple, « _design/File ».
Il ne s’agit en réalité par d’un document. Mais d’une vue.
Regardons l’une des vues générées par CouchREST.

{
    "all": {
        "map": "function(doc) {
            if (doc['couchrest-type'] == 'Image') {
                emit(doc['_id'],1);
            }
        }"
   }
}

Vous lisez bien, les vues CouchDB sont du javascript ! :)

Après avoir chargé cette vue, tentez de visiter la page votre_base/_design/votre_vue/_view/all.
Vous constaterez que seuls les éléments de type « Image » sont retournés.
Notre fonction javascript est exécutée sur chacun des éléments. Et seuls ceux que vous désirez (en les insérant avec « emit ») sont retournés.

Prenons le cas d’une plateforme de blog (oui cas bidon).
Supposons que nous désirions visualiser uniquement les documents ayant l’attribut « published » à true.

{
    "all": {
        "map": "function(doc) {
            if (doc['published'] == true) {
                emit(doc['_id'], doc);
            }
        }"
    }
}

De la même manière que tout à l’heure, nous ne retournons que les billets publiés dans notre blog.
Voici ainsi le contenu qui m’est retourné pour la vue précédente

{
    "total_rows":2,
    "offset":0,
    "rows":[
        {
            "id":"7c46156162a59d145cf9cf7850e6b677",
            "key":"7c46156162a59d145cf9cf7850e6b677",
            "value":{
                "_id":"7c46156162a59d145cf9cf7850e6b677",
                "_rev":"5-f726b6d7f469b686079fbe4c5f50726b",
                "title":"My First Blog Post",
                "content":"My Post Content",
                "published":true,
                "comments":[
                    {"author":"John","content":"First Comment"},
                    {"author":"James","content":"Second Comment"}
                ]
            }
        },
        {
            "id":"f14fde843b20e18561ea5e8055dbc3b3",
            "key":"f14fde843b20e18561ea5e8055dbc3b3",
            "value":{
                "_id":"f14fde843b20e18561ea5e8055dbc3b3",
                "_rev":"1-5eef22da217f5858542d175c41d2ef3d",
                "title":"My Second Post",
                "content":"Second Content",
                "published":true
            }
        }
    ]
}

Vous constatez que j’ai inséré plusieurs commentaires sur l’un de mes billets. Il ne s’agit pas de documents différents mais bien du même document.
L’attribut « comments » est un tableau de commentaires, chacun pouvant contenir les éléments que je désire.

Et la, la problématique à laquelle on pense rapidement, c’est : « Mais comment je fais pour récupérer la liste de tous mes documents ? »
Comme précédemment, avec une vue bien évidemment !

{
    "all": {
        "map": "function(doc) {
            for (var i in doc.comments) {
                emit(doc, doc.comments[i]);
            }
        }"
    }
}

Nous parcourons chacun de nos documents. Puis dans chacun de ceux-ci, nous parcourons tous les commentaires.
Et insérons au resultat tous les commentaires.
Le résultat obtenu est le suivant.

{
    "total_rows":2,
    "offset":0,
    "rows":[
        {
            "id":"7c46156162a59d145cf9cf7850e6b677",
            "key":"My First Blog Post",
            "value":{
                "author":"James",
                "content":"Second Comment"
            }
        },
        {
            "id":"7c46156162a59d145cf9cf7850e6b677",
            "key":"My First Blog Post",
            "value":{
                "author":"John",
                "content":"First Comment"
            }
        }
    ]
}

Nous avons bien la liste de tous nos commentaires. Et (vu que nous l’avons ajoutés au résultat), nous avons même le titre billet qui va avec ! :)

Et les performances dans tout ça ?
Comme c’est de l’HTTP, il y a du cache natif, que CouchDB gère parfaitement bien. Si vous chargez plusieurs fois votre vue, vous verrez donc que la page retournée est bien un 304 not modified.
Je n’ai pas testé avec énormément d’enregistrements. Mais j’ai discuté de cela avec Mirsal lundi soir lors de l’apéro Web Event Lyon. Et apparemment, même avec des milliers de billets, la chose ne posera pas trop de problèmes.
Je reviendrai là-dessus dans quelques mois si il y a matière à en dire quelque chose ;)

Quoi qu’il en soit, je trouve les vues particulièrement intéressantes. Je me retrouve régulièrement frustré par les limitations de SQL.
En développant celles-ci en Javascript, la chose devient virtuellement illimitée.

Mirsal disait également hier: « Les bases de données orientées document sont généralement ce qu’il y a de plus adapté.
C’est SQL qui devrait être utilisé uniquement dans des cas atypiques. »
Je n’irai pas jusqu’à m’avancer comme cela. Mais les possibilités sont tellement impressionnantes que cela laisse rêveur.

11 Réponses à “CouchDB et requêtes avancées: les vues”

  1. Raphaël dit :

    Très intéressant, je n’ai pas encore eu le temps de me plonger dans les BDD « orientées documents » car je n’en ai pas l’utilité.

    Contrairement à ce que Mirsal dit, je pense sincèrement que c’est les BDD orientées documents qui sont pour des cas atypiques.

  2. J’ai deux remarques :
    - je n’ai jamais touché à CouchDB, mais ta façon de récupérer tous les commentaires me semble extrêmement compliquée, par rapport au besoin.
    Il doit y avoir une façon plus simple, de type requêtage xPath (genre Document.comment.all )
    - il existe un autre outil, type CouchDB, qui me semble très intéressant, c’est MangoDB. Il me paraît plus simpliste niveau utilisation (et extrêmement performant).

    Note au passage : ca faisait un moment que je m’étais balladé sur ton blog, y a des articles vraiment intéressants :)

    @Raphael : les bdd orientées document sont atypiques aujourd’hui parce que pendant longtemps elles n’étaient pas exploitables dans un environnement de production a forte volumétrie (niveau performances particulièrement). Je pense qu’elles vont réellement s’imposer comme un standard dans les années qui viennent, parce que correspondant beaucoup plus à la « vraie vie » qu’un stockage relationnel.

  3. Raphaël dit :

    @Sébastien j’ai encore de la peine à voir une BDD « documents » pour de la gestion (ecommerce, logistique, production etc…). Pourtant c’est 90% des cas d’utilisation d’une BDD non ? +1 pour MangoDB sinon !

  4. 90% des cas d’utilisation ? huhu. Pas chez moi.

    Dans les projets que nous traitons, ce type de stockage représente 90% des besoins !

    D’un point de vue plus global, on est certainement pas à 90% c’est évident. Mais je reste persuadé que ceci va prendra le pas petit à petit sur le relationnel …

    On en reparle dans 4/5 ans ! ;-)

  5. Raphaël dit :

    @Sébastien donne moi des exemples parce que là sincèrement à part quelques cas très particuliers… Tu vois une BDD documents remplacer le système de gestion d’un supermarché ? d’un prestataire logistique ? d’un site ecommerce ? d’un ERP ? d’une GPAO ? C’est pourtant la majorité des CU d’une BDD relationnelle…

  6. Damien dit :

    Personnelement, je le vois tout à fait.

  7. @Raphael Ce que je veux te dire, c’est qu’il n’y a pas que l’informatique de gestion, et qu’il me semble bien simpliste de résumer le marché de l’informatique à ça (cf ton « 90% »).

    J’ai des dizaines d’exemples de projets de dev ou un CouchDB ou un MangoDB aurait été très pertinent (beaucoup plus qu’un stockage relationnel standard, qui m’a causé d’ENORMES problèmes). En particulier, bien entendu, tout ce qui tourne autour de la gestion de contenu (tiens ? « orienté Document » ? quel hasard !) et du e-commerce (oh que oui).

    Après, libre à chacun de faire ce qu’il veut. Y’aura toujours des suiveurs et des suivis ! :-)

  8. Damien dit :

    Concernant notamment le « doc.comments.all », apparemment, non, cela n’est pas possible.
    Voir notamment à ce sujet un article (en) sur le même sujet :
    http://www.cmlenz.net/archives/2007/10/couchdb-joins

  9. Raphaël dit :

    @Sébastien je résume pas le marché de l’informatique mais le marché des BDD. Désolé mais quand tu vois le poids des éditeurs d’ERP/GPAO (SAP le premier) sur le marché des BDD (Oracle etc…).

    @Damien la gestion c’est des données très structurées avec des fortes contraintes relationnelles, tout le contraire d’une BDD orientée documents ! Evidemment que tu peux le faire mais c’est juste pas le meilleur outil.

  10. @Raphael

    AMHA, les bases de données orientées documents sont plus adaptées pour la pluspart des cas d’utilisation d’une base de données *sur le web* (un petit bout de la discussion s’est perdu en chemin)

    Quand il y a besoin d’un comportement ACID, l’utilisation d’une base de données relationelle est pleinement justifié, seulement ça se paye très cher en termes de performances (les sgbd relationnels verouillent habituellement les données de façon à ne pas permettre les accès concurrents lors des insertions)

    @Damien

    Très bon article :)

  11. En général emit(doc['_id'], doc); n’est pas une bonne pratique, il vaut mieux utiliser emit(doc['_id'], null); et effectuer une requête avec ?include_docs=true&reduce=false

Laisser une réponse

 
Fork me on GitHub