1

有没有办法指定一个异构数组作为模式属性,它可以同时包含ObjectId和字符串?我想有像下面这样:Mongoose填充ObjectId引用或字符串

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

是唯一的选择一个Mixed类型,我操控着自己?我已经穿过discriminators,看起来有点有前途,但它看起来只适用于子文档而不适用于其他模式的引用。当然,我也只是有一个UserModel参考,并创建一个UserModel,只是存储的IP地址或任何我使用来识别它们,但似乎是它可以迅速获得巨大的失控在空间方面(有一个模型对于我遇到的每个IP都听起来很糟糕)。

编辑:

例子:

的游戏已经登录的用户一,三个匿名用户,文件应该是这个样子:

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

理想的情况下,这将被填充到:

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

编辑:

我决定走了不同的路线:不是混合类型,我与不同的性质区分。事情是这样的:

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

我必须确保只有一个usersessionID会填充一个给定的条目的验证。在某些方面,这更加复杂,但它确实可以避免做这种有条件填充的需要,并且在迭代它们时确定每个条目的类型。我没有尝试过任何答案,但他们看起来很有希望。

+0

你想用它做什么?这实际上很重要,因为它取决于你打算做什么。既然你提到了'ObjectId',那么它会建议你想引用另一个/同一个集合中的一个项目。如果你混合使用类型,那么像'.populate()'这样的东西将不起作用。 –

+0

我的目标是允许登录用户或匿名用户加入游戏。登录用户应该将游戏与其登录关联起来,匿名用户(显然)没有登录。 'populate()'是我不确定的事情之一;如果我的用例被Mongoose支持,那么大概会有一种方法可以巧妙地“填充”数组中ObjectId中的那些条目,但是你说这不会发生? –

+0

是的,我说这不会发生。不要试图在评论中解释,花点时间在你的问题上扩展你的用例(最好是用示例数据和期望的结果)。解决问题的一个好办法是**不要**用“你认为解决方案是什么”*来表述它,而是用你想要的最终结果来解释问题。为了给出最好的建议,这些要点可能会更清晰, –

回答

0

如果你的内容去使用Mixed或至少部分不会与.populate()工作方案,那么你可以转移“加入”为己任,以“服务器”,而不是使用的MongoDB的$lookup功能和一点点花哨匹配。

对于我来说,如果我有一个"games"集合文件是这样的:

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

然后我发声明到服务器“加入”与"users"收集数据,其中一个ObjectId是现在的这个样子:

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" 
      } 
     } 
     } 
    } 
    }} 
]) 

当连接到用户这给结果对象为:

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

所以“花哨”的一部分考虑"players"数组中的条目时,真正依靠这种逻辑语句:

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

该如何工作是到MongoDB的,一个ObjectId,实际上所有BSON类型都有一个specific sort precedence。在ObjectIdString之间的数据是“混合”的情况下,那么“串”值被认为是“小于”“BSON对象”的值,并且ObjectId值是“大于”。

这使您可以将源数组中的ObjectId值分隔到自己的列表中。鉴于该列表,您在$lookup执行“加入”从其他集合中获取对象。

为了将它们放回去,我使用$map来将原始"players"中的每个元素“转置”,其中找到匹配的ObjectId与相关对象。另一种方法是“拆分”这两种类型,在Users和“字符串”之间执行$lookup$concatArrays。但是这不会保持原始数组的顺序,所以$map可能更适合。


我将由"players"阵列的内容类似地滤波以仅包含所述ObjectId值,然后调用“模型”形式的补充说明的是,相同的基本方法可以在一个“客户端”的操作被施加.populate()来自“内部”初始查询的响应。该文档显示了这种使用形式的一个例子,就像在本网站上做了一些回答,然后才能用猫鼬做一个“嵌套填充”。

另一个关键点在于.populate()本身就是在聚合管道操作员出现之前很早就存在的一种猫鼬方法,并且是MongoDB本身无法执行任何类型的“连接”的解决方案。所以这些操作确实是“客户”方面的模拟,并且实际上只执行额外的查询,而您在自己发布报表时不需要知道这些查询。

因此,在现代场景中通常应该使用“服务器”功能,并避免多个查询所涉及的开销以获得结果。