I'm trying to perform a query that filters an array from another document, to mention something about the structure, I try to populate the document based on the specified product and store id, so that it doesn't bring the other data from the array but in this case I understand that I can't access the document fields while in the middleware, I'm reading the documentation but I still learning concepts
bUnitSchema.pre(/^find/, function (next) {
this.populate({
path: "menuItem.product",
select: {
"storeId.$": 1,
},
match: {
"storeId.store": "62a811d1af67f5415770f297",
},
});
next();
});
any guide would be of excellent help
I try something like this
bUnitSchema.pre(/^find/, function (next) {
this.populate({
path: "menuItem.product",
select: {
"storeId.$": 1,
},
match: {
*//here im trying to do something like this*
*"storeid.store": bUnitSchema.menuItem.store*
},
});
next();
});
but give me
store not defined
I can solve it, i use a post hook :
bUnitSchema.post(/^find/, async function (docs) {
for (let doc of docs) {
for (let store of doc.menuItem) {
await doc.populate({
path: "menuItem.product",
select: {
image: 1,
imagePath: 1,
plateFor: 1,
description: 1,
name: 1,
_id: 1,
"storeId.$": 1,
},
match: {
"storeId.store": store.store,
},
});
}
}
});
If anyone has a better idea, it would be appreciated.
Related
I have MongoDB database (with Mongoose) containing a collection of Products (among others), which looks like this:
[
{
name: 'Product A',
url: 'product-a',
category: 'accesory',
price: 12,
shortDescription: ['example description'],
technicalSpecs: [{ speed: 10, weight: 20 }],
images: [],
reviews: [],
relatedProducts: [
{
url: 'product-b',
name: 'Product B',
// to be added in Update query
//id: id_of_related_product
}
]
} /* other Product objects */
]
As every MongoDB document is provided with _id property by default, but within the relatedProducts array i only have url and name properties, i want to add the id property (associated with corresponding Product) for each object in the relatedProducts array, so i will be able to conveniently query and process those related products.
I came up with an idea to query all Products to get only those, which have non-empty relatedProducts array. Then i loop them and i search for Product model, which has specific url and name properties - this let's me get it's true (added by MongoDB) _id. At the end i want to add this _id to matching object inside relatedProducts array.
My code:
async function assignIDsToRelatedProducts(/* Model constructor */ Product) {
const productsWithRelatedOnes = await Product.find(
{ relatedProducts: { $ne: [] }}, ['relatedProducts', 'name', 'url']
);
for (const productItem of productsWithRelatedOnes) {
for (const relatedProduct of productItem.relatedProducts) {
const product = await Product.findOne(
{ url: relatedProduct.url, name: relatedProduct.name },
'_id'
);
// throws error
await productItem.updateOne(
{ 'relatedProducts.url': relatedProduct.url },
{ $set: { 'relatedProducts.$.id': product._id } }
);
}
}
}
However it throws the following error:
MongoError: Cannot create field 'url' in element {relatedProducts: [ /* array's objects here */ ]}
I don't know why MongoDB tries to create field 'url', as i use it to project/query url field (not create it) in updateOne method. How to fix this?
And - as i am newbie to MongoDB - is there a simpler way of achieving my goal? I feel that those two nested for..of loops are unnecessary, or even preceding creation of productsWithRelatedOnes variable is.
Is it possible to do with Mongoose Virtuals? I have tried it, but i couldn't match virtual property within the same Product Model - attach it to each object in relatedProducts array - after calling .execPopulate i received either an empty array or undefined (i am aware i should post at-the-time code of using Virtual, but for now i switched to above solution).
Although i didn't find solution or even reason of my problem, i solved it with a slightly other approach:
async function assignIDsToRelatedProducts(Product) {
const productsHavingRelatedProducts = Product.find({ relatedProducts: { $ne: [] }});
for await (const withRelated of productsHavingRelatedProducts) {
for (const relatedProductToUpdate of withRelated.relatedProducts) {
const relatedProduct = await Product
.findOne(
{ url: relatedProductToUpdate.url, name: relatedProductToUpdate.name },
['url', '_id']
);
await Product.updateMany(
{ 'relatedProducts.url': relatedProduct.url },
{ $set: { 'relatedProducts.$.id': relatedProduct._id } }
);
}
}
const amountOfAllProducts = await Product.find({}).countDocuments();
const amountOfRelatedProductsWithID = await Product
.find({ 'relatedProducts.id': { $exists: true } }).countDocuments();
console.log('All done?', amountOfAllProducts === amountOfRelatedProductsWithID);
}
Yet, i still suppose it can be done more concisely, without the initial looping. Hopefully somebody will suggest better solution. :)
I am attempting to update an existing record in Mongo DB and although the "save" completes successfully, The record in the DB is not actually updated. Here's a before an after of the data:
Before:
{
_id: '56be4ba9938e836b6f47e84a',
type: 'ec-input',
key: 'ec-input_2',
templateOptions: {
placeholder: 'My Placeholder...',
subtext: 'My Subtest',
label: 'My Label'
},
order: 1,
form: '56bd3e76413de7c862979b8c',
__v: 0
}
After:
{
_id: '56be4ba9938e836b6f47e84a',
type: 'ec-input',
key: 'ec-input_2',
templateOptions: {
placeholder: 'I am different now!!',
subtext: 'Me too..',
label: 'My Label'
},
order: 1,
form: '56bd3e76413de7c862979b8c',
__v: 0
}
The templateOptions field is configured as type mongoose.Schema.Types.Mixed on the mongoose model. Here's how i'm trying to save the update:
export function update(req, res) {
if (req.body._id) {
delete req.body._id;
}
Field.findByIdAsync(req.params.id)
.then(entity => {
var updated = _.merge(entity, req.body);
updated.saveAsync()
.spread(updated => {
if (entity) {
res.status(200).json(entity);
}
});
});
}
I set a breakpoint before updated.saveAsync is called and updated correctly has the modified data. I also set a breakpoint in the spread callback, and updated still has the modified data. After updated.saveAsync is complete, I check mongo directly, and the record was not updated.
Any ideas about what I can do to make this work would be greatly appreciated. Thanks!
I want to query a library by id, and I need only some properties. I tried below script but it is not working:
Library.findOne({
id: libraryId
}, {
latitude: 1, longitude: 1,
name: 1, address: 1, image: 1
}).exec(function (err, libObj) {
if (err)
return res.ok(err);
return res.ok(libObj);
});
What is wrong in my code?
For projections you could use the native() method that has direct access to the mongo driver:
var criteria = { id: libraryId },
projection = { latitude: 1, longitude: 1, name: 1, address: 1, image: 1 };
// Grab an instance of the mongo-driver
Library.native(function(err, collection) {
if (err) return res.serverError(err);
// Execute any query that works with the mongo js driver
collection.findOne(criteria, projection).exec(function (err, libObj) {
console.log(libObj);
});
});
-- UPDATE --
Another option is to use the find() method which can accept the criteria and projection documents as parameters and append limit(1) to return just one document. For the projection object you would need to use the select key that holds an array of the projection fields:
var criteria = { _id: Library.mongo.objectId(libraryId) },
projection = { select: [ "latitude", "longitude", "name", "address", "image" ] };
Library.find(criteria, projection).limit(1).exec(function (err, res) {
var libObj = res[0];
console.log(libObj );
});
#chridam Thanks a lot for your answer. I changed something and the script working well
var criteria = { _id: Library.mongo.objectId(libraryId) },
projection = { latitude: 1, longitude: 1, name: 1, address: 1, image: 1 };
// Grab an instance of the mongo-driver
Library.native(function(err, collection) {
if (err) return res.serverError(err);
// Execute any query that works with the mongo js driver
collection.findOne(criteria, projection, function (err, libObj) {
sails.log(libObj);
sails.log(err);
});
});
I want to exclude some fields from result.
I have code:
users = db.select('users');
users.find( {}, { sort: { points:1 }, privateKey:0, publicKey:0}, function(err,data){
res.send(data);
});
I want to exclude private and public key from results.
Can I do that using monk?
You can also do it like this:
users.find( {}, { sort: { points:1 }, fields : { privateKey:0, publicKey:0} },
function(err,data){
res.send(data);
}
);
According to documentation first argument in find is filter and second is projection .But you have used sort . It will not able to interpret . You are trying to confuse projection with sort .Sorting should be after find and projection.
You can write projection like { field1: <boolean>, field2: <boolean> ... }
Note :
The find() method always includes the _id field even if the field is not explicitly stated to return in the projection parameter.
users.find({}, { privateKey: 0, publicKey: 0 }).sort({points: 1}).toArray(
function (err, data) {
res.send(data);
});
For me, I need to use the .project() method:
const someFunction = async () => {
const result = await users
.find({}, { sort: { points: 1 })
.project({ privateKey: 0, publicKey: 0});
};
This is what worked for me for excluding the _id field.
const courseChapters = await db
.collection("users")
.find({}, { projection: { _id: 0 } })
.toArray();
So the example in the question would look something like this.
users.find(
{},
{ projection: { points: 1, privateKey: 0, publicKey: 0 } },
function (err, data) {
res.send(data);
}
);
Check out this other answer that says you may need the fields field instead of projection depending upon your driver
I have a data set like this:
{
name : 'Doc Name',
photos: [
{
name: 'photo1',
url: 'http://.....'
},
{
name: 'photo2',
url: 'http://......'
}
],
etc ...
Using Monk https://github.com/LearnBoost/monk how do I update photo2? I can use an index as I am iterating over the fields at the moment.
My current attempt below gives me an error, and I can't use a variable for the JSON selector (as in the index).
collection.update({_id: data._id}, {photos[i].data: filename}, function(err, updatedata) {
});
Updating items at a position in an array can be done using the positional $ operator
collection.update(
{ _id: data.id, "photos.name": "photo2" },
{ $set: { "photos.$.data": "yourdata" } }
)
So I found a solution to my problem but there may be some better options and I will leave it unanswered. But for anyone else with the same issue this is what I did:
I extracted the MongoDB document as an object in Node.js, manipulated the document, and then replaced the entire array in a single update statement. For example here is some pseudo code:
collection.find({id: 1}, function(err, doc){
for(i=0; i< doc.array.length; i++) {
//do what you gotta do
doc.array[i].property = 'new value';
}
collection.update({id: 1}, {$set : {"doc.array": doc.array}}, function(err,doc){
console.log(err);
}
})