1

Gibt es eine Möglichkeit, ein heterogenes Array als Schema-Eigenschaft anzugeben, wo es sowohl ObjectIds als auch Strings enthalten kann? Ich möchte so etwas wie die folgenden haben:Mungo füllt entweder ObjectId-Referenz oder String

var GameSchema = new mongoose.schema({ 
    players: { 
     type: [<UserModel reference|IP address/socket ID/what have you>] 
    } 

ist die einzige Option eine Mixed Art, die ich selbst verwalten? Ich bin über discriminators gelaufen, die etwas vielversprechend aussehen, aber es sieht so aus, als ob es nur für Filialdokumente und nicht für Verweise auf andere Schemas funktioniert. Natürlich könnte ich einfach eine UserModel Referenz haben und eine UserModel erstellen, die nur die IP-Adresse oder was auch immer ich verwende, um sie zu identifizieren, aber das scheint, könnte es schnell außer Kontrolle geraten in Bezug auf Platz (mit einem Modell für jeden IP finde ich Geräusche schlecht).

EDIT:

Beispiel:

{ players: [ ObjectId("5fd88ea85...."), "192.0.0.1", "192.1.1.1", "192.2.2.1"] } 

Im Idealfall würde dies ausgefüllt werden:

Ein Spiel eine angemeldete Benutzer hat, drei anonyme Benutzer, das Dokument wie folgt aussehen sollte

{ players: [ UserModel(id: ..., name: ...), "192.0.0.1", "192.1.1.1", "192.2.2.1"] } 

EDIT:

Ich habe mich entschieden, eine andere Route zu gehen: anstatt Typen zu mischen, unterscheide ich mich mit anderen Eigenschaften. Etwas wie folgt aus:

players: [ 
    { 
     user: <object reference>, 
     sessionID: <string>, 
     color: { 
      type: String 
     }, 
     ...other properties... 
    } 
] 

Ich habe eine Prüfung die nur eine von user oder sessionID sind bevölkert für einen bestimmten Eintrag gewährleistet. In mancher Hinsicht ist dies komplexer, aber es ist nicht notwendig, diese Art des bedingten Auffüllens durchzuführen und herauszufinden, welcher Typ jeder Eintrag ist, wenn man über sie iteriert. Ich habe keine der Antworten ausprobiert, aber sie sehen vielversprechend aus.

+0

Wofür möchten Sie das verwenden? Das hat tatsächlich Bedeutung, da es davon abhängt, was Sie vorhaben. Da Sie "ObjectId" erwähnen, würde es vorschlagen, dass Sie auf ein Element in einer anderen/gleichen Sammlung verweisen möchten. Wenn Sie Typen mischen, funktionieren Dinge wie '.populate()' nicht. –

+0

Mein Ziel ist es, entweder eingeloggten Benutzern oder anonymen Benutzern ein Spiel beizutreten. Angemeldete Benutzer sollten das Spiel mit ihrem Login verknüpft haben, anonyme Benutzer (offensichtlich) haben kein Login. 'populate()' ist eines der Dinge, über die ich mir nicht sicher war; Wenn mein Anwendungsfall von Mongoose unterstützt würde, dann gäbe es vermutlich einen Weg, um nur die Einträge im Array, die ObjectIds sind, intelligent zu "bevölkern", aber du sagst, dass das nicht passieren wird? –

+0

Ja, ich sage, das wird nicht passieren. Anstatt zu versuchen, in Kommentaren zu erklären, nehmen Sie sich die Zeit und erweitern Sie Ihren Anwendungsfall (vorzugsweise mit Beispieldaten und gewünschten Ergebnissen) in Ihrer Frage. Ein guter Weg zu einer Frage ist es, sie nicht in Bezug darauf zu formulieren, was * "Sie denken, die Lösung ist" *, sondern einfach das Problem im Hinblick auf das gewünschte Ergebnis zu erklären. Diese Punkte könnten klarer sein, um die besten Ratschläge zu geben, –

Antwort

0

Wenn Sie zufrieden sind mit zu gehen Mixed mit oder zumindest einige Schemata, das mit .populate() nicht funktionieren, dann können Sie verschieben die „Join“ Verantwortung gegenüber dem „Server“ statt mit der $lookup Funktionalität von MongoDB und eine wenig Phantasie Matching .

Für mich, wenn ich eine "games" Sammlung Dokument wie dieses:

{ 
     "_id" : ObjectId("5933723c886d193061b99459"), 
     "players" : [ 
       ObjectId("5933723c886d193061b99458"), 
       "10.1.1.1", 
       "10.1.1.2" 
     ], 
     "__v" : 0 
} 

Dann habe ich die Anweisung an den Server zu „verbinden“ mit den "users" Sammlungsdaten senden, wo ein ObjectId vorhanden ist wie folgt:

Game.aggregate([ 
    { "$addFields": { 
    "users": { 
     "$filter": { 
     "input": "$players", 
     "as": "p", 
     "cond": { "$gt": [ "$$p", {} ] } 
     } 
    } 
    }}, 
    { "$lookup": { 
    "from": "users", 
    "localField": "users", 
    "foreignField": "_id", 
    "as": "users" 
    }}, 
    { "$project": { 
    "players": { 
     "$map": { 
     "input": "$players", 
     "as": "p", 
     "in": { 
      "$cond": { 
      "if": { "$gt": [ "$$p", {} ] }, 
      "then": { 
       "$arrayElemAt": [ 
       { "$filter": { 
        "input": "$users", 
        "as": "u", 
        "cond": { "$eq": [ "$$u._id", "$$p" ] } 
       }}, 
       0 
       ] 
      }, 
      "else": "$$p" 
      } 
     } 
     } 
    } 
    }} 
]) 

die das Ergebnis gibt, wenn der Benutzer verbunden Objekt als:

{ 
     "_id" : ObjectId("5933723c886d193061b99459"), 
     "players" : [ 
       { 
         "_id" : ObjectId("5933723c886d193061b99458"), 
         "name" : "Bill", 
         "__v" : 0 
       }, 
       "10.1.1.1", 
       "10.1.1.2" 
     ] 
} 

So ist das „fancy“ Teil verlässt sich wirklich auf diese logische Anweisung, wenn die Einträge in der "players" Array Berücksichtigung

"$filter": { 
    "input": "$players", 
    "as": "p", 
    "cond": { "$gt": [ "$$p", {} ] } 
    } 

Wie dies funktioniert, ist, dass zu MongoDB, ein ObjectId und tatsächlich alle BSON Typen ein specific sort precedence haben.In diesem Fall, in dem die Daten „gemischte“ zwischen ObjectIdString und dann wird die „string“ Werte werden als „weniger als“ der Wert eines „BSON Object“, und die Werte sind ObjectId „größer“ ist.

Auf diese Weise können Sie die ObjectId Werte aus dem Quellfeld in ihre eigene Liste trennen. Angesichts dieser Liste, Sie $lookup, führen Sie die "Join" bei Abrufen der Objekte aus der anderen Sammlung.

Um sie zurück zu setzen, verwende ich $map, um jedes Element der ursprünglichen "players" zu "transponieren", wo die übereinstimmende ObjectId mit dem verwandten Objekt gefunden wurde. Ein alternativer Ansatz wäre die beiden Typen zu „Split“ sein, führen Sie die $lookup und $concatArrays zwischen den Users und den „Strings“. Aber das würde nicht die ursprüngliche Array-Reihenfolge beibehalten, so $map kann eine bessere Passform sein.


I Zettel wird, dass dasselbe kann grundlegende Prozess durch ähnliches Filtern des Inhalts des "players" Array in einem „Client“ Betrieb angewendet werden, nur die ObjectId Werte enthalten, und dann den Aufruf der „Modell“ Form von .populate() von "innen" die Antwort der ersten Abfrage. Die Dokumentation zeigt ein Beispiel für diese Form der Verwendung, ebenso wie einige Antworten auf dieser Seite, bevor es möglich war, ein "verschachteltes Populate" mit Mungo zu machen.

Der andere Punkt des Geistes ist hier, dass .populate() sich als Mungo Methode lange existierte, bevor die $lookup Aggregation Pipeline-Betreiber zustande kam, und war eine Lösung für eine Zeit, in MongoDB selbst unfähig war, eine „join“ jeglicher Art der Durchführung . Die Operationen sind also tatsächlich "Client" -Seite als eine Emulation und führen wirklich nur zusätzliche Abfragen durch, die Sie beim Ausgeben der Anweisungen selbst nicht beachten müssen.

daher in einem modernen Szenario im Allgemeinen wünschenswert sein, die „Server“ Funktionen zu benutzen, es sollte und vermeidet den Aufwand mit mehreren Abfragen beteiligt ist, um das Ergebnis zu erhalten.