为了账号安全,请及时绑定邮箱和手机立即绑定

如何在MongoDB中查询引用的对象?

/ 猿问

如何在MongoDB中查询引用的对象?

幕布斯6054654 2019-10-11 14:35:46

我的Mongo数据库中有两个集合,并且Foo包含对一个或多个Bars的引用:


Foo: { 

  prop1: true,

  prop2: true,

  bars: [

     {

     "$ref": "Bar",

     "$id": ObjectId("blahblahblah")

     }

  ]

}


Bar: {

   testprop: true

}

我想要的是找到所有Foo至少Bar具有一个testprop设置为true的。我已经尝试过此命令,但不会返回任何结果:


db.Foo.find({ "bars.testprop" : { "$in": [ true ] } })

有任何想法吗?


查看完整描述

3 回答

?
慕的地10843

您现在可以在Mongo 3.2中使用 $lookup


$lookup 接受四个论点


from:在同一数据库中指定要执行连接的集合。from集合无法分片。


localField:指定从文档输入到$ lookup阶段的字段。$ lookup在from集合的文档中对localField和foreignField执行相等的匹配。


foreignField:指定from集合中文档中的字段。


as:指定要添加到输入文档中的新数组字段的名称。新数组字段包含from集合中的匹配文档。


db.Foo.aggregate(

  {$unwind: "$bars"},

  {$lookup: {

    from:"bar",

    localField: "bars",

    foreignField: "_id",

    as: "bar"


   }},

   {$match: {

    "bar.testprop": true

   }}

)


查看完整回答
反对 回复 2019-10-11
?
慕雪6442864

你不能 参见http://www.mongodb.org/display/DOCS/Database+参考


您必须在客户端中执行此操作。


查看完整回答
反对 回复 2019-10-11
?
沧海一幻觉

我们遇到了类似的问题,因为我们将MongoDB(3.4.4,实际上是3.5.5用于测试)与Morphia结合使用@Referenece,在两个实体上使用了Morphia 。尽管我们对这种解决方案不满意,但正在考虑删除这些声明,而是手动进行参考查找。


即我们有一个公司集合和一个用户集合。Morphia中的用户实体包含@Refrence有关公司实体的声明。相应的公司集合包含以下条目:


/* 1 */

{

    "_id" : ObjectId("59a92501df01110fbb6a5dee"),

    "name" : "Test",

    "gln" : "1234567890123",

    "uuid" : "f1f86961-e8d5-40bb-9d3f-fdbcf549066e",

    "creationDate" : ISODate("2017-09-01T09:14:41.551Z"),

    "lastChange" : ISODate("2017-09-01T09:14:41.551Z"),

    "version" : NumberLong(1),

    "disabled" : false

}


/* 2 */

{

    "_id" : ObjectId("59a92501df01110fbb6a5def"),

    "name" : "Sample",

    "gln" : "3210987654321",

    "uuid" : "fee69ee4-b29c-483b-b40d-e702b50b0451",

    "creationDate" : ISODate("2017-09-01T09:14:41.562Z"),

    "lastChange" : ISODate("2017-09-01T09:14:41.562Z"),

    "version" : NumberLong(1),

    "disabled" : false

}

而用户集合包含以下条目:


/* 1 */

{

    "_id" : ObjectId("59a92501df01110fbb6a5df0"),

    "userId" : "admin",

    "userKeyEncrypted" : {

        "salt" : "78e0528db239fd86",

        "encryptedAttribute" : "e4543ddac7cca9757721379e4e70567bb13956694f473b73f7723ac2e2fc5245"

    },

    "passwordHash" : "$2a$10$STRNORu9rcbq4qYUMld4G.HJk8QQQQBmAswSNC/4PBn2bih0BvjM6",

    "roles" : [ 

        "ADMIN"

    ],

    "company" : {

        "$ref" : "company",

        "$id" : ObjectId("59a92501df01110fbb6a5dee")

    },

    "uuid" : "b8aafdcf-d5c4-4040-a96d-8ab1a8608af8",

    "creationDate" : ISODate("2017-09-01T09:14:41.673Z"),

    "lastChange" : ISODate("2017-09-01T09:14:41.765Z"),

    "version" : NumberLong(1),

    "disabled" : false

}


