Question Contraintes uniques dans couchdb


Existe-t-il des techniques / propositions pour imposer des contraintes uniques? Oui, nous pouvons créer des clés uniques, mais nous ne pouvons pas changer les clés et les clés. De plus, cette approche ne convient pas pour une validation compliquée (identifiant unique distinct, email unique distinct, etc.)

Par exemple, un compte devrait avoir identifiant unique et email. Dériver une clé de ce champs résultera en incohérence:

key1: "Account-john@example.net-john", { email: "john@example.net", login: "john"}
key2: "Account-mary@example.net-mary", { email: "mary@example.net", login: "mary"}

En regardant bien, mais:

key1: "Account-john@example.net-mary", { email: "john@example.net", login: "mary"}
key2: "Account-mary@example.net-mary", { email: "mary@example.net", login: "mary"}

Oups, maintenant nous avons 2 comptes avec login: "mary"


25
2017-10-09 00:16


origine


Réponses:


C'est l'un des bits les moins amusants de CouchDB. La meilleure façon de gérer des champs uniques susceptibles de changer (comme dans votre exemple utilisateur) est de créer des documents de type «pointeur» avec les valeurs uniques en tant que composant de la clé, puis de les utiliser pour obtenir des valeurs uniques. Pour ce faire, vous devez disposer d'une clé prévisible pour le document principal, puis enregistrer les documents de réclamation de champ uniques avant d'enregistrer le document principal (et laisser les conflits de clé empêcher l'enregistrement du document principal).

Avec un utilisateur avec un nom d'utilisateur unique et un e-mail unique, vos documents principaux peuvent ressembler à ceci:

user-1234: { username: "kurt", email: "kurt@localhost" }
user-9876: { username: "petunia", email: "pie@crust.com" }

Les pointeurs de champs uniques ressemblent à ceci:

user-username-kurt: { primary_doc: "user-1234" }
user-email-kurt@localhost: { primary_doc: "user-1234" }
user-username-petunia: { primary_doc: "user-9876" }
user-email-pie@crust.com: { primary_doc: "user-9876" }

La création ou la mise à jour d'un utilisateur prend les mesures suivantes:

  1. Préparez votre document utilisateur, générez une clé si nécessaire
  2. Enregistrer un document "pointeur" pour chaque champ unique modifié
  3. Si vous enregistrez l'un de ces fichiers, arrêtez et corrigez les erreurs
  4. Enregistrer le document utilisateur principal

L'étape 3 demandera un peu de réflexion. Par exemple, vous ne voudrez pas essayer les valeurs uniques de revendication pour les champs qui n'ont pas changé. Vous pourriez, mais vous auriez à mettre une logique supplémentaire pour gérer un cas où vous réclamez une valeur pour un utilisateur qui possède déjà cette valeur.

L’étape 3 serait un bon endroit pour permettre aux gens d’accepter également les anciennes valeurs revendiquées. Si un utilisateur a "libéré" le nom d'utilisateur kurt, par exemple, je peux simplement mettre à jour ce document particulier pour qu'il pointe vers ma nouvelle clé après avoir vérifié qu'il n'est plus utilisé. L'alternative consiste à effacer les valeurs uniques revendiquées lorsqu'elles changent. Je ne suis pas sûr de ce qui serait moins de travail, vraiment. Laisser les anciennes valeurs revendiquées est le plus logique pour moi.

La chose intéressante à propos de cette solution est que vous n'avez pas besoin d'utiliser ces documents de pointeur pour rien une fois qu'ils sont créés. Vous pouvez créer des vues comme d'habitude sur vos documents utilisateur, et les utiliser pour interroger par courrier électronique ou nom d'utilisateur.

Cette configuration vous permet également d'établir des relations sans avoir à vous soucier des clés utilisateur en cascade. Je ne sais pas pour vous, mais mes documents utilisateur sont référencés par à peu près tous les autres documents du système. Changer une clé d'utilisateur serait une douleur énorme dans le cul.


15
2017-12-19 17:42



Réponse de base

Structurer votre POST / PUT pour le document qui a le champ que vous souhaitez conserver unique comme suit:

  1. Créer un vue. dans le fonction de carte utiliser le champ que vous souhaitez appliquer unique comme le clé. La valeur ne peut être rien. Utilisez le réduire la fonction pour obtenir un compte pour chacune de vos clés. Le meilleur moyen (pour les performances) est d’utiliser le _compter réduire la fonction.

  2. Immédiatement après vous PUT / POST un nouveau document dans la base de données, saisir le retour id et rev et OBTENIR / yourdb / _design / yourapp / _view / viewname? group = true & key = "valeur-de-votre-unique-champ-de-l'étape-1".

  3. Si le résultat du dernier OBTENIR vous donne une valeur de comptage autre que 1, alors vous venez d'insérer un duplicata. Immédiatement EFFACER / yourdb /id-de-pas-2? rev =rev-de-step-2.

  4. Se détendre.


Exemple brut

Disons que vous stockez des comptes d'utilisateur et que vous voulez vous assurer que l'adresse e-mail est unique, mais vous ne voulez pas en faire l'ID de document (pour quelque raison que ce soit). Créez une vue pour vérifier rapidement l'unicité de l'adresse électronique décrite ci-dessus. Appelons ça des emails. Il aurait un fonction de carte peut-être similaire à cela ...

function(doc) {  
  if(doc.type === 'account') {
    emit(doc.email, 1);
  }
}

Et juste _count comme le réduire la fonction. Si vous émettez un 1 comme ci-dessus _sum travaillera également. Avoir et vérifier un type Le champ sur votre document, comme indiqué ci-dessus, n’est qu’une convention, mais je trouve cela parfois utile. Si vous ne stockez que des comptes d'utilisateur, vous n'en avez probablement pas besoin.

Disons maintenant que nous insérons un document comme ça ...

POST /mydb/
{
  "name": "Joe",
  "email": "joe@example.org"
}

Et CouchDB répondra avec quelque chose comme ...

{
  ok: true,
  id: 7c5c249481f311e3ad9ae13f952f2d55,
  rev: 1-442a0ec9af691a6b1690379996ccaba6
}

Vérifiez si nous avons maintenant plusieurs joe@example.org dans la base de données ...

GET /mydb/_design/myapp/_view/emails/?group=true&key="joe@example.org"

Et CouchDB répondra avec quelque chose comme ...

{
  rows: [
    {
      key: "joe@example.org",
      value: 1
    }
  ]
}

Si value est autre chose que 1 Dans cette réponse, vous avez probablement simplement inséré un courrier électronique en double. Donc, supprimez le document et renvoyez une erreur dans le sens de L'adresse email doit être unique, similaire à une réponse de base de données SQL typique, ou ce que vous voulez.

La suppression irait quelque chose comme ça ...

DELETE /mydb/7c5c249481f311e3ad9ae13f952f2d55?rev=1-442a0ec9af691a6b1690379996ccaba6

Brève discussion (si c'est important pour vous)

Si vous venez d'un bon vieux contexte SQL, cela va sembler faux et bizarre. Avant de sortir, considérez ces points. Avoir une contrainte sur un champ, comme l'unicité ou autre, implique un schéma. CouchDB est sans schemaless. En venant de * SQL, vous devrez changer votre façon de penser. ACID et les serrures ne sont pas la seule voie. CouchDB offre beaucoup de flexibilité et de puissance. La façon dont vous l'utilisez pour obtenir ce que vous voulez va dépendre des détails de votre cas d'utilisation et de la façon dont vous pouvez échapper aux limites de la pensée traditionnelle des bases de données relationnelles.

La raison pour laquelle je mets parfois en œuvre l’unicité de cette façon est que Couchy pour moi. Si vous pensez à la manière dont CouchDB fonctionne avec la gestion des conflits de mises à jour, etc., cette approche suit le même flux. Nous stockons le document qui nous a été donné, puis nous vérifions si notre champ est unique. Si ce n'est pas le cas, récupérez le document que nous venons d'insérer par la poignée que nous avons encore sur lui, et faites quelque chose pour résoudre le besoin d'unicité, comme le supprimer par exemple.

Vous pouvez être tenté dans l'exemple ci-dessus de vérifier l'unicité de l'adresse e-mail avant toi POST Le document. Faites attention!! S'il y a plusieurs choses en cours, il est possible qu'un autre document avec la même adresse e-mail puisse être inséré dans la base de données en un instant. après vous vérifiez si l'e-mail existe, mais avant tu fait ton POST! Cela vous laissera un email en double.

Il n'y a rien de mal à exécuter le chèque pour l'adresse e-mail avant vous POST. Par exemple, c'est une bonne idée si votre utilisateur remplit un formulaire et que vous pouvez ajuster la valeur du champ email dans la base de données. Vous pouvez pré-avertir l'utilisateur que l'adresse e-mail existe ou empêcher l'envoi, etc. Cependant, dans tous les cas, vous devriez toujours aussi vérifier le caractère unique après toi POST Le document. Puis réagissez au besoin. Du point de vue de l'interface utilisateur, les étapes ci-dessus ne seraient pas différentes du résultat obtenu à partir d'une base de données SQL * traditionnelle, avec une contrainte d'unicité.

Est-ce que quelque chose peut aller mal? Oui. Considère ceci. Assumer joe@example.org n'existe pas déjà dans la base de données. Deux documents arrivent presque en même temps pour être enregistrés dans la base de données. Doc A est POSTed, puis Doc B est POSTed mais avant que nous puissions vérifier le caractère unique de la Doc A  POST. Alors maintenant, quand nous faisons le contrôle de l'unicité pour le Doc A  POST, on voit ça joe@example.org est dans la base de données deux fois. Nous allons donc le supprimer et faire un rapport avec le problème. Mais disons avant que nous puissions supprimer Doc A, la vérification de l'unicité pour Doc B arrive également, obtenir la même valeur de compte pour joe@example.org. Maintenant les deux POSTs sera rejeté, même si joe@example.org était à l'origine en réalité pas dans la base de données! En d'autres termes, si deux documents avec une valeur correspondante entrent dans votre application presque en même temps, sa possible qu'ils pouvaient se voir POSTs et conclure à tort que la valeur qu'ils portent est déjà dans la base de données! On ne peut pas vraiment empêcher cela car il n'y a pas de style RDB traditionnel verrouillage dans CouchDB. Mais en échange de ce petit prix, nous obtenons une réplication maître-maître et une tonne d’autres trucs sympas. Je vais le prendre! Et si c'est un gros problème pour votre implémentation, vous pouvez essayer de le résoudre en implémentant un mécanisme de nouvelle tentative, etc.

Enfin, CouchDB est vraiment un petit bijou de base de données, mais ne vous contentez pas de prendre cela en compte et d’attendre qu’il s’agisse d’une approche à toute épreuve. Beaucoup va vraiment dépendre des détails de votre application spécifique.


21
2018-03-16 17:56



Ça dépend. Considérons le cas répliqué multi-maître, il pourrait y avoir des entrées en conflit ajoutées cohérentes au sein de chaque maître, mais non cohérentes une fois qu'elles sont répliquées. Vous n'utilisez peut-être qu'un seul serveur couchdb, mais en général, ils le conçoivent en supposant un cas multi-maître et ne mettent pas en place des fonctionnalités qui ne fonctionneraient correctement que sur un seul serveur non répliqué.

Si vous ne vous souciez que du cas du serveur unique, vous pouvez probablement reconstruire vos couchjs avec le support réseau et effectuer une requête http dans votre validate_doc_update()fonction qui effectuerait une requête sur la base de données pour voir si l'adresse e-mail est déjà utilisée et échouer la mise à jour si c'est le cas. Vérifier ici pour plus de détails sur le mécanisme de validation. Je ne recommande pas de le faire, au lieu de cela, j'intégrerais toute l'unicité dans le champ id (directement ou via le hachage) et je me contenterais de déplacer le document si l'utilisateur modifiait tout ce qui le concernait.


9
2017-10-18 14:16