Node.JS, MONGODB, not using Mongoose.
I have a document I'm saving. When I use UPSERT, it structures the data as so :
{ "_id" : ObjectId("573a123f55e195b227e7a78d"),
"document" :
[ {"name" : "SomeName" } ]
}
When I use INSERT it inserts it all on the root level :
{ "_id" : ObjectId("573a123f55e195b227e7a78d"), "name" : "SomeName" }
This is obviously going to lead to lots of inconsistencies. I have tried various things. I've tried various methods such as findOneAndReplace, Update with Upsert, I've tried Replace, $setOnInsert. It all does the same thing when Upsert is involved it seems.
I have tried to use document[0] to access the first block of the array, but that throws an error... 'Unexpected Token ['
I have tried various methods and dug for hours through the various documentation, and have searched high and low for someone else having this problem, but it doesn't seem to be well documented issue for anyone else.
Anyone have any recommendations to make sure that all the fields are on the ROOT level, not nested under the variable name? Relevant code below.
findReplace: function(db, document, collection) {
var dbCollection = db.collection(collection);
var filter = document.name;
return new Promise(function(resolve, reject) {
dbCollection.updateOne({
"name" : filter
}, {
document
}, {
upsert: true
}, function(err, result) {
if (err) {
console.log('error', err)
reject(err);
}
console.log("Found Document and Upserted replacement Document");
resolve([{'status' : 'success'}]);
});
});
}
When you do this:
{
document
}
You are creating an object containing a document property and the variable's value as its value:
{
document: [ {"name" : "SomeName" } ]
}
This is new functionality from ES6. If you want to access the first item of the document variable, don't create a new object:
return new Promise(function(resolve, reject) {
dbCollection.updateOne({
"name" : filter
}, document[0], { // <========
upsert: true
}, function(err, result) {
if (err) {
console.log('error', err)
reject(err);
}
console.log("Found Document and Upserted replacement Document");
resolve([{'status' : 'success'}]);
});
});
Related
I think there are multiple ways to do this, and that has me a little confused as to why I can't get it to work.
I have a schema and I would like to update Notes within it but I can't seem to do it. Additionally, if I want to return the notes how would I go about doing it?
schema :
{
_id : 1234
email : me#me.com
pass : password
stock : [
{
Ticker : TSLA
Market : Nasdaq
Notes : [
"Buy at 700",
"Sell at 1000"
]
},
{
Ticker : AAPL
Market : Nasdaq
Notes : [
"Buy at 110",
"Sell at 140"
]
},
]
}
Each user has a list of stocks, and each stock has a list of notes.
Here is what I have tried in order to add a note to the list.
router.post(`/notes/add/:email/:pass/:stock/:note`, (req, res) => {
var email = req.params.email
var pass = req.params.pass
var note = req.params.note
var tempStock = req.params.stock
userModel.findOne({email: email} , (err, documents)=>{
if (err){
res.send(err);
}
else if (documents === null){
res.send('user not found');
}else if (bcrypt.compareSync(pass , documents.pass)){
userModel.findOneAndUpdate({email : email , "stock.Ticker" : tempStock}, {$push : {Notes : note}} ,(documents , err)=>{
if(err){
res.send(err);
}else {
res.send(documents.stock);
}
})
}
})
})
Thanks :)
Currently, you are pushing the new note into a newly created Notes property inside the model instead of into the Notes of the concrete stock. I am not completely aware of the mongoose semantics but you need something like this:
userModel.findOneAndUpdate({ email: email, "stock.Ticker": tempStock }, { $push: { "stock.$.Notes": note } }, (documents, err) => {
$ gives you a reference to the currently matched element from the stock array.
For the second part, I am not sure what you mean by
Additionally, if I want to return the notes how would I go about doing it?
They should be returned by default if you're not doing any projection excluding them.
Also, as per the docs(and general practise), the callback for the findOneAndUpdate has a signature of
(error, doc) => { }
instead of
(documents, err) => { }
so you should handle that.
So I am currently working on a project with mongodb and nodejs and I was wondering, how can you update data in mongodb via nodejs? My problem is that I want to keep the old data and add new. For example, here is the data currently in my mongodb
{
"_id" : ObjectId("5a1c0c1c3b147ec2e31cceb3"),
"event_id" : "1",
"event_medium" : "null",
"event_tags" : ["#JustTesting"]
}
So I want to add new data to the event_tags array and still keep the old data.
So for example the end result would be this:
{
"_id" : ObjectId("5a1c0c1c3b147ec2e31cceb3"),
"event_id" : "1",
"event_medium" : "null",
"event_tags" : ["#JustTesting", "#Test", "#Something"]
}
You should use the update function of MongoDB for that. MongoDB knows different update operators, in your case you may use $push or $pushAll (the second is deprecated):
update one after the other with $push
YourCollection.update({ _id: 'xxx' }, { $push: { event_tags: '#Test' } });
YourCollection.update({ _id: 'xxx' }, { $push: { event_tags: '#Something' } });
or both at once with $pushAll (deprecated now)
YourCollection.update({ _id: 'xxx' }, { $pushAll: { event_tags: ['#Test', '#Something'] } });
To interact with MongoDB form your NodeJS app, I would use a library like this one.
Your starting point is the Update function in CRUD (Create,Read, Update, Delete) operations in Mongodb.
Your node program should have among others the update function where you set the _id field you want to update and load the content fields to be update in 'data' for example as below:
myModel.prototype.update = function (_id, data, callback) {
const query = { _id: this.mongo.ObjectId(_id) };
debug(' update:' + JSON.stringify(query));
this.mongo.collection('mycollection').update(query, data, callback);
};
This piece of code should be put in your Model, if you use MVC pattern.
There is a lot to go.
Honestly I recommend a more deep tutorial like parts 3 and 4 of this one for nodejs and Mongoose (mongo db driver):
MDN tutorial for mongo/node/express
I assume you are using mongoose..
eventModel.findOne({event_id:1 },
function(err, eventObj){
if(err){
//handle error
} else {
if(eventObj === null) {
//event doesnot exist
}
var tagList = eventObj.event_tags;
tagList.push('new_tag1');
tagList.push('new_tag2');
eventObj.event_tags = tagList;
eventObj.save(function(err){
if(err){
//handle error
} else {
//success
}
})
I am trying to find a document in a collection. I have been using this query structure on nearly all my collections, but for some reason this isnt working for this collection. Any help?
The only data in this collection right now
[ { _id: 581757143389e565b5cf8124,
companyProfileID: '86660a5b-7f61-4238-889d-1cc3087947b9',
url: 'sentsoftware.com',
appID: 1 } ]
Query Structure:
function getCompany(companyUrl, app, callback) {
MongoClient.connect(url, function(err, db) {
if (err) {
console.log(err);
} else {
console.log("We are connected");
}
var collection = db.collection('Companies');
collection.find({url: companyUrl, appID: app}).toArray(function (err, result) {
if (err) {
console.log(err);
callback(err);
} else if (result.length) {
console.log("found");
callback(result);
} else {
console.log("No document found");
callback(err);
}
});
});
}
I keep getting No document found. But if i were to take out the , appID: app part, it finds the document.
A Mongo query is type specific (although numbers can be cast), you've likely got a string where you are expecting a number.
If you ask for the string "1",
collection.find({appID: "1"})
it will only return documents with a string equal to "1", if you ask for the number 1
collection.find({appID: 1})
it will only return documents with a real number (Double, Long, Int) in.
You can check which types you have with a $type query:
collection.find( { appID : { $type : "string" } } ); // or type 2 for earlier mongo versions
collection.find( { appID : { $type : "double" } } ); // or type 1 for earlier mongo versions
collection.find( { appID : { $type : "int" } } ); // or type 16 for earlier mongo versions
Check out the BSON types and $type in the docs, currently (2016/11/11): https://docs.mongodb.com/manual/reference/operator/query/type/#op._S_type
Interestingly it will cast between Doubles, Longs and Ints; so inserting these:
db.collection("temp").insertOne({ "type" : "my double", "num" : new mongo.Double(1.0) });
db.collection("temp").insertOne({ "type" : "my long", "num" : new mongo.Long(1) });
db.collection("temp").insertOne({ "type" : "my int", "num" : 1 });
and searching for:
{ "num" : 1 }
returns the three documents.
I noticed a strange behavior with the mongodb node.js driver findOneAndUpate()...
I mistakenly gave it just a objectId string....thinking it would default to searching by _id field of a document.... so, when I used
User.prototype.updatePetArray = function(user, petElement) {
return this.collection.findOneAndUpdate(user,
{ $push: { pets: petElement } },
{ returnOriginal: false,
maxTimeMS: QUERY_TIME});
}
it pulled up and modified this document, which does not have this number at all:
{ "_id" : ObjectId("56d4e2a381c9c28b3056f792"), "username" : "bob123", "location" : "AT", ...}
Why did it modify this document when 56d4d35881c9c28b3056f78a is not in the document?
After I test it following your code with one non-exist ObjectID,
var col = db.collection('users');
col.findOneAndUpdate('56cd129222222', {fname: 'Daved'}, function(err, r) {
if (err)
console.log(err);
else
console.log(r);
db.close();
});
As a result the first document in the collection was changed .
Per this doc
findOneAndUpdate(filter, update, options, callback)
filter: Specify an empty document { } to update the first document returned in the collection.
So I think this non-exist ObjectId is consider to the same behavior with {}
After reviewing the source code of findAndModify, eventually, the
// Execute the command
self.s.db.command(queryObject
is invoked, and the queryObject is
var queryObject = {
'findandmodify': self.s.name
, 'query': query
};
So I test runCommand in mongo shell with non-exist ObjectId as below, as result, the first document is returned.
> db.users.runCommand({findandmodify: 'users', query: '123ddae344', update: true
})
{
"lastErrorObject" : {
"updatedExisting" : true,
"n" : 1
},
"value" : {
"_id" : ObjectId("56c687275d81735019263d1f"),
"fname" : "Daved"
},
"ok" : 1
}
The docs keep saying the filter parameter should be an object.
The wrong behavior is likely to some side effect of mis-interpreting the value, being a string not an object (and maybe a truthy value, non-empty string).
A piece of my Mongo document structure is:
{ "_id": ObjectId("xxxxxx..."),
"Country" : "UNITED KINGDOM",
"Line" : "something",
"Records" : [
{"rdata" : "foo", "rtype" : "X", "name" : "John"},
{"rdata" : "bar", "rtype" : "Y", "name" : "Bill"}
], ...
I'm using Mongoose to access the data via the following model:
var Record = new Schema({
rdata: String,
rtype: String,
name: String
}, {_id: false});
var ThingSchema = new Schema({
Country: String,
Line : String,
Records : [Record],
Let's say I want to update the "Line" property of one of my documents, from being "Line" : "something" to "Line" : "way more interesting" by sending a PUT request to the appropriate API URL. I can see that the data being sent is all right. This is what the API does:
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Thing.findById(req.params.id, function (err, thing) {
if (err) { return handleError(res, err); }
if(!thing) { return res.send(404); }
var updated = _.merge(thing, req.body);
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, updated);
});
});
};
The API comes back with 200/OK - but I see the following updated data:
{ "_id": ObjectId("xxxxxx..."),
"Country" : "UNITED KINGDOM",
"Line" : "way more interesting", <-- updated correctly
"Records" : [
{"rdata" : "foo", "rtype" : "X", "name" : "John"},
{"rdata" : "foo", "rtype" : "X", "name" : "John"}
], ...
Notice, how the Records array got messed up by overwriting my second record by duplicating the first one. (If I switch on the automatic addition of '_id' to the subdocument by Mongoose, then even the "_id" fields will be the same on the two records within the array).
It may be relevant, that originally the Records were not added via Mongoose - but by importing a JSON document. Any suggestion as to how to start finding out why this is happening would be fantastic.
Try changing _.merge to _.extend, then call save directly on the thing document returned by the findById() method instead of the merged object updated:
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Thing.findById(req.params.id, function (err, thing) {
if (err) { return handleError(res, err); }
if(!thing) { return res.send(404); }
_.extend(thing, req.body);
thing.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, thing);
});
});
}
Another option is using the set method on the entity i.e. thing.set(req.body) before calling the save method on the thing object.
This answer by ShitalShah highlights the differences between merge and extend that is causing duplicates in your resulting object with merge but essentially:
Here's how extend/assign works: For each property in source, copy its
value as-is to destination. if property values themselves are objects,
there is no recursive traversal of their properties. Entire object
would be taken from source and set in to destination.
Here's how merge works: For each property in source, check if that
property is object itself. If it is then go down recursively and try
to map child object properties from source to destination. So
essentially we merge object hierarchy from source to destination.
While for extend/assign, it's simple one level copy of properties from
source to destination.
JSBin to illustrate the differences