/* 2 */

{

    "_id" : ObjectId("59a92501df01110fbb6a5df1"),

    "userId" : "sample",

    "userKeyEncrypted" : {

        "salt" : "e3ac48695dea5f51",

        "encryptedAttribute" : "e804758b0fd13c219c3fc383eaa9267b70f7b8a1ed74f05575add713ce11804a"

    },

    "passwordHash" : "$2a$10$Gt2dq1vy4J9MeqDnXjokAOtvFcvbhe/g9wAENXFPaPxLAw1L4EULG",

    "roles" : [ 

        "USER"

    ],

    "company" : {

        "$ref" : "company",

        "$id" : ObjectId("59a92501df01110fbb6a5def")

    },

    "uuid" : "55b62d4c-e5ee-408d-80c0-b79e02085b02",

    "creationDate" : ISODate("2017-09-01T09:14:41.873Z"),

    "lastChange" : ISODate("2017-09-01T09:14:41.878Z"),

    "version" : NumberLong(1),

    "disabled" : false

}


/* 3 */

{

    "_id" : ObjectId("59a92501df01110fbb6a5df2"),

    "userId" : "user",

    "userKeyEncrypted" : {

        "salt" : "ab9df671340a7d8b",

        "encryptedAttribute" : "7d8ad4ca6ad88686d810c70498407032f1df830596f72d931880483874d9cce3"

    },

    "passwordHash" : "$2a$10$0FLFw3ixW79JIBrD82Ly6ebOwnEDliS.e7GmrNkFp2nkWDA9OE/RC",

    "uuid" : "d02aef94-fc3c-4539-a22e-e43b8cd78aaf",

    "creationDate" : ISODate("2017-09-01T09:14:41.991Z"),

    "lastChange" : ISODate("2017-09-01T09:14:41.995Z"),

    "version" : NumberLong(1),

    "disabled" : false

}

为了创建特殊的公司用户视图,我们还希望在用户中取消对公司的引用,并且仅包括选定的字段。根据错误报告中的评论,我们了解到MongoDB提供了一种$objectToArray: "$$ROOT.element"将给定元素的字段基本上划分为键和值对的操作。请注意,$objectToArray操作已在MongoDB 3.4.4版中添加!


使用该$objectToArray操作对用户集合中包含的company元素进行的汇总如下所示:


dp.user.aggregate([{ 

    $project: { 

        "userId": 1, 

        "userKeyEncrypted": 1, 

        "uuid":1, 

        "roles": 1, 

        "passwordHash": 1, 

        "disabled": 1, 

        company: { $objectToArray: "$$ROOT.company" }

    } 

}])

以上聚合的结果如下所示:


/* 1 */

{

    "_id" : ObjectId("59a92501df01110fbb6a5df0"),

    "userId" : "admin",

    "userKeyEncrypted" : {

        "salt" : "78e0528db239fd86",

        "encryptedAttribute" : "e4543ddac7cca9757721379e4e70567bb13956694f473b73f7723ac2e2fc5245"

    },

    "passwordHash" : "$2a$10$STRNORu9rcbq4qYUMld4G.HJk8QQQQBmAswSNC/4PBn2bih0BvjM6",

    "roles" : [ 

        "ADMIN"

    ],

    "uuid" : "b8aafdcf-d5c4-4040-a96d-8ab1a8608af8",

    "disabled" : false,

    "company" : [ 

        {

            "k" : "$ref",

            "v" : "company"

        }, 

        {

            "k" : "$id",

            "v" : ObjectId("59a92501df01110fbb6a5dee")

        }

    ]

}


/* 2 */

{

    "_id" : ObjectId("59a92501df01110fbb6a5df1"),

    "userId" : "sample",

    "userKeyEncrypted" : {

        "salt" : "e3ac48695dea5f51",

        "encryptedAttribute" : "e804758b0fd13c219c3fc383eaa9267b70f7b8a1ed74f05575add713ce11804a"

    },

    "passwordHash" : "$2a$10$Gt2dq1vy4J9MeqDnXjokAOtvFcvbhe/g9wAENXFPaPxLAw1L4EULG",

    "roles" : [ 

        "USER"

    ],

    "uuid" : "55b62d4c-e5ee-408d-80c0-b79e02085b02",

    "disabled" : false,

    "company" : [ 

        {

            "k" : "$ref",

            "v" : "company"

        }, 

        {

            "k" : "$id",

            "v" : ObjectId("59a92501df01110fbb6a5def")

        }

    ]

}


