Through Flutter I want to add a Map element in Firestore.
Instead of fetching the whole map and replacing it with the added element is it possible to directly add a new key and value?
Example:
Map = { '2020-05-21' : true } -- Add --> Map = { '2020-05-21' : true, '2020-05-22': true }
This is how I tried to do this but without any success.
return collection.document('id').updateData({'map.$date' : true});
Use this approach:
final ref = Firestore.instance.document('collection_id/document_id');
await ref.updateData({
'field.key2': 'value2',
});
Since I am not having an idea about how your data looks like on server, I tested this one and it worked.
Edit:
// initial work
var ref = Firestore.instance.document('Employees/54G...Naf');
var initialData = {
'date_1': '1',
};
await ref.setData({
'busy_map': initialData,
});
// updated work
var updatedData = {
'busy_map.date_2': '2',
};
await ref.updateData(updatedData);
Related
Currently, I'm trying to create a Discord bot that will save a to-do list in a database. To do so, I wanted to use an array that contains any items the person adds by typing !listadd (something) that will then be stored in a MongoDB database. However, while this seems to work for Strings, I cannot seem to change an array at all using this method.
await list.findOneAndUpdate({_id: id},{$push:{todoList: msg}, itemToAdd:msg},{upsert : true})
This line above is what I'm using to update the list. While the change to itemToAdd is reflected perfectly in the database, nothing is pushed to todoList. I can't even seem to update todoList at all past setting what the default value is in the schema. (Like, if I put {todoList:['one','two']} as the update object, it still won't do anything)
I've tried using the .updateOne() method and, again, it works fine for itemToAdd, but nothing happens to todoList. I've also tried using the .markModified() method and I've either used it wrong or it isn't working here because, still, nothing changes in the database.
Full Code:
const mongoose = require('mongoose');
const todoSchema = new mongoose.Schema({
todolist : [String],
_id: {
type : String,
required : true
},
itemToAdd:{
type: String,
required: false
},
indexOfItemToRemove:{
type: Number,
required: false
}
});
const list = mongoose.model('list', todoSchema);
module.exports = async (client) => {
client.on('message', async (message) => {
const { author } = message;
const { id } = author;
const { bot } = author;
if (!bot){
console.log('AUTHOR:', author);
if (message.content.includes('!listadd ')){
const msg = message.content.substring(9)
await list.findOneAndUpdate({_id: id},{$push:{todoList: msg}, itemToAdd:msg},{upsert : true})
message.reply('Entry Added!')
}
if (message.content.includes('!listget')){
let doc = await list.findOne({_id:id})
message.reply(doc.itemToAdd)
}
}
})
}
I've been through the different topics related to my issue but couldn't fix it.
My firebase cloud function with the onCreate trigger does not deploy. I get this error : Failed to load function definition from source: Failed to generate manifest from function source: SyntaxError: await is only valid in async functions and the top level bodies of modules
// add action to users when they have the objective already added
exports.addActionToUserWhenCreated = functions.firestore
.document('actions/{documentUid}')
.onCreate(async (snap, context) => {
// get the objectives
let actionObjectives = snap.data().objectives
let actionId = snap.id
let objSelectedBy = []
// For each objective, get the selectedBy field
actionObjectives.forEach(objective => {
const docSnap = await db.doc(`objectives/${objective}`).get()
objSelectedBy = docSnap.data().selectedBy
objSelectedBy.forEach(user => {
// Add the actionId to the user's selectedActions
db.doc(`users/${user}/selectedActions/${actionId}`).set({
achievedRate: 0,
})
})
// Add all the userIds to the action's selectedBy field
db.doc(`actions/${actionId}`).update({
selectedBy: objSelectedBy,
}, {merge: true});
})
return;
});
Do you see the problem?
Thanks!
Max
Got this sorted out but it took me some time and precious help from Puff!
First issue here : the forEach => await cannot be used in the forEach but it works with a for...of loop.
Second : Promise.all after the await => Original answer from Puff here.
The working case of this function is therefore :
To make it understandable, you can replace action with product and objective with category. It would be as if you want to automatically add a newly created product to every users that are following a specific category.
exports.addActionToUserWhenCreated = functions.firestore
.document('actions/{documentUid}')
.onCreate(async (snap, context) => {
// get the objectives
let actionObjectives = snap.data().objectives
let actionId = snap.id
let actionSelectedBy = []
// For each objective,
for (const objective of actionObjectives) {
// get the selectedBy field (array of userIds)
const snap = await db.doc(`objectives/${objective}`).get()
const objSelectedBy = snap.data().selectedBy;
console.log("objSelectedBy",objSelectedBy)
// Add the actionId to the user's selectedActions
Promise.all(objSelectedBy.map(user => {
return db.doc(`users/${user}/selectedActions/${actionId}`).set({
achievedRate: 0,
})
}))
// Add all the objectives' userIds to the action's selectedBy field (removing duplicate):
actionSelectedBy = [...new Set([...actionSelectedBy ,...objSelectedBy])]
}
// Update the action's selectedBy field :
db.doc(`actions/${actionId}`).update({
selectedBy: actionSelectedBy,
}, {merge: true});
});
I am baffled by this post method, it will update fields 'x and y', but any attempt to set an array of widgets fails.
It is finding the correct item to update, passing all the required information through, but it will not allow insertion of, or update to 'widgets' fields.
Even if I remove the data intended for widgets and arbitrarily send through 'foo' it will not update with a field 'widgets'.
What am I doing wrong here???
API Call to Update Widgets. The Arbitrary X and Y values will update on the database, but any attempt to update widget makes no change
const saveUpdatedWidgets = async (update, _id) => {
console.log("called to update widgets ",update.widgets," in pagecard saveUpdatedWidgets")
let widgetObject = []
for(let u=0;u<update.widgets.length;u++){
widgetObject.push({
id: update.widgets[u].id,
text: update.widgets[u].text
})
}
Api.withToken().post('/pagewidget/'+_id,
{widgets: widgetObject, x:250, y:250}
).then(function (response) {
console.log("?worked ",response.data)
}).catch(function (error) {
console.log("page save failed for some reason on pagecard: ",error.response);
});
};
This will return the following in the console:
Code for post method is:
//THIS ROUTER WILL NOT UPDATE ANY WIDGETS FOR SOME REASON
router.post('/pagewidget/:_id',auth, async(req,res)=>{
console.log("request to update ",req.body," for id ",req.params," in pagewidgetsave post")
const query = { "_id": req.params };
const addedWidgets = req.body;
const newValues = { $set: addedWidgets }
try {
const thePage = await Pages.updateOne( query, newValues);
res.status(201).send(thePage)
console.log("updated Page: ",thePage);
}
catch(e){
console.log(e);
res.status(400).send(e)
}
})
Results from the console running node shows that values are going through, but only x and y actually update in database..
Here is the axios api.js file if there are any issues here:
import axios from 'axios';
const baseURL = process.env.REACT_APP_BASE_URL || "http://localhost:3001"
export default {
noToken() {
return axios.create({
baseURL: baseURL
});
},
withToken() {
const tokenStr = window.sessionStorage.getItem("token")
return axios.create({
baseURL: baseURL,
headers: {"Authorization" : `Bearer ${tokenStr}`}
});
}
}
What is going on!!?? It finds the page OK, and updates x and y values, but can't update widgets, even if the values for widget are just a string or number...
I found the issue. the MongoDB documentation doesn't mention this too well, and in its examples for updateOne() it passes an object for the update argument.
BUT, if you are setting a new field, this argument must be wrapped inside an array to use $set, this is because it can accept both methods to $set and to $unset. (see mongoDB docs)
(i.e. updateOne({query} , [{$set: {field:"value"}, {$unset: {otherfield:"othervalue"}])
In the end the post method just had to change to const thePage = await Pages.updateOne( query, [newValues]); (with newValues stored as an object inside an array, to allow addition of $unset if it was needed.
This is why it would update existing values OK, but it would not set new values into the database.
What a journey....
Full code for post method here
router.post('/pagewidget/:_id',auth, async(req,res)=>{
const query = {"_id": req.params._id};
const addedWidgets = req.body;
const newValues = { $set: addedWidgets }
try {
const thePage = await Pages.updateOne( query, [newValues]);
res.status(201).send(thePage)
console.log("updated Page: ",thePage);
}
catch(e){
console.log(e);
res.status(400).send(e)
}
})
I am trying to update a value in an array, i tried doing it this way:
Mongoose, update values in array of objects
But it doesn't seem to work for me:
let myLessonDB = await myLessonSchema.findById(req.params.id);
myLessonDB.update(
{ 'lessons.lesson': req.body.lessonID },
{ $set: { 'lessons.$.present': true } }
);
myLessonDB return this:
{"_id":"5eafff00726616772ca852e2",
"lessons":[{"level":"1","present":false,"lesson":"5eb00211154ac86dc8459d6f"}],
"__v":0}
I am trying to change a value in lessons by the lesson ID as shown but it doesn't work.
No errors or anything it looks like it cant find the object in the array
Anyone know what I am doing wrong?
let myLessonDB = await myLessonSchema.findById(req.params.id);
myLessonDB.lessons.map(oneLes => {
if(oneLes.lesson == req.body.lessonID){
oneLes.present = true;
}
})
myLessonDB.save().then( finalLesson => {
console.log(finalLesson);
})
I'm actually building a simple application in meteor that's taking a weapon's skin thought my steam inventory, parse the weapon's market hash name, and finally return the median price of the skin according to the steam market. My problem is about storing the price of the skin, I use this npm package that I have included in Meteor (I followed the asynchronous way) and I can easily get the skin's price. I put this code into a Meteor method on the server-side, like this :
Meteor.methods({
getPrice:function(weapon,skin,state,loteryRef)
{
var csgomarket = Meteor.npmRequire('csgo-market');
var Q = Meteor.npmRequire('q');
var wears = [state];
var data = {
prices : [{
weapon : weapon,
cached : false,
skins : [skin],
skinData : {},
price : '',
}]
}
var getPrice = function(theData) {
var promises = [];
theData.skins.forEach(function(skin) {
theData.skinData[skin] = {};
wears.forEach(function(wear) {
promises.push(csgomarket.getSinglePriceAsync(theData.weapon, skin, wear, false).then(function(data) {
theData.price = data.median_price;
}));
});
});
return Q.allSettled(promises).then(function() {
return theData;
});
}
getPrice(data.prices[0]).then(function(results) {
console.log(results.price);
});
}
});
The result of the promise's function getPrice(data.prices[0]).then(function(results) {}); can only be console.logged and I can't save results outside of this function. When I try to perform an insert in this function for saving this value, it doesn't work too. What I'm doing wrong ?
Please forgive me for my quite good English