mongoose get relative data without query - javascript

I have this two schema, fruits schema and user schema. I split them in 2 different collection, but a fruit schema can have a reference to a user.
const FruitSchema = new Schema({
title: {
type: String
},
buyer: {
_id: {
type: Schema.Types.ObjectId,
default: null
}
}
})
const UserSchema = new Schema({
name: {
type: String
},
age: {
type: Number
},
gender: {
type: String
}
})
How would you query all fruits with the user info? What I can think of is find all fruits, get the buyer._id then find each user, map them back the fruit array, sounds so tedious and complicated. If it's mysql I just JOIN.

You need to use $lookup to join buyer id with user id, for all matching collections, it will return the user info as an embedded document
db.fruitz.aggregate(
[
{
$lookup : {
from : "userz",
localField : "buyer_id",
foreignField: "_id",
as : "buyerInfo"
}
}
]
)
fruits collection
> db.fruitz.find().pretty()
{ "_id" : 2, "title" : "apple", "buyer_id" : 1 }
{ "_id" : 1, "title" : "banana", "buyer_id" : 2 }
users collection
> db.userz.find().pretty()
{ "_id" : 1, "name" : "abc", "age" : 20, "gender" : "M" }
>
$lookup aggregate
> db.fruitz.aggregate( [ { $lookup : { from : "userz", localField : "buyer_id", foreignField: "_id", as : "buyerInfo" } } ] ).pretty()
output
{
"_id" : 2,
"title" : "apple",
"buyer_id" : 1,
"buyerInfo" : [
{
"_id" : 1,
"name" : "abc",
"age" : 20,
"gender" : "M"
}
]
}
{ "_id" : 1, "title" : "banana", "buyer_id" : 2, "buyerInfo" : [ ] }
>