/* 3 */

{

    "_id" : ObjectId("59a92501df01110fbb6a5df2"),

    "userId" : "user",

    "userKeyEncrypted" : {

        "salt" : "ab9df671340a7d8b",

        "encryptedAttribute" : "7d8ad4ca6ad88686d810c70498407032f1df830596f72d931880483874d9cce3"

    },

    "passwordHash" : "$2a$10$0FLFw3ixW79JIBrD82Ly6ebOwnEDliS.e7GmrNkFp2nkWDA9OE/RC",

    "uuid" : "d02aef94-fc3c-4539-a22e-e43b8cd78aaf",

    "disabled" : false,

    "company" : null

}

现在,只需过滤不需要的内容(即没有分配公司的用户并选择正确的数组条目),以便提供$lookup@sidgate已经解释的操作并将被取消引用的公司的值复制到用户响应中。


也就是说,像下面这样的聚合将执行联接,并将公司的数据添加到将公司分配为as在查找中定义的值的用户:


db.user.aggregate([

    { $project: { "userId": 1, "userKeyEncrypted": 1, "uuid":1, "roles": 1, "passwordHash": 1, "disabled": 1, company: { $objectToArray: "$$ROOT.company" }} }, 

    { $unwind: "$company" }, 

    { $match: { "company.k": "$id"}  }, 

    { $lookup: { from: "company", localField: "company.v", foreignField: "_id", as: "company_data" } }

])

以上汇总的结果如下所示:


/* 1 */

{

    "_id" : ObjectId("59a92501df01110fbb6a5df0"),

    "userId" : "admin",

    "userKeyEncrypted" : {

        "salt" : "78e0528db239fd86",

        "encryptedAttribute" : "e4543ddac7cca9757721379e4e70567bb13956694f473b73f7723ac2e2fc5245"

    },

    "passwordHash" : "$2a$10$STRNORu9rcbq4qYUMld4G.HJk8QQQQBmAswSNC/4PBn2bih0BvjM6",

    "roles" : [ 

        "ADMIN"

    ],

    "uuid" : "b8aafdcf-d5c4-4040-a96d-8ab1a8608af8",

    "disabled" : false,

    "company" : {

        "k" : "$id",

        "v" : ObjectId("59a92501df01110fbb6a5dee")

    },

    "company_data" : [ 

        {

            "_id" : ObjectId("59a92501df01110fbb6a5dee"),

            "name" : "Test",

            "gln" : "1234567890123",

            "uuid" : "f1f86961-e8d5-40bb-9d3f-fdbcf549066e",

            "creationDate" : ISODate("2017-09-01T09:14:41.551Z"),

            "lastChange" : ISODate("2017-09-01T09:14:41.551Z"),

            "version" : NumberLong(1),

            "disabled" : false

        }

    ]

}


/* 2 */

{

    "_id" : ObjectId("59a92501df01110fbb6a5df1"),

    "userId" : "sample",

    "userKeyEncrypted" : {

        "salt" : "e3ac48695dea5f51",

        "encryptedAttribute" : "e804758b0fd13c219c3fc383eaa9267b70f7b8a1ed74f05575add713ce11804a"

    },

    "passwordHash" : "$2a$10$Gt2dq1vy4J9MeqDnXjokAOtvFcvbhe/g9wAENXFPaPxLAw1L4EULG",

    "roles" : [ 

        "USER"

    ],

    "uuid" : "55b62d4c-e5ee-408d-80c0-b79e02085b02",

    "disabled" : false,

    "company" : {

        "k" : "$id",

        "v" : ObjectId("59a92501df01110fbb6a5def")

    },

    "company_data" : [ 

        {

            "_id" : ObjectId("59a92501df01110fbb6a5def"),

            "name" : "Sample",

            "gln" : "3210987654321",

            "uuid" : "fee69ee4-b29c-483b-b40d-e702b50b0451",

            "creationDate" : ISODate("2017-09-01T09:14:41.562Z"),

            "lastChange" : ISODate("2017-09-01T09:14:41.562Z"),

            "version" : NumberLong(1),

            "disabled" : false

        }

    ]

}

