web-dev-qa-db-fra.com

Association MongoDB Plusieurs à plusieurs

Comment feriez-vous une association plusieurs à plusieurs avec MongoDB?

Par exemple; Disons que vous avez une table Utilisateurs et une table Roles. Les utilisateurs ont de nombreux rôles et les rôles ont de nombreux utilisateurs. En langage SQL, vous créez une table UserRoles.

Users:
    Id
    Name

Roles:
    Id
    Name

UserRoles:
    UserId
    RoleId

Comment le même type de relation est-il traité dans MongoDB?

125
Josh Close

Selon vos besoins, vous pouvez tout mettre dans le document utilisateur:

{name:"Joe"
,roles:["Admin","User","Engineer"]
}

Pour obtenir tous les ingénieurs, utilisez:

db.things.find( { roles : "Engineer" } );

Si vous souhaitez conserver les rôles dans des documents distincts, vous pouvez inclure l'ID du document dans le tableau des rôles à la place du nom:

{name:"Joe"
,roles:["4b5783300334000000000aa9","5783300334000000000aa943","6c6793300334001000000006"]
}

et configurer les rôles comme:

{_id:"6c6793300334001000000006"
,rolename:"Engineer"
}
84
diederikh

Au lieu d’essayer de modéliser en fonction de nos années d’expérience avec les SGBDR, j’ai trouvé beaucoup plus facile de modéliser des solutions de référentiel de documents à l’aide de MongoDB, Redis et d’autres mémoires de données NoSQL, en optimisant les cas d’utilisation lus, tout en tenant compte des contraintes atomiques. opérations d'écriture devant être prises en charge par les cas d'utilisation d'écriture.

Par exemple, les utilisations du domaine "Utilisateurs dans les rôles" sont les suivantes:

  1. Rôle - Créer, Lire, Mettre à jour, Supprimer, Répertorier les utilisateurs, Ajouter un utilisateur, Supprimer un utilisateur, Effacer tous les utilisateurs, Index d'utilisateurs ou similaire pour prendre en charge "L'utilisateur est-il en rôle" (opérations comme un conteneur + ses propres métadonnées).
  2. Utilisateur - Créer, Lire, Mettre à jour, Supprimer (opérations CRUD comme une entité autonome)

Ceci peut être modélisé comme les modèles de document suivants:

User: { _id: UniqueId, name: string, roles: string[] }
    Indexes: unique: [ name ]
Role: { _id: UniqueId, name: string, users: string[] }
    Indexes: unique: [ name ]

Pour prendre en charge les utilisations à haute fréquence, telles que les fonctionnalités liées au rôle de l'entité User, User.Roles est intentionnellement dénormalisé, stocké sur l'utilisateur ainsi que Role.Users disposant d'un stockage dupliqué.

Si cela n’est pas évident dans le texte, c’est le type de réflexion qui est encouragé lors de l’utilisation de référentiels de documents.

J'espère que cela aidera à combler le fossé en ce qui concerne le côté lecture des opérations.

Du côté de l'écriture, ce qui est encouragé est de modéliser selon les écritures atomiques. Par exemple, si les structures de document nécessitent l'acquisition d'un verrou, la mise à jour d'un document, puis d'un autre et éventuellement de plusieurs documents, le relâchement du verrou entraîne probablement l'échec du modèle. Ce n’est pas parce que nous pouvons créer des verrous distribués que nous sommes censés les utiliser.

Dans le cas du modèle Utilisateur dans les rôles, les opérations qui étendent notre prévention de l'écriture atomique des verrous consiste à ajouter ou à supprimer un utilisateur d'un rôle. Dans les deux cas, une opération réussie entraîne la mise à jour d'un seul utilisateur et d'un seul document de rôle. Si quelque chose échoue, il est facile d'effectuer le nettoyage. C’est l’une des raisons pour lesquelles le modèle d’Unité de travail est assez fréquent lorsque des référentiels de documents sont utilisés.

L'opération qui étouffe réellement nos tentatives d'écriture atomique consiste à effacer un rôle, ce qui obligerait de nombreuses mises à jour utilisateur à supprimer le nom Role.name du tableau User.roles. Cette opération de clear est alors généralement déconseillée, mais si nécessaire peut être mise en œuvre en ordonnant les opérations:

  1. Obtenez la liste des noms d'utilisateur auprès de Role.users.
  2. Itérez les noms d'utilisateur de l'étape 1, supprimez le nom de rôle de User.roles.
  3. Effacer les rôles.