MongoDB Provides $lookup to join records from two collections:
In your case you can join fruits and user using $lookup:
db.fruits.aggregate([
{
$lookup:
{
from: "fruits",
localField: "buyer",
foreignField: "_id",
as: "buyer_info"
}
}
])
$lookup hat got more powerful in 3.6(if you're using 3.6) which allows adding expressions and on the things you're joining from right collection

Related

Filter data using mongoose populate

I have two data structures "database" and "components"
const DatabaseSchema = mongoose.Schema({
components: [{ type: Schema.Types.ObjectId, ref: 'Components', required: false }],
});
const ComponentsSchema = mongoose.Schema({
name: { type: String, required: true, trim: true, unique: true, lowercase: true },
updatedAt: Date,
});
I want to filter all items in the database by component names
search rule I'm using
Database.find({
components: { $elemMatch: { name: /antr/i } }
}).populate({
path: 'components',
select: 'name -_id'
}).select(['descript','components']).exec( (err,data) => {
console.log(err);
res.json(data);
});
however always return an empty element
Please try this :
As I've already suggested you can use this :
Database.find({})
.populate({ path: 'components', match: { name: /antr/i }, select: 'name -_id' })
.exec((err, data) => { console.log(err); res.json(data); });
Since you're seeing empty array's is because of the filter query in match which doesn't find appropriate documents in components collection w.r.t. ObjectIds in components array of database document, this is normal. May be you can filter those out in code, as you aren't looking in that way, You can use mongoDB's $lookup from aggregation framework which is equivalent to .populate() from mongoose.
Database.aggregate(
[{
$lookup: {
from: "components",
"let": { "ids": "$components" },
pipeline: [
{ $match: { $expr: { $in: ['$_id', '$$ids'] } } }],
as: "dbComponentsArray"
}
}, { $unwind: '$dbComponentsArray' }, { $match: { 'dbComponentsArray.name': /antr/i } },
{ $group: { _id: '$_id', dbComponentsArray: { $push: '$dbComponentsArray' }, data: { $first: '$$ROOT' } } }, { $addFields: { 'data.dbComponentsArray': '$dbComponentsArray' } },
{ $replaceRoot: { 'newRoot': '$data' } }])
Sample Data in collections :
components :
/* 1 */
{
"_id" : ObjectId("5d481cd098ba991c0857959f"),
"name" : "antracito",
"updatedAt" : ISODate("2019-08-05T12:10:56.777Z"),
"__v" : 0
}
/* 2 */
{
"_id" : ObjectId("5d481cd098ba991c0857958f"),
"name" : "anacito",
"updatedAt" : ISODate("2019-08-05T12:10:56.777Z"),
"__v" : 0
}
/* 3 */
{
"_id" : ObjectId("5d481cd098ba991c0857951f"),
"name" : "antracito",
"updatedAt" : ISODate("2019-08-05T12:10:56.777Z"),
"__v" : 0
}
/* 4 */
{
"_id" : ObjectId("5d481cd098ba991c0857952f"),
"name" : "anacito",
"updatedAt" : ISODate("2019-08-05T12:10:56.777Z"),
"__v" : 0
}
database :
/* 1 */
{
"_id" : ObjectId("5d4979d52a17d10a6c8de81b"),
"components" : [
ObjectId("5d481cd098ba991c0857951f"),
ObjectId("5d481cd098ba991c0857952f"),
ObjectId("5d481cd098ba991c0857953f"),
ObjectId("5d481cd098ba991c0857959f")
]
}
Output :
/* 1 */
{
"_id" : ObjectId("5d4979d52a17d10a6c8de81b"),
"components" : [
ObjectId("5d481cd098ba991c0857951f"),
ObjectId("5d481cd098ba991c0857952f"),
ObjectId("5d481cd098ba991c0857953f"),
ObjectId("5d481cd098ba991c0857959f")
],
"dbComponentsArray" : [
{
"_id" : ObjectId("5d481cd098ba991c0857959f"),
"name" : "antracito",
"updatedAt" : ISODate("2019-08-05T12:10:56.777Z"),
"__v" : 0
},
{
"_id" : ObjectId("5d481cd098ba991c0857951f"),
"name" : "antracito",
"updatedAt" : ISODate("2019-08-05T12:10:56.777Z"),
"__v" : 0
}
]
}

Can i filter one collection that has _id in other collection and have result on in mongodb

I have the collection "A" like this
{
"_id" : ObjectId("5c4c1f2a5173562961468b30"),
"name" : "first"
},
{
"_id" : ObjectId("57c162f267045ec439ba2485"),
"name" : "second"
}
Like above i have multiple doc in a collection,
Now i have colection "B" like below which has _id of Collections a's doc
{
"_id" : ObjectId("57d14ad36495a197593ab7ab"),
"a_id" : ObjectId("5c4c1f2a5173562961468b30"),
"name" : "ab"
},
{
"_id" : ObjectId("57d941503dd051cc04205e1e"),
"a_id" : ObjectId("5c4c1f2a5173562961468b30"),
"name" : "cd "
}
Now i need a query that will give me result collection "A" based on how many documents are associated with "B"
If "A" has 2 documents in "B" I need those 2 "B" documents
This simple aggregation make your result
db.A.aggregate([
{
"$lookup": {
"from": "B",
"localField": "_id",
"foreignField": "a_id",
"as": "docuentInB"
}
},
{
$match: {
$expr: { $gte: [{ $size: "$docuentInB" }, 2 ] }
}
},
{
$project: {
docuentInB: 0
}
}
])

How to delete the field in the mongodb collection?

I have a data collection in mongodb like below:
{
{ "_id" : ObjectId("1"), "name" : "ABC", "group" : [ObjectId("11"), ObjectId("12"), ObjectId("13")]}
{ "_id" : ObjectId("2"), "name" : "DEF", "group" : [ObjectId("21"), ObjectId("22"), ObjectId("23")]}
}
I want to delete ObjectId("11") in the group field in document
ObjectId("1").
I tried the code below:
aId = "1"
bId = "11"
db.collection.updateOne({ _id: ObjectId(aId) }, { $pull: { group: { _id: ObjectId(bId) } } })
But failed.
I have also tried:
aId = "1"
bId = "11"
db.collection.updateOne({ _id: aId }, { $pull: { group: { _id: bId } } })
But still failed to delete it.
Anything wrong with my code?
Use $unset like this:
db.products.update(
{ sku: "unknown" },
{ $unset: { quantity: "", instock: "" } })
This code will remove the fields quantity and instock from the firstdocument in the products collection where the field sku has a value of unknown.
For more info

How to join two table in mongodb and push into array

Here i am having two collection Organizations & Groups , my requirement is i want to check oldOrgID in Organizations table and i have to take schoolCode & schoolName and push into mainData upto i had completed,
Now my question is Organizations table i have to take orgID and i have to check in Groups table otherIds.orgID, if suppose match means i have to take the name in Groups table push in to mainData
Organizations:
{
"_id" : ObjectId("5c11efebd9cb4d35f47d6bd0"),
"orgID" : "5b6c82462fb9ca35444d0ba2",
"name" : "The Punjab Public School",
"oldOrgID" : "176348"
}
Groups:
/* 1 createdAt:12/13/2018, 11:06:02 AM*/
{
"_id" : ObjectId("5c11efc2d9cb4d35f47d6bcf"),
"groupID" : "2",
"name" : "8 B",
"otherIds" : {
"orgID" : "ORG1"
},
"version" : NumberInt(1)
},
/* 2 createdAt:12/13/2018, 11:05:08 AM*/
{
"_id" : ObjectId("5c11ef8cd9cb4d35f47d6bce"),
"groupID" : "1",
"name" : "8 A",
"otherIds" : {
"orgID" : "ORG1"
},
"version" : NumberInt(1)
}
My Code
var mainData = {};
var schoolCode = 176348 ;
db.Organizations.find({"oldOrgID" : schoolCode})
// .limit(1)
// .skip(15)
.forEach(function(doc){
var OrgID = doc.orgID;
if(mainData[OrgID] === undefined )
{
mainData[OrgID] = {}; // org name undefined means we are making empty object here
}
mainData[OrgID]['schoolCode'] = doc.oldOrgID;
mainData[OrgID]['schoolName'] = doc.name;
});
mainData
Getting Output
{
"5b6c82462fb9ca35444d0ba2" : {
"schoolCode" : "176348",
"schoolName" : "The Punjab Public School"
}
}
Expected Output
{
"5b6c82462fb9ca35444d0ba2" : {
"schoolCode" : "176348",
"schoolName" : "The Punjab Public School",
"group-section" :
[
{
"name" : "8 B"
},
{
"name" : "8 A"
}
]
}
}
Aggregation code
db.Organizations.aggregate([
{
$match: {
oldOrgID: "176348"
}
},
{
$lookup: {
from: "Groups",
localField: "orgID",
foreignField: "otherIds.orgID",
as: "group-section"
}
},
{
$project: {
"group-section._id": 0,
"group-section.groupID": 0,
"group-section.otherIds": 0,
"group-section.version": 0
}
}
])
Output
{
"_id" : ObjectId("5c11efebd9cb4d35f47d6bd0"),
"orgID" : "5b6c82462fb9ca35444d0ba2",
"name" : "The Punjab Public School",
"oldOrgID" : "176348",
"group-section" : [ ]
}
$lookup is one of the pipeline stage of aggregation query so you need to use the Mongo Aggregation query as:
db.collection.aggregate([
{
$match: {
oldOrgID: "176348"
}
},
{
$lookup: {
from: "other",
localField: "orgID",
foreignField: "otherIds.orgID",
as: "group-section"
}
},
{
$project: {
"group-section._id": 0,
"group-section.groupID": 0,
"group-section.otherIds": 0,
"group-section.version": 0
}
}
])
You can try Aggregate Query and use the
LOOKUP operation:
Read more: https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/
You can something like this:
db.Organizations.aggregate([{
$lookup:
{
from: "Groups" // <collection to join>,
localField: "oldOrgID" // <field from the input documents>,
foreignField: "otherIds.orgId" // <field from the documents of the "from" collection>,
as: "group-section" <output array field>
}
}]);
Hope this give you idea.

How do I query referenced objects in MongoDB?

I've got two collections in my Mongo database, and the Foos contain references to one or more Bars:
Foo: {
prop1: true,
prop2: true,
bars: [
{
"$ref": "Bar",
"$id": ObjectId("blahblahblah")
}
]
}
Bar: {
testprop: true
}
What I want is to find all of the Foos that have at least one Bar that has its testprop set to true. I've tried this command, but it doesn't return any results:
db.Foo.find({ "bars.testprop" : { "$in": [ true ] } })
Any ideas?
You can now do it in Mongo 3.2 using $lookup
$lookup takes four arguments
from: Specifies the collection in the same database to perform the join with. The from collection cannot be sharded.
localField: Specifies the field from the documents input to the $lookup stage. $lookup performs an equality match on the localField to the foreignField from the documents of the from collection.
foreignField: Specifies the field from the documents in the from collection.
as: Specifies the name of the new array field to add to the input documents. The new array field contains the matching documents from the from collection.
db.Foo.aggregate(
{$unwind: "$bars"},
{$lookup: {
from:"bar",
localField: "bars",
foreignField: "_id",
as: "bar"
}},
{$match: {
"bar.testprop": true
}}
)
You can't. See http://www.mongodb.org/display/DOCS/Database+References
You have to do it in the client.
We have had a similar issue as we use MongoDB (3.4.4, actually 3.5.5 for testing) in combination with Morphia where we use #Referenece on a couple of entities. We are though not that happy with this solution and are considering removing these declarations and instead do the reference lookups manually.
I.e. we have a company collection and a user collection. The user entity in Morphia contains a #Refrence declaration on a company entity. The respective company collections contains entries like:
/* 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
}
while the user collections contains the following entries:
/* 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
}
In order to create a special company user view we also wanted to dereference the company in the user and only include selected fields. Based on a comment within a bug report we learned that MongoDB provides a $objectToArray: "$$ROOT.element" operation which basically splits fields of the given elements into key and value pairs. Note that $objectToArray operation was added in MongoDB version 3.4.4!
An aggregation on the company element contained in the user collection using the $objectToArray operation may look like below:
dp.user.aggregate([{
$project: {
"userId": 1,
"userKeyEncrypted": 1,
"uuid":1,
"roles": 1,
"passwordHash": 1,
"disabled": 1,
company: { $objectToArray: "$$ROOT.company" }
}
}])
The result of above aggregation looks like this:
/* 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
}
Now it's simply a matter of filtering unwanted stuff (i.e. users that have no company assigned and selecting the right array entries) in order to feed the $lookup operation #sidgate has already explained and copy the value of the dereferenced company into the user response.
I.e. an aggregation like the one below will perform an join and add the data of the company to users that have a company assigned as the as value defined in the lookup:
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" } }
])
The result to the above aggregation can be seen below:
/* 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
}
]
}
As can hopefully be seen we only have the two users that contained a company reference and the two users now have also the complete company data in the response. Now additional filtering can be applied to get rid of the key/value helper and also to hide unwanted data.
The final query we came up with looks like this:
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 ] } } }
])
Which finally returns our desired representation:
/* 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"
}
Some final note to this approach: This aggregation isn't very fast, sadly, but at least it gets the job done. I haven't tested it with an array of references as originally asked though this may require some additional unwindings probably.
Update: A further way of aggregating the data, which is more in line with the comments in the above mentioned bug report, can be seen below:
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 ] } } }
])
Here the $let: { vars: ..., in: ... } operation copies the key and value of the reference into an own object and thus allows later on to lookup the reference via the corresponding operation.
Which of these aggregations performs better has yet to be profiled.
Well.. you could query the Bar Model for the _id of all documents with testprop: true, then do a find $in and populate bars on the Foo Model with an array of those _id's you got from the first query.. :P
Maybe that counts as "In the Client" :P just a thought.
It wasn't possible before, but improvements from Mongo v3.4 we can get very close to it.
You can do it with mongo-join-query. Your code would look like this:
const mongoose = require("mongoose");
const joinQuery = require("mongo-join-query");
joinQuery(
mongoose.models.Foo,
{
find: { "bars.testprop": { $in: [true] } },
populate: ["bars"]
},
(err, res) => (err ? console.log("Error:", err) : console.log("Success:", res.results))
);
How does it work?
Behind the scenes mongo-join-query will use your Mongoose schema to determine which models to join and will create an aggregation pipeline that will perform the join and the query.
Disclosure: I wrote this library to tackle precisely this use case.

Categories

Resources