2012-08-13 3 views
3

problème d'interrogation difficile mongodb: J'ai une collection 'Comptes' avec un tas de documents qui (simplifié et avec des données réelles échangées pour non-monde réel valeurs) ressemblent à ceci:Requête Mongodb pour les documents qui ont des sous-objets avec une certaine valeur elem dans un tableau associatif

{"_id": "<Mongo Binary Data>", 
"Roles": { 
    "D7753879C7020F8ECF947122FA211413": { 
     "_id": "<Mongo Binary Data>", 
     "OrgName": "ACME", 
     "Rolename": "Coyote Liaison", 
    }, 

    "CFA7722E6799170706E4C5FFF3F01E63": { 
     "_id": "<Mongo Binary Data>", 
     "OrgName": "ACME",  
     "Rolename": "MembershipAdmin", 
    }, 

    "C7020F8ECF947122FAGIGHFVFF3F7753": { 
     "_id": "<Mongo Binary Data>", 
     "OrgName": "Initech",  
     "Rolename": "MembershipAdmin", 
    } 
    } 
} 

les clés du tableau de rôles sont une combinaison d'un identifiant de rôle et un identifiant org qui sont ensuite haché, ce qui le rend très rapide à interroger (une fois un objet de compte a été chargé de MongoDB et en C#) pour si un compte a un rôle donné pour une organisation donnée, c'est-à-dire, est un utilisateur MembershipAdmin pour Initech.

Maintenant, je veux interroger les utilisateurs qui ont un rôle pour ANY org, qui en pseudo-SQL pourrait être exprimé comme «SELECT tous les comptes qui ont au moins un objet de rôle où Rolename = THISROLENAME». C'est à dire, obtenir tous les utilisateurs qui sont MembershipAdmins.

J'ai essayé:

{ 
    Roles.Rolename: "MembershipAdmin" 
} 

et ce

{ 
    Roles: {"Rolename": "MembershipAdmin"} 
} 

et ce

{ 
    Roles: {"$elemMatch": {"Rolename": "MembershipAdmin"}} 
} 

... en vain et ont vu plusieurs réponses, etc., en disant que le seul moyen de sortir de cela est de pousser la clé de tableau associatif dans les sous-objets, ce que je ne veux pas faire car il fait le primaire fonction de cette structure de données (vérifier si un compte a un rôle donné pour une organisation donnée) très rapidement. Le cas d'utilisation que je décris ci-dessus ne doit pas être méga-rapide car il fait partie de la responsabilité d'un utilisateur admin, je suis donc heureux de les faire patienter quelques instants - donc les requêtes avec une récursivité excessive, etc. .

Est-ce que quelqu'un a des idées pour faire ce travail sans refactoriser la structure de données? À la fin de mon esprit avec ceci.

Un grand merci,

G

