mongoose findOneAndUpdate appends objects only - javascript

I have an object like follows;
var exObj = { 'title' : 'name1'};
some of them have a data property of objects (not an array as I want to reference by name) and look like
exObj = {
title : 'name2',
data : {
prop1 : 'prop1',
prop2 : 'prop2'
}
}
Now I want to add another property to data, sometimes the data property will exist, and sometimes not, but I want to append a property (addedProp) and save it so I will end up with this;
exObj = {
title : 'name2',
data : {
prop1 : 'prop1',
prop2 : 'prop2',
addedProp : 'value'
}
}
When using findOneAndUpdate I can only seem to pass in a whole object which is then appended; something like this is what I currently do.
var data = {};
data.addedProp = value;
Collection.findOneAndUpdate({
title: 'name2'
}, {
data
}, {
upsert: true
})
.exec(function(err) {
if (err) {
console.log(err);
} else {
console.log('Updated');
}
});
But obviously this overides the data object that exists; what is the correct way to do a findOneAndUpdate and make more meaningful changes to the object? I tried casting to toObject() but then I don't have a proper mongoose object to do a .save() on.
To add further clarification; which this worked for simple properties (and worked well) which I know are set; there are some values I wish to add which I need to check if they have a value for the property before adding the property.
So something like this;
Collection.findOneAndUpdate({
field: val
}, {
if (tObj.title) {
title = tObj.title;
}
if (tObj.date) {
release_date = tObj.date;
}
if (tObj.name) {
name = tObj.name;
}
}, { upsert: true
})
.exec(function(err) {
if (err) {
//handler
} else {
//handler
}
});

Your question first seem daunting, but solution is quite simple, you don't need to use $ or upsert, because you are not using any array , so don't need of positional operator or upsert. You can use below code.
Collection.findOneAndUpdate({
title: 'name2'
},{
$set:{"data.prop3":"new prop3"}
})
.exec(function(err) {
if (err) {
console.log(err);
} else {
console.log('Updated');
}
});
It will add prop3 if not exists, or if it exists it will update it. I have checked the code on my local db with update and findOneAndUpdate

