web-dev-qa-db-fra.com

Principes de modélisation des documents CouchDB

J'ai une question à laquelle j'essaie de répondre depuis un certain temps, mais je n'arrive pas à comprendre:

Comment concevez-vous ou divisez-vous les documents CouchDB?

Prenez un article de blog par exemple.

La manière semi "relationnelle" de le faire serait de créer quelques objets:

  • Publier
  • Utilisateur
  • Commentaire
  • Marque
  • Fragment

Cela a beaucoup de sens. Mais j'essaie d'utiliser couchdb (pour toutes les raisons pour lesquelles c'est génial) pour modéliser la même chose et cela a été extrêmement difficile.

La plupart des articles de blog vous donnent un exemple simple de la façon de procéder. Ils le divisent essentiellement de la même manière, mais disent que vous pouvez ajouter des propriétés "arbitraires" à chaque document, ce qui est certainement agréable. Vous auriez donc quelque chose comme ça dans CouchDB:

  • Publication (avec des balises et des extraits de "pseudo" modèles dans la doc)
  • Commentaire
  • Utilisateur

Certaines personnes diraient même que vous pouvez y ajouter le commentaire et l'utilisateur, vous auriez donc ceci:

post {
    id: 123412804910820
    title: "My Post"
    body: "Lots of Content"
    html: "<p>Lots of Content</p>"
    author: {
        name: "Lance"
        age: "23"
    }
    tags: ["sample", "post"]
    comments {
        comment {
            id: 93930414809
            body: "Interesting Post"
        } 
        comment {
            id: 19018301989
            body: "I agree"
        }
    }
} 
</code>

Cela a l'air très agréable et est facile à comprendre. Je comprends également comment vous pouvez écrire des vues qui ont extrait uniquement les commentaires de tous vos documents de publication, pour les intégrer dans des modèles de commentaire, de même avec les utilisateurs et les balises.

Mais alors je pense, "pourquoi ne pas simplement mettre tout mon site dans un seul document?":

site {
    domain: "www.blog.com"
    owner: "me"
    pages {
        page {
            title: "Blog"
            posts {
                post {
                    id: 123412804910820
                    title: "My Post"
                    body: "Lots of Content"
                    html: "<p>Lots of Content</p>"
                    author: {
                        name: "Lance"
                        age: "23"
                    }
                    tags: ["sample", "post"]
                    comments {
                        comment {
                            id: 93930414809
                            body: "Interesting Post"
                        } 
                        comment {
                            id: 19018301989
                            body: "I agree"
                        }
                    }
                }
                post {
                    id: 18091890192984
                    title: "Second Post"
                    ...
                }
            }
        }
    }
} 
</code>

Vous pouvez facilement faire des vues pour trouver ce que vous vouliez avec cela.

Alors la question que j'ai est, comment déterminez-vous quand diviser le document en petits documents, ou quand faire des "RELATIONS" entre les documents?

Je pense qu'il serait beaucoup plus "orienté objet", et plus facile à mapper sur des objets de valeur, s'il était divisé comme suit:

posts {
    post {
        id: 123412804910820
        title: "My Post"
        body: "Lots of Content"
        html: "<p>Lots of Content</p>"
        author_id: "Lance1231"
        tags: ["sample", "post"]
    }
}
authors {
    author {
        id: "Lance1231"
        name: "Lance"
        age: "23"
    }
}
comments {
    comment {
        id: "comment1"
        body: "Interesting Post"
        post_id: 123412804910820
    } 
    comment {
        id: "comment2"
        body: "I agree"
        post_id: 123412804910820
    }
} 
</code>

... mais cela commence à ressembler davantage à une base de données relationnelle. Et souvent, j'hérite de quelque chose qui ressemble à "l'ensemble du site dans un document", il est donc plus difficile de le modéliser avec des relations.

J'ai lu beaucoup de choses sur comment/quand utiliser les bases de données relationnelles par rapport aux bases de données de documents, ce n'est donc pas le problème principal ici. Je me demande plutôt quelle est la bonne règle/le bon principe à appliquer lors de la modélisation des données dans CouchDB.

Un autre exemple concerne les fichiers/données XML. Certaines données XML ont des niveaux d'imbrication supérieurs à 10+, et je voudrais visualiser qu'en utilisant le même client (Ajax sur Rails par exemple, ou Flex) que je voudrais rendre JSON à partir d'ActiveRecord, CouchRest , ou tout autre mappeur relationnel d'objets. Parfois, j'obtiens d'énormes fichiers XML qui représentent la structure complète du site, comme celui ci-dessous, et je devrais le mapper à des objets de valeur à utiliser dans mon Rails = application donc je n'ai pas à écrire une autre façon de sérialiser/désérialiser les données:

<pages>
    <page>
        <subPages>
            <subPage>
                <images>
                    <image>
                        <url/>
                    </image>
                </images>
            </subPage>
        </subPages>
    </page>
</pages> 
</code>

Les questions générales de CouchDB sont donc:

  1. Quelles règles/principes utilisez-vous pour diviser vos documents (relations, etc.)?
  2. Est-il acceptable de mettre l'ensemble du site dans un seul document?
  3. Si oui, comment gérez-vous la sérialisation/désérialisation de documents avec des niveaux de profondeur arbitraires (comme le grand exemple json ci-dessus ou l'exemple xml)?
  4. Ou ne les transformez-vous pas en VO, décidez-vous simplement que "celles-ci sont trop imbriquées dans la carte relationnelle objet, donc je vais simplement y accéder en utilisant des méthodes XML/JSON brutes"?

Merci beaucoup pour votre aide, la question de savoir comment diviser vos données avec CouchDB a été difficile pour moi de dire "c'est ainsi que je devrais le faire à partir de maintenant". J'espère arriver bientôt.

J'ai étudié les sites/projets suivants.

  1. Données hiérarchiques dans CouchDB
  2. Wiki CouchDB
  3. Canapé - Application CouchDB
  4. CouchDB Le guide définitif
  5. PeepCode CouchDB Screencast
  6. CouchRest
  7. README CouchDB

... mais ils n'ont toujours pas répondu à cette question.

119
Lance Pollard

Je sais que c'est une vieille question, mais je l'ai rencontrée en essayant de trouver la meilleure approche pour ce même problème. Christopher Lenz a écrit un article sur Nice sur les méthodes de modélisation des "jointures" dans CouchDB . L'un de mes points à retenir était: "La seule façon d'autoriser l'ajout non conflictuel de données connexes est de placer ces données dans des documents distincts." Donc, pour des raisons de simplicité, vous voudriez vous pencher vers la "dénormalisation". Mais vous heurterez une barrière naturelle en raison d'écritures contradictoires dans certaines circonstances.

Dans votre exemple de publications et de commentaires, si une publication unique et tous ses commentaires se trouvaient dans un même document, deux personnes tentant de publier un commentaire en même temps (c'est-à-dire contre la même révision du document) provoqueraient un conflit. Cela empirerait encore dans votre scénario "site entier dans un seul document".

Je pense donc que la règle de base serait de "dénormaliser jusqu'à ce que ça fasse mal", mais le point où cela "fera mal" est où vous avez une forte probabilité que plusieurs modifications soient publiées contre la même révision d'un document.

16
jake l

Le livre dit, si je me souviens bien, de dénormaliser jusqu'à "ça fait mal", tout en gardant à l'esprit la fréquence à laquelle vos documents peuvent être mis à jour.

  1. Quelles règles/principes utilisez-vous pour diviser vos documents (relations, etc.)?

En règle générale, j'inclus toutes les données nécessaires pour afficher une page concernant l'élément en question. En d'autres termes, tout ce que vous imprimeriez sur une feuille de papier réelle que vous remettriez à quelqu'un. Par exemple. un document de cotation boursière comprendrait le nom de la société, la bourse, la devise, en plus des chiffres; un document contractuel comprendrait les noms et adresses des contreparties, toutes les informations sur les dates et les signataires. Mais les cotations boursières de dates distinctes formeraient des documents séparés, des contrats séparés formeraient des documents distincts.

  1. Est-il acceptable de mettre l'ensemble du site dans un seul document?

Non, ce serait idiot, car:

  • il faudrait lire et écrire l'intégralité du site (le document) à chaque mise à jour, et c'est très inefficace;
  • vous ne bénéficieriez d'aucune mise en cache de vue.
15
Eero

Je pense que la réponse de Jake cloue l'un des aspects les plus importants du travail avec CouchDB qui peut vous aider à prendre la décision de cadrage: les conflits.

Dans le cas où vous avez des commentaires en tant que propriété de tableau de la publication elle-même, et que vous avez juste une base de données `` post '' avec un tas d'énormes documents `` post '', comme Jake et d'autres l'ont correctement souligné, vous pouvez imaginer un scénario sur un article de blog très populaire où deux utilisateurs soumettent simultanément des modifications au document de publication, ce qui entraîne une collision et un conflit de version pour ce document.

À CÔTÉ: Comme cet article le souligne , considérez également que chaque fois que vous demandez/mettez à jour ce document, vous devez obtenir/définir le document dans son intégralité, donc en passant autour de documents massifs qui représentent soit l'ensemble du site, soit un article contenant de nombreux commentaires, cela peut devenir un problème que vous voudriez éviter.

Dans le cas où les publications sont modélisées séparément des commentaires et que deux personnes soumettent un commentaire sur une histoire, celles-ci deviennent simplement deux documents de "commentaire" dans cette BD, sans problème de conflit; juste deux opérations PUT pour ajouter deux nouveaux commentaires à la base de données "comment".

Ensuite, pour écrire les vues qui vous rendent les commentaires d'une publication, vous passeriez le postID puis émettriez tous les commentaires qui font référence à cet ID de publication parent, triés dans un ordre logique. Peut-être que vous passez même quelque chose comme [postID, byUsername] comme clé de la vue "commentaires" pour indiquer le message parent et comment vous voulez que les résultats soient triés ou quelque chose dans ce sens.

MongoDB gère les documents un peu différemment, permettant aux index d'être construits sur les sous-éléments d'un document, vous pouvez donc voir la même question sur la liste de diffusion MongoDB et quelqu'un disant "faites simplement des commentaires une propriété de la publication parent".

En raison du verrouillage en écriture et de la nature de maître unique de Mongo, le problème de révision conflictuel de deux personnes ajoutant des commentaires ne se poserait pas là-bas et la capacité de requête du contenu, comme mentionné, n'est pas affectée trop mal en raison de index.

Cela étant dit, si vos sous-éléments dans soit DB vont être énormes (disons 10s de milliers de commentaires), je crois que c'est la recommandation de les deux camps pour faire ces éléments séparés; J'ai certainement vu que c'était le cas avec Mongo car il y a des limites supérieures sur la taille d'un document et de ses sous-éléments.

5
Riyad Kalla