I am trying to update a mongodb user document. It is as below
{
"_id":"123",
"email":"sam#example.com"
}
I want to add one field 'name' to this document.
My code is as below
async function test() {
const user = {"_id":"123", "email" : "sam#example.com" };
async function setUsername(user, update) {
await userCollection.updateOne(user, update);
}
await setUsername(user, { $set: { name: "sam"} });
}
test();
However, when I see in the db, I am not able to see the field set in the document.
I am sure I am missing someway how the node driver is implemented, but I am not sure of the issue.
I have even tried using upsert: true option which gave me an error as the document was already existing.
I guess I had given the function name wrong and I didn't create the document before.
posting the final snippet that works:
const { MongoClient } = require("mongodb");
async function test() {
const mclient = await MongoClient.connect("mongodb://localhost:27017/?w=1", {
useNewUrlParser: true
});
const db = mclient.db("test");
const userCollection = db.collection("user");
const user = { _id: "123", email: "sam#example.com" };
function setUsername(user, update) {
return userCollection.updateOne(user, update);
}
await setUsername(user, { $set: { name: "sam" } });
}
(async () => {
test();
})();
Related
im a total newbie in js (typescript, mongoDB, node.)
i just found that my code is not behaving as i expected, im getting 6 registers on the mongoDB instead of just one, it should check if the register exists and then update it, i dont know if it is something related to the await / async or i am doing something wrong, thanks in advace, here is my code.
fields.forEach((value) => {
try {
const mongoConnection = new DocumentDbRepository();
let checksIfExists = await mongoConnection.getValue(key, information[uniqueValue]);
if(checksIfExists==null){
let insert = await mongoConnection.insertValue(information);
console.log(insert);
}
if(checksIfExists?.passValue===information.passValue){
console.log('---------update---------');
let sons = Object.values(information.ticketToRide);
information.ticketToRide = sons;
let update = await mongoConnection.updateRegister(information, checksIfExists._id);
console.log(update);
} else {
console.log('---------insert---------');
let sons = Object.values(information.ticketToRide);
information = sons;
let insert = await mongoConnection.insertValue(information);
console.log(insert);
}
} catch (error) {
console.log(error)
}
}
async getValue(uniqueValue: any, keyValue:any) {
if (this._connection == null) {
await this.connect();
}
const db = this._connection.db(DocumentDbRepository.DbName);
const ticketToRide = db.collection("ticketToRide");
const query = {};
query[uniqueValue] = ''+keyValue+'';
const passInfo = await ticketToRide.findOne(query);
return passInfo;
}
async insertValue(information: any) {
if (this._connection == null) {
await this.connect();
}
const db = this._connection.db(DocumentDbRepository.DbName);
const ticketToRide = db.collection("ticketToRide");
let check = await ticketToRide.insertOne(
information
)
return check;
}
First, you don't need to create a connection inside the loop.
Second, mongodb has an update() or updateMany() method that has a special option { upsert: true }. If it is passed, insert will happen automatically.
Usage example:
Person.update( { name: 'Ted' }, { name: 'Ted', age : 50 }, { upsert: true })
const updateTask = async (req, res) => {
const { id } = req.params;
let update = {};
if (req.body.taskTitle) update.taskTitle = req.body.taskTitle;
if (req.body.taskContent) update.taskContent = req.body.taskContent;
if (req.body.role) update.role = req.body.role;
let task = await Task.updateOne(
{ taskId: id },
{
$set: {
update,
},
},
{ runValidators: true }
);
};
This is my code to update my data in database
as I am trying to update single single data or key if I want to update single data but it's not updating any thing i don't know where its not working as i tried to console data data come perfectly
const updateTask = async (req, res) => {
const { id } = req.params;
let update = {};
if (req.body.taskTitle) update.taskTitle = req.body.taskTitle;
if (req.body.taskContent) update.taskContent = req.body.taskContent;
if (req.body.role) update.role = req.body.role;
let task = await Task.updateOne(
{ taskId: id },
{
$set: update,
},
{ runValidators: true }
);
};
all you have to do was remove curly brackets and it will work like a charm $set : update
what the variable Task is ? And I’m sorry, but I didn’t really understand your question, so could you rephrase it more clearly ?
If not, be careful, users might inject things into your database. You should use the mongo-sanitize script.
Like that :
And check if your id is in the right form. If in your database it is not in bjson with MongoId. If this is the case, do not hesitate to convert your id as realized below.
const sanitize = require('mongo-sanitize');
const mongo = require('mongodb');
const updateTask = async (req, res) => {
const { id } = req.params;
let update = {};
if (req.body.taskTitle) update.taskTitle = sanitize(req.body.taskTitle);
if (req.body.taskContent) update.taskContent = sanitize(req.body.taskContent);
if (req.body.role) update.role = sanitize(req.body.role);
let task = await Task.updateOne(
{ taskId: mongo.ObjectId(sanitize(id)) }, // You can remove the mongo.ObjectId if your id is not a objectid.
{
$set: {
update,
},
},
{ runValidators: true }
);
};
I have these collection of items from firestore:
availability : true
stocks: 100
item: item1
I kind of wanted to decrement the stocks after submitting the form: I have these where() to compare if what the user chose is the same item from the one saved in the firestore.
function incrementCounter(collref) {
collref = firestore
.collection("items")
.doc()
.where(selectedItem, "==", selectedItem);
collref.update({
stocks: firestore.FieldValue.increment(-1),
});
}
This is how I'll submit my form and I've set the incrementCounter() after saving it:
const handleSubmit = (e) => {
e.preventDefault();
try {
const userRef = firestore.collection("users").doc(id);
const ref = userRef.set(
{
....
},
},
{ merge: true }
);
console.log(" saved");
incrementCounter();
} catch (err) {
console.log(err);
}
};
There's no error in submitting the form. However, the incrementCounter() is not working and displays this error:
TypeError: _Firebase_utils__WEBPACK_IMPORTED_MODULE_5__.firestore.collection(...).doc(...).where is not a function
There are few problems here
There should be async-await for both functions
Your fieldValue should start from firebase.firestore.FieldValue not firestoreFieldValue
Also where clause is used for collection, not doc() so remove that as well. Also I don't think this will update the full collection but do check it and see. (The error you are getting is because of this)
I don't know how you are importing firebase in this application and I don't know how you have declared firestore but mostly firestore variable is declared like this
const firestore = firebase.firestore();
In here firestore is a function, not a property
But when you are using it in the FieldValue then it should be like
firebase.firestore.FieldValue.increment(-1),
Notice that firestore here is a property not a function
Your full code should be like this
async function incrementCounter(collref) {
collref = firestore
.collection("items")
.where(selectedItem, "==", selectedItem);
const newRef = await collref.get();
for(let i in newRef.docs){
const doc = newRef.docs[i];
await doc.update({
stocks: firebase.firestore.FieldValue.increment(-1),
});
// You can also batch this
}
}
const handleSubmit = async (e) => {
e.preventDefault();
try {
const userRef = firestore.collection("users").doc(id);
const ref = await userRef.set(
{
....
},
},
{ merge: true }
);
console.log(" saved");
await incrementCounter();
} catch (err) {
console.log(err);
}
};
The where() method exists on a CollectionReference and not a DocumentReference. You also need to get references to those documents first so first get all the matching documents and then update all of them using Promise.all() or Batch Writes:
function incrementCounter() {
// not param required ^^
const collref = firestore
.collection("items")
// .doc() <-- remove this
.where(selectedItem, "==", selectedItem);
// ^^^ ^^^
// doc field field value
// "item" {selectedItemName}
collRef.get().then(async (qSnap) => {
const updates = []
qSnap.docs.forEach((doc) => {
updates.push(doc.ref.update({ stocks: firebase.firestore.FieldValue.increment(-1) }))
})
await Promise.all(updates)
})
}
If you are updating less than 500 documents, consider using batch writes to make sure all updates either fail or pass:
collRef.get().then(async (qSnap) => {
const batch = firestore.batch()
qSnap.docs.forEach((doc) => {
batch.update(doc.ref, { stocks: firebase.firestore.FieldValue.increment(-1) })
})
await batch.commit()
})
You can read more about batch writes in the documentation
So I'm sending data properly to mongo and data (user input information), which is correctly held in backend. In console I'm getting interceptor that tells me that data is received from Mongo DB, but how to properly get those properties (user's email, title of photo and url blob) or 'data'? So it can be seen as individual data (email, title...) and not as the whole object like it can be seen in console now.
--THIS IS IN MY VUE--
dohvatiObjavu(){
this.objava = Objave.dohvati_objavu();
console.log("Current post " + this.objava);
}
},
-- THIS IS IN SERVICES--
[let Objave = {
async dohvati_objavu() {
let response = await Service.get(/galerija)
let data = response.data;
console.log("Current posts in services: "+data.naslov)
return {
id: data._id,
email: data.email,
naslov: data.naslov,
noviOpisSlike: data.noviOpisSlike,
slika: data.slikaReference,
}
},
}
--THIS IS IN BACKEND--
app.get ('/galerija', async (req , res) => {
let db = await connect();
let cursor = await db.collection('galerija').find();
let results = await cursor.toArray();
res.json(results);
});
-- MY CONSOLE--
Objave.dohvati_objavu(); is an async function. So you should also await this inside your Vue method dohvatiObjavu().
I created a simplified working example, based on your code:
const Objave = {
dohvati_objavu: async function() {
// mock Service.get(/galerija) with Promise.resolve
const data = await Promise.resolve({
id: 'mockId',
email: 'mockEmail',
naslov: 'mockNaslov',
noviOpisSlike: 'mockNoviOpisSlike',
slika: 'mockSlika',
});
return {
id: data._id,
email: data.email,
naslov: data.naslov,
noviOpisSlike: data.noviOpisSlike,
slika: data.slikaReference
}
}
}
const MyVueComponent = class {
objava = undefined;
// DOES NOT WORK
dohvatiObjavu() {
this.objava = Objave.dohvati_objavu();
console.log("[dohvatiObjavu] Current post ", this.objava);
}
// WORKS
async dohvatiObjavu2() {
this.objava = await Objave.dohvati_objavu(); // <!-- await
console.log("[dohvatiObjavu2] Current post ", this.objava);
}
}
const component = new MyVueComponent()
component.dohvatiObjavu();
component.dohvatiObjavu2();
Hi all thanks for looking to my question,
I would like to delete a child referenced in a parent
here is the structure:
const parentSchema: = new Schema({
name: String,
child: { type: mongoose.Schema.Types.ObjectId, ref:'Child' },
})
const childSchema: = new Schema({
name: String,
})
the child is saved to its own child collection and the parent contains its reference.
my approach for this is as follow:
parentSchema.statics.deleteByID = async function (id: string) {
try {
const parent = await this.findOne({ id })
const child = await Child.findOne({_id: parent.child })
const childDel = child && await child.remove()
const parentDel = await parent.remove()
console.log(parent, child, childDel, parentDel)
} catch(err) {
throw new Error(err)
}
}
this works just fine, i wanted to know if this is the best approach.
I don't think if mongoose has this feature built-in.
The best you can do is to create a remove middleware as described here:
By the way, to make your existing code shorter, you can use findByIdAndDelete. It returns the deleted document, so with the following code 2 database hits make the job:
const parentDel = await Parent.findByIdAndDelete(id);
const childDel = await Child.deleteOne({_id: parentDel.child});
console.log(parentDel, childDel);
parentDel will look like this:
{
"_id": "5de0114ad068f335b480925a",
"name": "Parent 1",
"child": "5de01144d068f335b4809259",
"__v": 0
}
And childDel will look like this:
{
"n": 1,
"ok": 1,
"deletedCount": 1
}
I think this is the best approach for my problem, hope it helps anyone.
My problem was in thinking that the pre('remove') hook would be triggering on Class call, but it is called only on instance.
So rather than Parent.deleteOne(), i first find the instance i want to delete with findOne(), and then trigger the pre('remove') with parent.remove(), and delete necessary childs...
Here is an example:
parentSchema.pre<ParentDocument>('remove', async function() {
try {
if (this.child01) {
await Child01.deleteOne({ _id: this.child01 })
}
if (this.child02) {
await Child02.deleteOne({ _id: this.child02 })
}
} catch(err) {
throw new Error(err)
}
})
parentSchema.statics.deleteByID = async function (id: string) {
try {
const parent = await this.findOne({ id })
return !!(parent && await parent.remove())
} catch(err) {
throw new Error(err)
}
}