可以看到,我们只有两个包含公司参考的用户,并且两个用户现在在响应中也具有完整的公司数据。现在,可以应用其他过滤来摆脱键/值助手,并隐藏不需要的数据。


我们提出的最终查询如下所示:


db.user.aggregate([

    { $project: { "userId": 1, "userKeyEncrypted": 1, "uuid":1, "roles": 1, "passwordHash": 1, "disabled": 1, company: { $objectToArray: "$$ROOT.company" }} }, 

    { $unwind: "$company" }, 

    { $match: { "company.k": "$id"}  }, 

    { $lookup: { from: "company", localField: "company.v", foreignField: "_id", as: "company_data" } },

    { $project: { "userId": 1, "userKeyEncrypted": 1, "uuid":1, "roles": 1, "passwordHash": 1, "disabled": 1,  "companyUuid": { $arrayElemAt: [ "$company_data.uuid", 0 ] } } }

])

最终返回我们想要的表示形式:


/* 1 */

{

    "_id" : ObjectId("59a92501df01110fbb6a5df0"),

    "userId" : "admin",

    "userKeyEncrypted" : {

        "salt" : "78e0528db239fd86",

        "encryptedAttribute" : "e4543ddac7cca9757721379e4e70567bb13956694f473b73f7723ac2e2fc5245"

    },

    "passwordHash" : "$2a$10$STRNORu9rcbq4qYUMld4G.HJk8QQQQBmAswSNC/4PBn2bih0BvjM6",

    "roles" : [ 

        "ADMIN"

    ],

    "uuid" : "b8aafdcf-d5c4-4040-a96d-8ab1a8608af8",

    "disabled" : false,

    "companyUuid" : "f1f86961-e8d5-40bb-9d3f-fdbcf549066e"

}


/* 2 */

{

    "_id" : ObjectId("59a92501df01110fbb6a5df1"),

    "userId" : "sample",

    "userKeyEncrypted" : {

        "salt" : "e3ac48695dea5f51",

        "encryptedAttribute" : "e804758b0fd13c219c3fc383eaa9267b70f7b8a1ed74f05575add713ce11804a"

    },

    "passwordHash" : "$2a$10$Gt2dq1vy4J9MeqDnXjokAOtvFcvbhe/g9wAENXFPaPxLAw1L4EULG",

    "roles" : [ 

        "USER"

    ],

    "uuid" : "55b62d4c-e5ee-408d-80c0-b79e02085b02",

    "disabled" : false,

    "companyUuid" : "fee69ee4-b29c-483b-b40d-e702b50b0451"

}

关于这种方法的最后一点注意:可悲的是,这种聚合不是很快,但是至少可以完成工作。我没有像最初要求的那样使用一系列引用来测试它,尽管这可能需要一些其他的解决方法。


更新:汇总数据的另一种方式与上述错误报告中的注释更加一致,如下所示:


db.user.aggregate([

    { $project: { "userId": 1, "userKeyEncrypted": 1, "uuid":1, "roles": 1, "passwordHash": 1, "disabled": 1, companyRefs: { $let: { vars: { refParts: { $objectToArray: "$$ROOT.company" }}, in: "$$refParts.v" } } } },

    { $match: { "companyRefs": { $exists: true } } },

    { $project: { "userId": 1, "userKeyEncrypted": 1, "uuid":1, "roles": 1, "passwordHash": 1, "disabled": 1, "companyRef": { $arrayElemAt: [ "$companyRefs", 1 ] } } },

    { $lookup: { from: "company", localField: "companyRef", foreignField: "_id", as: "company_data" } },

    { $project: { "userId": 1, "userKeyEncrypted": 1, "uuid":1, "roles": 1, "passwordHash": 1, "disabled": 1,  "companyUuid": { $arrayElemAt: [ "$company_data.uuid", 0 ] } } }

])

在此,$let: { vars: ..., in: ... }操作会将引用的键和值复制到自己的对象中,从而允许以后通过相应的操作查找引用。


这些聚合中哪一个的性能更好,尚待分析。


查看完整回答
反对 回复 2019-10-11

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信