Dans le cas d'un problème susceptible de se produire à l'étape 2, il est facile de revenir en arrière, car le même ensemble de noms d'utilisateur de l'étape 1 peut être utilisé pour récupérer ou continuer.

29
paegun

Je suis tombé par hasard sur cette question et, bien qu’elle soit ancienne, j’ai pensé qu’il serait utile d’ajouter quelques possibilités qui ne sont pas mentionnées dans les réponses données. De plus, les choses ont un peu évolué au cours des dernières années. Il est donc intéressant de souligner que SQL et NoSQL se rapprochent.

L'un des commentateurs a évoqué l'attitude de prudence judicieuse selon laquelle "si les données sont relationnelles, utilisez-les". Cependant, ce commentaire n'a de sens que dans le monde relationnel, où les schémas viennent toujours avant l'application.

MONDE RELATIONNEL: Données de structure> Écrire une application pour l'obtenir
NOSQL WORLD: Application de conception> Structure des données en conséquence

Même si les données sont relationnelles, NoSQL reste une option. Par exemple, les relations un-à-plusieurs ne posent aucun problème et sont largement couvertes dans documents MongoDB

UNE SOLUTION 2015 À UN PROBLÈME DE 2010

Depuis que cette question a été postée, il y a eu de sérieuses tentatives pour rapprocher noSQL de SQL. L’équipe dirigée par Yannis Papakonstantinou de l’Université de Californie (San Diego) a travaillé sur FORWARD , une implémentation de SQL ++ qui pourrait bientôt être la solution au problème persistant. des problèmes comme celui posté ici.

À un niveau plus pratique, la sortie de Couchbase 4.0 signifie que, pour la première fois, vous pouvez créer des JOINs natifs dans NoSQL. Ils utilisent leur propre N1QL. Ceci est un exemple de JOIN de leur tutoriels :

SELECT usr.personal_details, orders 
        FROM users_with_orders usr 
            USE KEYS "Elinor_33313792" 
                JOIN orders_with_users orders 
                    ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END

N1QL permet la plupart des opérations SQL, voire toutes, y compris l'agrégation, le filtrage, etc.

LA NOUVELLE SOLUTION HYBRIDE

Si MongoDB est toujours la seule option, j'aimerais revenir à mon argument selon lequel l'application devrait avoir la priorité sur la structure des données. Aucune des réponses ne mentionne l’incorporation hybride, dans laquelle la plupart des données interrogées sont incorporées dans le document/objet, et les références sont conservées pour une minorité de cas.

Exemple: des informations (autres que le nom du rôle) peuvent-elles attendre? L'amorçage de l'application pourrait-il être plus rapide en ne demandant rien de ce dont l'utilisateur n'a pas encore besoin?

Cela pourrait être le cas si l'utilisateur se connecte et qu'il a besoin de voir toutes les options pour tous les rôles auxquels il appartient. Cependant, l'utilisateur est un "ingénieur" et les options pour ce rôle sont rarement utilisées. Cela signifie que l'application doit uniquement afficher les options pour un ingénieur s'il souhaite cliquer dessus.

Ceci peut être réalisé avec un document qui indique à l'application au début (1) les rôles auxquels l'utilisateur appartient et (2) où obtenir des informations sur un événement lié à un rôle particulier.

   {_id: ObjectID(),
    roles: [[“Engineer”, “ObjectId()”],
            [“Administrator”, “ObjectId()”]]
   }

Ou encore mieux, indexez le champ role.name dans la collection de rôles et vous n'aurez peut-être pas besoin d'incorporer ObjectID ().

Autre exemple: les informations sur TOUS les rôles demandés sont-elles TOUT le temps?

Il est également possible que l'utilisateur se connecte au tableau de bord et effectue 90% des tâches liées au rôle "Ingénieur". L'intégration hybride pourrait être réalisée intégralement pour ce rôle particulier et ne conserver que des références.

{_id: ObjectID(),
  roles: [{name: “Engineer”, 
           property1: value1,
           property2: value2
          },   
          [“Administrator”, “ObjectId()”]
         ]
}

Être sans schéma n'est pas seulement une caractéristique de NoSQL, cela pourrait être un avantage dans ce cas. Il est parfaitement valide d'imbriquer différents types d'objets dans la propriété "Roles" d'un objet utilisateur.

13
cortopy

dans le cas où l'employé et l'entreprise est entité-objet essayez d'utiliser le schéma suivant:

employee{
   //put your contract to employee
   contracts:{ item1, item2, item3,...}
}

company{
   //and duplicate it in company
   contracts:{ item1, item2, item3,...}
}
4