Hello I need to know if it is possible to generate a structure tree so that you understand me I have this model:
const StructureSchema = new Schema({
parent: { type: Schema.ObjectId, ref: 'article' },
child : { type: Schema.ObjectId, ref: 'article' },
quantity: { type: Number, default: 0 },
utilization : { type: String},
increasePrice : { type : Number},
optional : { type : Boolean, default : false}
});
It happens that even there it returns me correct when it is a single level it happens that Child can act as Parent too and I would like to bring that structure too.
I was watching $ graphLookup but I could not implement it as I want some idea how to achieve it?
You seem to be using ObjectId (i.e. other _id) for the parent and child keys. I would recommend simplifying your design to using just parent as follows:
var r =
[
{"_id":0, otherData:"foo2"}
,{"_id":3, parent:0, otherData:"foo1"}
,{"_id":4, parent:0, otherData:"foo2"}
,{"_id":5, parent:0, otherData:"foo2"}
,{"_id":6, parent:0, otherData:"foo3"}
,{"_id":7, parent:6, otherData:"foo3"}
,{"_id":8, parent:6, otherData:"foo3"}
,{"_id":9, parent:8, otherData:"foo3"}
];
In this design a record without a parent is the "root."
Both parent-to-child and child-to-parent lookups are possible.
// Child-to-parent lookup:
db.foo.aggregate([
// Find the child we seek:
{$match: {_id: 9}}
// Now get the parent chain:
,{$graphLookup: {
from: "foo",
connectToField: "_id",
connectFromField: "parent",
startWith: "$parent", // usually value of connectFromField
depthField: "n",
as: "zz"
}}
]);
{
"_id" : 9,
"parent" : 8,
"otherData" : "foo3",
"zz" : [
{
"_id" : 8,
"parent" : 6,
"otherData" : "foo3",
"n" : NumberLong(0)
},
{
"_id" : 0,
"otherData" : "foo2",
"n" : NumberLong(2)
},
{
"_id" : 6,
"parent" : 0,
"otherData" : "foo3",
"n" : NumberLong(1)
}
]
}
// Parent-to-child lookup:
db.foo.aggregate([
// Find the parent we seek:
{$match: {_id: 0}} // just go for the top for fun
// Now get the child chains; there can be >1 child for a parent:
,{$graphLookup: {
from: "foo",
connectToField: "parent",
connectFromField: "_id",
startWith: "$_id", // usually value of connectFromField
depthField: "n",
as: "zz"
}}
]);
{
"_id" : 0,
"otherData" : "foo2",
"zz" : [
{
"_id" : 5,
"parent" : 0,
"otherData" : "foo2",
"n" : NumberLong(0)
},
{
"_id" : 8,
"parent" : 6,
"otherData" : "foo3",
"n" : NumberLong(1)
},
{
"_id" : 6,
"parent" : 0,
"otherData" : "foo3",
"n" : NumberLong(0)
},
{
"_id" : 9,
"parent" : 8,
"otherData" : "foo3",
"n" : NumberLong(2)
},
{
"_id" : 4,
"parent" : 0,
"otherData" : "foo2",
"n" : NumberLong(0)
},
{
"_id" : 3,
"parent" : 0,
"otherData" : "foo1",
"n" : NumberLong(0)
},
{
"_id" : 7,
"parent" : 6,
"otherData" : "foo3",
"n" : NumberLong(1)
}
]
}
Related
I am trying to figure out a way to update multiple documents in a collection with different values based on a key.
persons collection:
{
_id: 1,
name: "Jackie Chan",
Country: Australia
},
{
_id: 2,
name: "Brad Pitt",
Country: Russia
},
{
_id: 3,
name: "Al Pacino",
Country: USA
}
Payload:
{
_id: 1,
name:"Jackie Chan",
Country:"China"
}
,{
_id: 2,
name:"Brad Pitt",
Country:"USA"
}
persons collection after update:
{
_id: 1,
name: "Jackie Chan",
Country: "China"
},
{
_id: 2,
name: "Brad Pitt",
Country: "USA"
},
{
_id: 3,
name: "Al Pacino",
Country: "USA"
}
SQL equivalent would be :
update t1.country = t2.country from persons t1 inner join #temptable t2 on t1._id=t2._id
None of the examples mentioned here explain how to do it. Unless I am missing something?
It seems like bulk write is exactly the right tool. Simply map the payload array so to make it an array of updates, such as:
db.persons.bulkWrite(payload.map( function(p) {
return { updateOne:{
filter: {_id: p._id},
update: {$set: {Country: p.Country}}
}}
}))
//code when run from mongodb client
> db.persons.find();
{ "_id" : 1, "name" : "Jackie Chan", "Country" : "China1" }
{ "_id" : 2, "name" : "Brad Pitt", "Country" : "Russia1" }
{ "_id" : 3, "name" : "Al Pacino", "Country" : "USA1" }
> var payload=[{_id: 1,name:"Jackie Chan",Country:"China2"},
... {_id: 2,name: "Brad Pitt",Country: "Russia2"},
... {_id: 3, name: "Al Pacino",Country: "USA2"}];
> print("payload: ",payload);
payload: [object Object],[object Object],[object Object]
>
> db.persons.bulkWrite(payload.map( function(p) {
... return { updateOne:{
... filter: {_id: p._id},
... update: {$set: {Country: p.Country}}
... }}
... }));
{
"acknowledged" : true,
"deletedCount" : 0,
"insertedCount" : 0,
"matchedCount" : 3,
"upsertedCount" : 0,
"insertedIds" : {
},
"upsertedIds" : {
}
}
> db.persons.find();
{ "_id" : 1, "name" : "Jackie Chan", "Country" : "China2" }
{ "_id" : 2, "name" : "Brad Pitt", "Country" : "Russia2" }
{ "_id" : 3, "name" : "Al Pacino", "Country" : "USA2" }
>
my collection Stucture is like below:
{"_id" : ObjectId("5aec2ce5020ba15d2fb2665a"),
"password" : "e10adc3949ba59abbe56e057f20f883e",
"unique_id" : 22,
"first_name" : "Foram",
"last_name" : "Test ",
"country_phone_code" : "+61",
"email" : "a#a.com",
"phone" : "1231231231",
"device_type" : "android",
"admintypeid" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"vehicle_detail" : [
{
"service_name" : "You",
"_id" : ObjectId("5aec2d08020ba15d2fb2665c"),
"name" : "Qee",
"plate_no" : "Qwe111",
"model" : "Qee",
"color" : "Blue",
"passing_year" : "2005",
"service_type" : [
ObjectId("5a9f9c53b9e8fa66f9b934c1")
],
"admin_type_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"is_selected" : true,
"pictureData" : "vehicle_images/5aec2d08020ba15d2fb2665cWjyP.jpg"
},
{
"pictureData" : "vehicle_images/5aec35e00efc106080e14ec7y2tO.jpg",
"is_selected" : false,
"admin_type_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"service_type" : [
ObjectId("5a9f9c53b9e8fa66f9b934c1"),
ObjectId("5ac954b7075e16583dc3311f")
],
"passing_year" : "2018",
"color" : "No",
"model" : "Bcs",
"plate_no" : "12112",
"name" : "Bcs",
"_id" : ObjectId("5aec35e00efc106080e14ec7"),
"service_name" : "You"
}
],
"service_type" : [
ObjectId("5a9f9c53b9e8fa66f9b934c1")
],
"is_vehicle_document_uploaded" : true,
"is_trip" : [],
"__v" : 14
}
i want to campare ids in my service_type array which is in vehicle_detail array
to another collection named city_types than from that i have to comapare typid of city_types collection to tyes collection and get value of name from type collection
my city_type collection would be something like below
{
"_id" : ObjectId("5a9f9c53b9e8fa66f9b934c1"),
"countryid" : ObjectId("5abb275fd20731597cc01229"),
"cityid" : ObjectId("5abb27cbd20731597cc0122a"),
"typeid" : ObjectId("5a9f9b55b9e8fa66f9b934c0")
"__v" : 0}
containing type_id
and my types collection is like below
{
"_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"typename" : "You",
"description" : "KARRU You",
"__v" : 0,
"main_type" : 0
}
what i want it name of type
from id
i have used code below:
exports.get_provider_vehicle_list = function (req, res, next) {
var mongoose = require('mongoose');
var Schema = mongoose.Types.ObjectId;
var condition = {$match: {"_id": Schema(req.body.provider_id)}};
var vunwind = {$unwind: "$vehicle_detail"}
var lookup = {
$lookup:
{
from: "types",
localField: "vehicle_detail.admin_type_id",
foreignField: "_id",
as: "type_detail"
}
};
var unwind = {$unwind: {
path: "$type_detail",
preserveNullAndEmptyArrays: true
}
};
var group = {$group: {
_id: null,
"vehicle_detail": {$push: {
is_selected: "$vehicle_detail.is_selected",
admin_type_id: "$vehicle_detail.admin_type_id",
service_type: "$vehicle_detail.service_type",
passing_year: "$vehicle_detail.passing_year",
color: "$vehicle_detail.color",
model: "$vehicle_detail.model",
plate_no: "$vehicle_detail.plate_no",
name: "$vehicle_detail.name",
_id: "$vehicle_detail._id",
type_image_url: '$type_detail.type_image_url',
service_name:"$vehicle_detail.service_name",
pictureData:"$vehicle_detail.pictureData"
}}
}
}
Provider.aggregate([condition, vunwind, lookup, unwind, group], function (err, provider) {
if (err || provider.length == 0) {
res.json({success: true, vehicle_list: []})
} else {
res.json({success: true, vehicle_list: provider[0].vehicle_detail})
}
})
};
and my res is like below:
{
"success": true,
"vehicle_list": [
{
"is_selected": true,
"admin_type_id": "5a9f9b55b9e8fa66f9b934c0",
"service_type": [
"5a9f9c53b9e8fa66f9b934c1",
"5ac954b7075e16583dc3311f"
],
"passing_year": "2005",
"color": "Blue",
"model": "Qee",
"plate_no": "Qwe111",
"name": "Qee",
"_id": "5aec2d08020ba15d2fb2665c",
"type_image_url": "service_type_images/5a9f9b55b9e8fa66f9b934c0mjYA.png",
"service_name": "You",
"pictureData": "vehicle_images/5aec2d08020ba15d2fb2665cWjyP.jpg"
}
]
}
and res i want is below
{
"success": true,
"vehicle_list": [
{
"is_selected": true,
"admin_type_id": "5a9f9b55b9e8fa66f9b934c0",
"service_type": [
"5a9f9c53b9e8fa66f9b934c1",
"5ac954b7075e16583dc3311f"
],
"passing_year": "2005",
"color": "Blue",
"model": "Qee",
"plate_no": "Qwe111",
"name": "Qee",
"_id": "5aec2d08020ba15d2fb2665c",
"type_image_url": "service_type_images/5a9f9b55b9e8fa66f9b934c0mjYA.png",
"service_type_name": [
"You"
],
"service_name": "You",
"pictureData": "vehicle_images/5aec2d08020ba15d2fb2665cWjyP.jpg"
}
]
}
please check.
db.getCollection('vehicle').aggregate([
{
$unwind: {
path: '$vehicle_detail',
preserveNullAndEmptyArrays: true,
},
},
{
$unwind: {
path: '$vehicle_detail.service_type',
preserveNullAndEmptyArrays: true,
},
},
{
$lookup: {
from: 'city_type',
localField: 'vehicle_detail.service_type',
foreignField: '_id',
as: 'city_type',
},
},
{
$unwind: {
path: '$city_type',
preserveNullAndEmptyArrays: true,
},
},
{
$lookup: {
from: 'types',
localField: 'city_type.typeid',
foreignField: '_id',
as: 'types',
},
},
{
$unwind: {
path: '$types',
preserveNullAndEmptyArrays: false,
},
},
{
$group: {
_id: '$_id',
first_name: { $first: 1 },
last_name: { $first: 1 },
types: { $first: '$types' },
},
},
])
Result
{
"_id" : ObjectId("5aec2ce5020ba15d2fb2665a"),
"first_name" : 1.0,
"last_name" : 1.0,
"types" : {
"_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"typename" : "You",
"description" : "KARRU You",
"__v" : 0,
"main_type" : 0
}
}
Note : You can added fields in group which you need.
I am sending what I understood from your explanation. I hope it helps.
Some points:
I changed your objects city_types and types to an array, since you said Collections;
I link the id to match Vehicle->city->types
function ObjectId(id){
return id;
}
var obj = {"_id" : ObjectId("5aec2ce5020ba15d2fb2665a"),
"password" : "e10adc3949ba59abbe56e057f20f883e",
"unique_id" : 22,
"first_name" : "Foram",
"last_name" : "Test ",
"country_phone_code" : "+61",
"email" : "a#a.com",
"phone" : "1231231231",
"device_type" : "android",
"admintypeid" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"vehicle_detail" : [
{
"service_name" : "Service 1",
"_id" : ObjectId("5aec2d08020ba15d2fb2665c"),
"name" : "Qee",
"plate_no" : "Qwe111",
"model" : "Qee",
"color" : "Blue",
"passing_year" : "2005",
"service_type" : [
ObjectId("5a9f9c53b9e8fa66f9b934c1")
],
"admin_type_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"is_selected" : true,
"pictureData" : "vehicle_images/5aec2d08020ba15d2fb2665cWjyP.jpg"
},
{
"pictureData" : "vehicle_images/5aec35e00efc106080e14ec7y2tO.jpg",
"is_selected" : false,
"admin_type_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"service_type" : [
ObjectId("5a9f9c53b9e8fa66f9b934c1"),
ObjectId("5ac954b7075e16583dc3311f")
],
"passing_year" : "2018",
"color" : "No",
"model" : "Bcs",
"plate_no" : "12112",
"name" : "Bcs",
"_id" : ObjectId("5aec35e00efc106080e14ec7"),
"service_name" : "Service 2"
}
],
"service_type" : [
ObjectId("5a9f9c53b9e8fa66f9b934c1")
],
"is_vehicle_document_uploaded" : true,
"is_trip" : [],
"__v" : 14
};
var city_types = [{
"_id" : ObjectId("5aec2d08020ba15d2fb2665c"),
"countryid" : ObjectId("5abb275fd20731597cc01229"),
"cityid" : ObjectId("5abb27cbd20731597cc0122a"),
"typeid" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"__v" : 0
}];
var types = [{
"_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"typename" : "You",
"description" : "KARRU You",
"__v" : 0,
"main_type" : 0
}];
function print() {
//iterate over vehicle_detail
obj.vehicle_detail.forEach((vehicle)=>{
console.log("vehicle="+ vehicle._id);
//iterate over cities type
city_types.forEach((city)=> {
console.log("city="+ city._id);
if (city._id == vehicle._id) {
//iterate over types
types.forEach((type)=> {
console.log("type="+ type._id);
if (city.typeid == type._id) {
console.log("Type Name: "+type.typename);
}
})
}
})
});
}
print();
The output of the program is:
>node index4.js
vehicle=5aec2d08020ba15d2fb2665c
city=5aec2d08020ba15d2fb2665c
type=5a9f9b55b9e8fa66f9b934c0
Type Name: You
vehicle=5aec35e00efc106080e14ec7
city=5aec2d08020ba15d2fb2665c
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.
I have a schema :
{
"id": String,
"username": String,
"password": String,
"email": String,
"firstName": String,
"lastName": String,
"system" : {
"item" : {type: Number},
"update_interval" : { type: Number, max: 99999 },
"reading" : [
{
"id" : { type: Number},
"adc1" : { type: Number, max: 4095 },
"adc2" : { type: Number, max: 4095 },
"pc_datestamp" :Date,
}
]
}
now i want to add values to
"reading" : [
{
"id" : { type: Number},
"adc1" : { type: Number, max: 4095 },
"adc2" : { type: Number, max: 4095 },
"pc_datestamp" :Date,
}
]
but i dont know where I am wrong I have tried to update data from mongoshell but no success till now
> db.users.update( {"email" : "test#test.com", "system.item": 1, }, {"$push": {"system.$.reading": [{"adc1" : "123", "adc2": "1245", "id":"1" }] } })
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 16837,
"errmsg" : "The positional operator did not find the match needed from the query. Unexpanded update: system.$.reading"
}
> db.users.update( {"email" : "test#test.com", "system": {$elemMatch:{ item: 1}} }, {"$push": {"system.$.reading": {"adc1" : "123", "adc2": "1245", "id":"1" } } })
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
I have set the value of item as one
> db.users.find( {"email" : "test#test.com", "system.item": 1} ).pretty()
{
"_id" : ObjectId("56dd88578ff7fbd714091a4a"),
"lastName" : "test",
"firstName" : "test",
"email" : "test#test.com",
"password" : "$2a$10$wY9wr9oreza4fBX7CfXym.rPZUPrcesigYIfWd0zbM4dDjBy6k3vy",
"username" : "test",
"system" : {
"item" : 1,
"reading" : [ ]
},
"__v" : 0
}
I have followed
Mongodb $push in nested array
and this
Insert data in nested array in mongodb
and many more questions but cannot find whats wrong.
Since the positional $ operator acts as a placeholder for the first element that matches the query document, and the array field must appear as part of the query document, your update
operation does not satisfy these conditions hence it suffers from the error fate you are getting. In your query you are only referencing the "system.item" field which is not an array.
The other option you can do is ditch the positional $ operator in your update and just add the object to the array using $addToset which adds elements to an array only if they do not already exist in the set:
db.users.update(
{
"email": "test#test.com",
"system.item": 1
},
{
"$addToSet": {
"system.reading": {
"adc1" : "123",
"adc2": "1245",
"id":"1"
}
}
}
)
I have two collections. Lets call one baskets and the other one fruits.
In baskets we have the following document:
[{
basket_name: "John's Basket",
items_in_basket: [
{
fruit_id: 1,
comment: "Delicious!"
},
{
fruit_id: 2,
comment: "I did not like this"
}
]
}]
And in fruits we have the following documents:
[{
_id: 1,
fruit_name: "Strawberry",
color: "Red"
},
{
_id: 2,
fruit_name: "Watermelon",
color: "Green"
}]
How do I get information on each fruit in John's Basket?
The result should look like this:
[{
fruit_id: 1,
comment: "Delicious!",
fruit_name: "Strawberry",
color: "Red"
},
{
fruit_id: 2,
comment: "I did not like this",
fruit_name: "Watermelon",
color: "Green"
}]
There's no "join" in MongoDB. You either could:
consider using a MapReduce function to create a new structure that contains the merged data
write the code necessary to fetch each fruit instance on demand and merge it in your client code with a basket document.
denormalize the data and include the details for each fruit in the basket document. This poses it's own set of issues as data is duplicated and updates to a particular fruit would then need to be made to every usage in the collection.
Both have their pros and cons.
You might find this Q/A helpful, and also this documentation for MongoDB.
this is no longer true.
Since version 3.2, MongoDB added the $lookup command.
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 },
{ "_id" : 3 }
])
db.inventory.insert([
{ "_id" : 1, "sku" : "almonds", description: "product 1", "instock" : 120 },
{ "_id" : 2, "sku" : "bread", description: "product 2", "instock" : 80 },
{ "_id" : 3, "sku" : "cashews", description: "product 3", "instock" : 60 },
{ "_id" : 4, "sku" : "pecans", description: "product 4", "instock" : 70 },
{ "_id" : 5, "sku" : null, description: "Incomplete" },
{ "_id" : 6 }
])
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
])
returns:
{
"_id" : 1,
"item" : "almonds",
"price" : 12,
"quantity" : 2,
"inventory_docs" : [
{ "_id" : 1, "sku" : "almonds", "description" : "product 1", "instock" : 120 }
]
}
{
"_id" : 2,
"item" : "pecans",
"price" : 20,
"quantity" : 1,
"inventory_docs" : [
{ "_id" : 4, "sku" : "pecans", "description" : "product 4", "instock" : 70 }
]
}
{
"_id" : 3,
"inventory_docs" : [
{ "_id" : 5, "sku" : null, "description" : "Incomplete" },
{ "_id" : 6 }
]
}