You need to use dot notation to target specific fields within an embedded object, building up your update object based on the supplied fields:
var update = {};
if (tObj.title) {
update.title = tObj.title;
}
if (tObj.data) {
if (tObj.data.prop1) {
update['data.prop1'] = tObj.data.prop1;
}
if (tObj.data.prop2) {
update['data.prop2'] = tObj.data.prop2;
}
if (tObj.data.addedProp) {
update['data.addedProp'] = tObj.data.addedProp;
}
}
Collection.findOneAndUpdate({
title: 'name2'
}, {
$set: update
}, {
upsert: true
})
.exec(function(err) {

Related

MongoDB - set nested field using name from variable

I want to create new field in my document, lets call it "shelf", it will be an object.
Next I want to make two $set operations - I want to put arrays named "Tom" and "Anna" into my "shelf".
The problem is that I can't match correct query to do that.
I'm using nodejs MongoDB driver.
var myid = 'Tom-Anna'
var TomArray = ["Tom"]
var AnnaArray = ["Anna"]
await db.collection('people').updateOne(
{ pairid: myid },
{ $set: { shelf: TomArray } },
{ upsert: true }
)
await db.collection('people').updateOne(
{ pairid: myid },
{ $set: { shelf: AnnaArray } },
{ upsert: true }
)
Finally, the "shelf" document containing only "AnnaArray", because it's overwriting previously added "TomArray".
I can't add "Tom" and "Anna" array to "shelf" at the same time because content of arrays are generated separately.
I was trying this code:
var name = 'Tom'
var array = ['Tom']
await db.collection('people').updateOne(
{ pairid: myid },
{ $set: { shelf[name]: array } },
{ upsert: true }
)
But it's throwing following error:
{ $set: { shelf[name]: array } },
^
SyntaxError: Unexpected token [
My goal is to set my field like JSON:
"shelf": { "Tom": ["Tom"], "Anna": ["Anna"] }
You can use dot notation to specify nested key name:
var name = 'Tom'
var array = ['Tom']
db.people.update({ pairid: 1 }, { $set: { [`shelf.${name}`]: array } })

How is it possible to increase value for dynamic property in mongoDB

I'm trying to icrease a dynamic value on my database which depends on user's mark, lets say there is a "vote" object with 3 parameters:
//data structure :
_id : someObjectId,
gameId : someObjectId,
votes : {
a: 0,
b: 0,
c: 0
}
userVote = { vote: 'b', gameId: '5cf3c5cc1c9d44000053defb' }
function addVote(userVote) {
userVote.gameId = new ObjectId(userVote.gameId)
return mongoService.connect()
.then(db => {
const collection = db.collection('totalVotes');
return collection.updateOne(
{ gameId: userVote.gameId },
{ $inc: { "votes[userVote[vote]":1 } }
)
})
}
So, of course, the "inc" line isn't working that way, how is it possible to do this?
Thanks.
MongoDB has no way of knowing what votes[userVote[vote] even means (syntax error aside). It has no access to your client-side JavaScript. If you want to set this dynamically, change your $inc line to the following:
{ $inc: { ["votes." + userVote.vote] : 1 } }
If the bracket notation above is not supported, you can try something like the following instead:
let incVote = {};
incVote["votes." + userVote.vote] = 1;
return collection.updateOne(
{ gameId: userVote.gameId },
{ $inc: incVote }
);

how do I increment field in array of objects after finding it (using findOne()) and before saving it?

I want to update an object inside an array of schemas without having to do two requests to the database. I currently am incrementing the field using findOneAndUpdate() if the object already exists and it works fine. but in case the object does not exist then I am having to make another request using update() to push the new object and make it available for later increments.
I want to be able to do only one request (e.g. findOne()) to get the user and then increment the field only if object exists in the array and if not I would like to push the new object instead. then save the document. this way I am only making one read/request from the database instead of two.
this is the function now:
async addItemToCart(body, userId) {
const itemInDb = await Model.findOneAndUpdate(
{
_id: userId,
'cart.productId': body.productId,
},
{ $inc: { 'cart.$.count': 1 } }
);
if (itemInDb) return true;
const updated = await Model.update(
{ _id: userId },
{ $push: { cart: body } }
);
if (updated.ok !== 1)
return createError(500, 'something went wrong in userService');
return true;
}
what I would like to do is:
async addItemToCart(body, userId) {
const itemInDb = await Model.findOne(
{
_id: userId,
'cart.productId': body.productId,
}
);
if (itemInDb) {
/**
*
* increment cart in itemInDb then do itemInDb.save() <<------------
*/
} else {
/**
* push product to itemInDb then save
*/
}
Thank you!
You can try findOneAndUpdate with upsert.
upsert: true then create data if not exists in DB.
Model.findOneAndUpdate(
{
_id: userId,
'cart.productId': body.productId,
},
{ $inc: { 'cart.$.count': 1 } },
{
upsert: true,
}
)
Use $set and $inc in one query.
try {
db.scores.findOneAndUpdate(
{
_id: userId,
'cart.productId': body.productId,
},
{ $set: { "cart.$.productName" : "A.B.C", "cart.$.productPrice" : 5}, $inc : { "cart.$.count" : 1 } },
{ upsert:true, returnNewDocument : true }
);
}
catch (e){
//error
}
reference Link : here
You can use upsert.
upsert is defined as an operation that creates a new document when no document matches the query criteria and if matches then it updates the document. It is an option for the update command. If you execute a command like below it works as an update, if there is a document matching query, or as an insert with a document described by the update as an argument.
Example: I am just giving a simple example. You have to change it according to your requirement.
db.people.update(
{ name: "Andy" },
{
name: "Andy",
rating: 1,
score: 1
},
{ upsert: true }
)
So in the above example, if the people with name Andy is found then the update operation will be performed. If not then it will create a new document.

Filter child node query on AngularFire2 firebase

I have this data structure in my firebase.
"produtos" : {
"3" : {
"data" : "2017-09-21",
"fornecedor" : {
"cnpj" : "123234534534",
"fantasia" : "Barreirinha"
},
"nNF" : 3,
"peso" : 3,
"precoCompra" : 6,
"vendido" : false
},
"123" : {
"data" : "2017-09-14",
"fornecedor" : {
"cnpj" : "123234534534",
"fantasia" : "Barreirinha"
},
"nNF" : 123,
"peso" : 23000,
"precoCompra" : 2.21,
"vendido" : false
}
}
I need to get a list of values of objects that contain the CNPJ key.
I'm using Angular, AngularFire2 and TypeScript, but I can not understand.
retrieveProdutos(cnpjFornecedor: string) {
this.produtos = this.db.list(this.paths.pathProduto, {
query: {
orderByChild: 'cnpj',
equalTo: cnpjFornecedor,
orderByKey: true,
}
});
}
How do I get this list? is returning empty.
You can only query one field ;
query: {
orderByChild: "vendido",
equalTo: "false"
}
which for example would give you all the child nodes that have their vendido child key set to false.
Also, if you want to access each child value, you could do:
this.produtos.subscribe(produtos => {
produtos.forEach(produto => {
console.log("produto:", produto);
// And here you can access each produto data
});
});
or, depending on you access your database:
yourRef.on("value", (snapshot) => {
snapshot.forEach((child) => {
var produto = child.val();
console.log("produto nNF", produto.nNF);
});
});
Given your JSON, you're filtering by a nested property at a known path. This is possible by specifying the path to the property in the query:
retrieveProdutos(cnpjFornecedor: string) {
this.produtos = this.db.list(this.paths.pathProduto, {
query: {
orderByChild: 'fornecedor/cnpj',
equalTo: cnpjFornecedor
}
});
}
Update: on second read you say you want to return items that have a value for nNF. In this case you can filter with:
retrieveProdutos(cnpjFornecedor: string) {
this.produtos = this.db.list(this.paths.pathProduto, {
query: {
orderByChild: 'nNF',
startAt: 0
}
});
}
You'll want to note that Firebase Database queries can only order/filter on a single property. In many cases it is possible to combine the values you want to filter on into a single (synthetic) property. For an example of this and other approaches, see my answer here: http://stackoverflow.com/questions/26700924/query-based-on-multiple-where-clauses-in-firebase

Meteor, query Collections based on ids retrieved from another collection

I have 2 collections.
ItemList = new Mongo.Collection('items');
BorrowerDetails = new Mongo.Collection('borrow');
ItemList.insert({
brand: "brand-Name",
type: "brand-Type",
._id: id
});
BorrowerDetails.insert({
key: "ItemList.id", //equals to .id of the ItemList Collection
name : "borrowerName"
});
Question !
How can i retrieve records from the BorrowerDetails Collection based on a certain type from the ItemList Collection.
ex. Retrieve all records from the BorrowerDetails Collection where key is equals to the id of a record on the ItemList Collection whose type is equals to "Desktop".
return BorrowerDetails.find(
{ key :
ItemList.find(
{ type : 'Desktop' },
{ fields: {'_id':1 } }
)
}
); //error!
Note that I don't have nodejs right now in my laptop, so might have several errors since I am not able to test it.
First, Create a publication file (eg. server\publications\borrowPub.js). inside the file, you should create a publication method. The logic here is simple, get the itemid array first, and then pass it as parameter in Mongo select $in.
Meteor.publish('queryBorrowerTypeDesktop', function(criteria)
{
var itemArr = ItemList.find( { type : 'Desktop' },
{ fields: {'_id':1 } });
if(itemArr){
//itemArr found, so do a select in using the array of item id we retrieved earlier
var borrowers = BorrowerDetails.find({ key: { $in: itemArr } });
return borrowers;
}else{
//found nothing- so return nothing
return []
}
});
Second, in the router :
Router.route('/test', {
name: 'test',
action: function()
{
//change accordingly.
},
waitOn: function()
{//subscribe from publisher that we just created...
return [
Meteor.subscribe('queryBorrowerTypeDesktop')
];
},
data: function() {
if(Meteor.userId())
{
// also include the sorting and limit so your page will not crash. change accordingly.
return {
borrowerDetails: BorrowerDetails.find({},{sort: {name: -1}, limit: 30}),
}
}
}
});
Note that in data, BorrowerDetails.find() does not need to filter by anything because it has been filtered during the subscribe, which has been cached in MiniMongo in your browser.

Categories