[EDIT: la structure ci-dessus est interrogeable pas, voir la réponse acceptée pour une explication érudite mais rapide à pourquoi et ce que vous devez bien faire pour y remédier . Si vous êtes OK avec une solution de contournement hacky, cependant, vous pouvez stocker une copie des données dans un BsonArray à côté et interroger à l'aide de $ elemMatch]

Répondre

4

Il n'y a aucun moyen de faire ce que vous voulez avec ce schéma, et le schéma n'est pas très pratique pour commencer.Vous devez modifier votre schéma à ceci:

{"_id": "<Mongo Binary Data>", 
"Roles": [ 
    { 
     "hash": "D7753879C7020F8ECF947122FA211413", 
     "_id": "<Mongo Binary Data>", 
     "OrgName": "ACME", 
     "Rolename": "Coyote Liaison", 
    }, 

    { 
     "hash": "CFA7722E6799170706E4C5FFF3F01E63", 
     "_id": "<Mongo Binary Data>", 
     "OrgName": "ACME",  
     "Rolename": "MembershipAdmin", 
    }, 

    { 
     "hash": "C7020F8ECF947122FAGIGHFVFF3F7753", 
     "_id": "<Mongo Binary Data>", 
     "OrgName": "Initech",  
     "Rolename": "MembershipAdmin", 
    } 
    ] 
} 

La raison pour laquelle l'utilisation de valeurs comme noms de non stable champ document est si peu pratique est exactement parce qu'elle complique le plus l'interrogation. Le seul avantage est que certaines requêtes peuvent être un peu plus rapides, mais étant donné que cela cause aussi des problèmes d'indexation, c'est presque toujours une mauvaise idée.

Je vous suggère fortement de changer votre schéma pour ce qui précède plutôt que de chercher des solutions qui permettent votre schéma actuel.

EDIT: Comme mentionné dans la discussion ci-dessous, il est techniquement possible de créer la requête nécessaire en utilisant l'opérateur $ where. Si vous ne pouvez pas le faire autrement, cela signifie que vous avez une odeur de schéma et un potentiel goulot d'étranglement et de performance qui sera diaboliquement difficile à résoudre lorsque votre logiciel sera mis en ligne. N'utilisez pas $ où. Déjà.

+0

J'avais peur que cette approche soit la seule réponse. Merci pour la clarification et le raisonnement derrière cela. – dartacus

+0

En tant qu'addendum, la structure dans la question est ce que vous finissez avec si vous utilisez un Dictionary dans votre classe C# sous-jacente pour gérer des données comme celle-ci. Cela a du sens au niveau de l'application, même si ce n'est pas une structure pratique quand elle devient un schéma mongo. – dartacus

+0

Pour l'enregistrement, en tant que solution provisoire, j'ai adapté la méthode save de ma classe de compte pour copier les données clés du dictionnaire des rôles et les stocker dans un BsonArray appelé MiniRoles. Vous pouvez ensuite l'interroger avec { MiniRoles: {$ elemMatch: {"Rolename": "MembershipAdmin"}} } Ce n'est pas une solution idéale, évidemment, mais va me dépanner jusqu'à ce que je refactor mes classes. – dartacus

-1

[EDIT]: Ceci est faux. Fonctionne s'il s'agissait d'un tableau et non d'une structure de hachage. Cela, de toute façon a déjà été essayé dans la question.

Roles.Rolename peut être utilisé s'il y a un index dessus.

db.collectionname.ensureIndex({"Roles.Rolename": 1 }) 

puis,

db.collectionname.find({"Role.Rolename": "MembershipAdmin"}) 
# returning only the organization field. Not tested though 
db.collectionname.find({"Role.Rolename": "MembershipAdmin"},{"Role.Orgname": 1}) 

Indexing Embedded fields de MongoDB docs a d'info sur elle.

+1

Incorrect. Sa structure Rôles n'est pas un tableau dû mais un document incorporé avec des valeurs de hachage comme clés d'entrée. –

+0

Mon mauvais. Merci pour la correction. A eu un problème similaire moi-même la veille et a raté les différences. – Kashyap

+0

Pas de problème, ça arrive;) –

-1

Voici comment vous le faites:

db.Accounts.find({ 
    $where: function() { 
     for (var index in this.Roles) 
      if (this.Roles[index].Rolename == "THISROLENAME") 
       return this; 
    } 
}); 

Note: Je ne sais pas pourquoi ils ont conçu des requêtes régulières de MongoDB pour ne pas fonctionner si chacun de vos documents intégrés dans le tableau a sa propre clé, mais nous espérons à l'avenir, ils vont résoudre ce problème.

En attendant, cette solution fonctionne très bien. Je l'ai testé et il semble assez rapide.

+1

À moins que vous ayez une très bonne raison de faire cela, vous devriez toujours, sans exception, choisir le refactoring de schémas sur l'utilisation de $ where vous pouvez faire des requêtes natives. $ où aurait dû être obsolète il y a longtemps. –

+0

Je l'ai testé et ça marche. Je comprends que MongoDB a la faiblesse de ne pas être en mesure de traiter un tableau de documents incorporés dans une structure comme '[key: {document}, key: {document}]' de la même façon qu'il traite de '[{document} , {document}] '... mais parfois, refactoriser le modèle de données n'est tout simplement pas une option. Je suppose que quelqu'un ne pose pas cette question à moins que cela ne soit déjà exclu en tant qu'option. – CommaToast

+0

Je voulais dire {{clé: {document}, clé: {document}} ', désolé. – CommaToast

Questions connexes