I'm trying to extract a nested dictionary from a DataSnapshot.
var query = admin.database().ref("meetings").orderByChild("deadline");
query.once("value").then((snapshot) =>
{
snapshot.forEach((child) =>
{
console.log(snapshot.val());
}
}
This code prints this:
{
'82BE0F33-6812-4EFF-8CB4-FABDACA2B329':
{
deadline: 1509634558,
name: 'cfedfgh',
users:
{
'8YUDtUJuIde8fLaHCfeuSNsHUyq2': '1',
DVAYNspxcGfB001RSkq3S8cxvsH3: '1',
fJXgRJBoFAU0SmA2Zhn4DpdyLGh1: '1'
}
}
}
How would I go about creating an array of dictionary users values? Or access them at all.
Doing console.log(child.child("users").val()); gives me a null.
Database structure:
Using DataSnapshot#child() will give you another DataSnapshot for the location at the specified relative path. Therefore, a DataSnapshot with children is considered a parent node so doesn't have a specific value, so val() won't work here.
In order to convert your users value into any array, you'll need to iterate over each child underneath it and pass the key into an array, so something like this:
snapshot.forEach((child) => {
let users = [];
if (child.child("users").exists()) {
child.child("users").forEach((userSnapshot) => {
users.push(userSnapshot.key);
});
}
console.log(users);
}
Assuming snapshot is an array of objects, you can use the following. This will give you all of the users for each record.
const users = [].concat.apply([], snapshot.map(row => {
return Object.keys(row).map(key => {
return row[key].users
})
}))
console.log(users)
Related
Note: This is not a duplicate question, please read till the end and see the included image.
I have a nested object and an array field inside my collection/document in Firestore.
Main categories
Drinks
Snacks
Items for Drinks are
(Water, Energy, Milk, ...)
Items for Snacks are
(Chips, Biscuits, Corn, ..)
The user may subscribe to both categories for multiple items with an expiration date:
Drinks->Energy
Drinks->Milk
Snack->Chips
I want to update the [expDate] field where [name] is equal to drinks and [type] is equal to [energy]
I have explored Firestore documentation more importantly compound queries in Cloud Firestore and read so many article(s) and questions on stackeoverflow but I couldn't find my answer, below is part of my code which I tr.
db.collection(this.dbName)
.where("name", "==", "drinks")
.where("subscriptions.data.type", "==", "energy")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
let data = doc.data();
if (data.email == email) {
let subscriptions = data.subscriptions;
subscriptions.forEach((subscription) => {
if (subscription.name == productName) {
let prodTypes = subscription.data;
prodTypes.forEach((prodType) => {
if (prodType.type == itemType) {
let docRef = fb.db.collection(this.dbName).doc(email);
fb.db
.collection(this.dbName)
.doc(email)
.update(docRef, {
subscriptions: [
{
data: [
{
expDate: expiration,
type: itemType,
},
],
name: productName,
},
],
});
}
});
}
});
}
});
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
I don't get any console log result for the above query.
This query won't work:
db.collection(this.dbName)
.where("name", "==", "drinks")
.where("subscriptions.data.type", "==", "energy")
This returns documents that have a:
field name at their root with value "drinks" AND
have a field type nested under data nestedundersubscriptions**under the root** with the value"energy"`
Neither of those fields is present in the document you've shown, so the query won't return that document. If you add the fields under the root of the document, you'll see that the query returns it.
It looks like you're trying to return documents where the subscriptions array contains specific value in its items. For this you'll need to use the array-contains operator. This operation can test if an array field contains a specific complete value, like:
db.collection(this.dbName)
.where("subscriptions", "array-contains", { ... })
But here you have to specify the entire object that must exist in the array. You can't check whether one property if the item exists with a specific value, and you also can't check for a nested array as you seem to have here.
The solution, as is usually the case when dealing with NoSQL databases, is to change/augment your data model to allow the use-case. Since you want to query for documents that have a specific name and type, add top-level fields for all names and types that exist in this document:
{
names: ["drinks", "snacks"],
types: ["energy", "water"]
subscriptions: [
...
]
}
Now you can use (one of) the new top-level fields in your query:
db.collection(this.dbName)
.where("names", "array-contains", "drinks")
You can't add a second array-contains clause though, as a query can only have one such clause in Firestore. So you'll have to filter the types in your application code after using a query to retrieve only documents that contain drinks.
With my understanding firebase compound queries are not working for this kind of cases, I resolved the issue by changing the object with javascript map helper and update the document with return data of the function (changeExpDate), as follow:
changeExpDate(data, name, type, expDate) {
data = [data];
data.map((obj) => {
let subscriptions = obj.subscriptions;
for (var i = 0; i < subscriptions.length; i++) {
let items = obj.subscriptions[i].data;
for (var j = 0; j < items.length; j++) {
if (obj.subscriptions[i].name == name) {
if (items[j].type == type) {
obj.subscriptions[i].data[j].expDate = expDate;
}
}
}
}
});
return data;
}
By considering your conditions, got with following code:
let v = this.changeExpDate(
data, //The document objcet
productName, //Product Name
itemType, //Item type
expiration //Expiration date
);
try {
fb.db
.collection(this.dbName)
.doc(email)
.update(v[0])
.then(function () {
console.log("Updated!");
})
.catch(function (error) {
console.error("Error Updating: ", error);
});
} catch (error) {
console.error(error);
}
This is my ObjectIds array -
obj_ids = [
"5ee71cc94be8d0180c1b63db",
"5ee71c884be8d0180c1b63d9",
"5ee71c494be8d0180c1b63d6",
"5ee71bfd4be8d0180c1b63d4"
]
I am using these objectids to serach whether they exist in the db or not and based on that I want to send the response to server.
This is the code I am trying but I dont know how to populate the array and send it to the server.
var msg = [];
obj_ids.map((ele) => {
Lead.find({ _id: ele._id }, async function (error, docs) {
if (docs.length) {
msg.push(
`Lead already exist for Lead id - ${ele._id} assgined to ${docs[0].salesPerson}`
);
} else {
msg.push(`Lead doesn't exist for Lead id: ${ele._id}`);
const newDuty = new AssignedDuty({
duty: ele._id,
salesPerson: req.body.salesPerson,
});
await newDuty.save();
}
});
});
res.json(msg);
By doing this approach I am getting an empty array. I cannot put res.json(msg) inside the loop. If it is possible by using async-await, please guide me through.
You don't need to make multiple queries to find whether given object ids exist in the database.
Using $in operator, you can make one query that will return all the documents where the _id is equal to one of the object id in the list.
const docs = await Lead.find({
_id: {
$in: [
"5ee71cc94be8d0180c1b63db",
"5ee71c884be8d0180c1b63d9",
"5ee71c494be8d0180c1b63d6",
"5ee71bfd4be8d0180c1b63d4"
]
}
});
After this query, you can check which object id is present in the docs array and which is absent.
For details on $in operator, see $in comparison operator
Your code can be simplified as shown below:
const obj_ids = [
"5ee71cc94be8d0180c1b63db",
"5ee71c884be8d0180c1b63d9",
"5ee71c494be8d0180c1b63d6",
"5ee71bfd4be8d0180c1b63d4"
];
const docs = await Lead.find({
_id: { $in: obj_ids }
});
const msg = [];
obj_ids.forEach(async (id) => {
const doc = docs.find(d => d._id == id);
if (doc) {
msg.push(
`Lead already exist for Lead id - ${doc._id} assgined to ${doc.salesPerson}`
);
}
else {
msg.push(`Lead doesn't exist for Lead id: ${id}`);
const newDuty = new AssignedDuty({
duty: id,
salesPerson: req.body.salesPerson
});
await newDuty.save();
}
});
res.json(msg);
How to add value to a specific object to the array by the index?
I wrote this, but of course, it creates a new object in the array, but I want to insert "errors" to an existing object with index (on screen it 0 index)
ipcRenderer.on('fileData', (event, data) => {
this.setState({jobs: [...this.state.jobs, {errors: data}]})
});
Then i wrote this:
ipcRenderer.on('fileData', (event, data) => {
this.state.jobs.forEach((item, index) => {
this.setState({jobs: [...this.state.jobs, {errors: item[index] = data}]
})
console.log(this.state)
})
});
It inserts a value into the object, but without a name and it still creates a new element in the array
I want the result to be like this:
jobs: [
0: {errors: 10, fileName:...}
]
If you know the index, you can just do
const jobs = this.state.jobs.slice(0);
jobs[index].errors = data;
this.setState({jobs});
Might have to do more than slice the array, might have to make a deep copy, but yeah, that should work.
Firstly you can make a copy of your array like
let jobsCopy = this.state.jobs
Then if you know the index you could just do like
jobsCopy[index].errors = 10
this.setState({
jobs: jobsCopy
})
You would need to know the index of the object you want to change. For example if you know it is the first item in the array you can do this:
const indexToChange = 0
this.setState(prevState => prevState.map((obj, i) => {
if(i === indexToChange) {
return {
...obj,
errors: data
}
} else {
return obj
}
}))
I have an API that I am calling to return a query. This query's format cannot be changed to be easier to manipulate. It has a nested array within it that I need to associate with the data from the higher levels.
Specifically, I am trying to pull the higher level id field and and the "value" field within "column_values" and associate them with one another preferably within a new array. I feel like the answer is here but I just can't grasp how to pull the data in the correct format and associate it together. Most of the comment lines can probably be ignored, they are my other attempts at making the syntax work correctly. Sorry about the mess. I'm really new to this.
const axios = require('axios')
const body = {
query: ` query {boards(ids:307027197) {name, items {name id column_values(ids:lockbox_) {title id value text}}}} `,
}
console.log("Requesting Query....");
function getApi (callback){
setTimeout(function() {axios.post(`https://api.monday.com/v2`, body, {
headers: {
MY_API_KEY_DATA
},
})
.catch(err => {
console.error(err.data)
})
.then(res => {
var queried = res
var array = queried.data.data.boards[0].items
//console.log(queried)
//console.log(array)
console.log(array.length)
//console.log("Total Items:", array.length)
var i;
for (i = 0; i < array.length; i++){
callback(queried.data.data.boards[0].items)
//callback([(queried.data.data.boards[0].items[i].column_values[0])])
}
}, 0);
})
};
getApi(callback => {
console.log(callback)
//console.log(parsed)
//output for above
//{"name":"address","id":"1234","column_values":
//[{"title":"Lockbox#","id":"lockbox_","value":"\"31368720\"","text":"31368720"}]}
//console.log(JSON.parse(parsed))
//output for above
//[
// {
// name: 'address',
// id: '353428429',
// column_values: [ [Object] ]
// }
//]
});
setTimeout(function() {
console.log("Query Returned")},1000);
From your data, column_values is an array with objects in it. For an array, you will have to access it with the key. For your case, if your data is like
var data = {
"name":"address",
"id":"1234",
"column_values": [{"title":"Lockbox#","id":"lockbox_","value":"\"31368720\"","text":"31368720"}]
}
You can access the id of column_values as data.column_values[0].id
I cannot seem to find an answer on here that is relevant to this scenario.
I have my state in my React component:
this.state = {
clubs: [
{
teamId: null,
teamName: null,
teamCrest: null,
gamesPlayed: []
}
]
}
I receive some data through API request and I update only some of the state, like this:
this.setState((currentState) => {
return {
clubs: currentState.clubs.concat([{
teamId: team.id,
teamName: team.shortName,
teamCrest: team.crestUrl
}]),
}
});
Later on I want to modify the state value of one of the properties values - the gamesPlayed value.
How do I go about doing this?
If I apply the same method as above it just adds extra objects in to the array, I can't seem to target that specific objects property.
I am aiming to maintain the objects in the clubs array, but modify the gamesPlayed property.
Essentially I want to do something like:
clubs: currentState.clubs[ index ].gamesPlayed = 'something';
But this doesn't work and I am not sure why.
Cus you are using concat() function which add new item in array.
You can use findIndex to find the index in the array of the objects and replace it as required:
Solution:
this.setState((currentState) => {
var foundIndex = currentState.clubs.findIndex(x => x.id == team.id);
currentState.clubs[foundIndex] = team;
return clubs: currentState.clubs
});
I would change how your state is structured. As teamId is unique in the array, I would change it to an object.
clubs = {
teamId: {
teamName,
teamCrest,
gamesPlayed
}
}
You can then update your state like this:
addClub(team) {
this.setState(prevState => ({
clubs: {
[team.id]: {
teamName: team.shortName,
teamCrest: teamCrestUrl
},
...prevState.clubs
}
}));
}
updateClub(teamId, gamesPlayed) {
this.setState(prevState => ({
clubs: {
[teamId]: {
...prevState.clubs[teamId],
gamesPlayed: gamesPlayed
},
...prevState.clubs
}
}));
}
This avoids having to find through the array for the team. You can just select it from the object.
You can convert it back into an array as needed, like this:
Object.keys(clubs).map(key => ({
teamId: key,
...teams[key]
}))
The way I approach this is JSON.parse && JSON.stringify to make a deep copy of the part of state I want to change, make the changes with that copy and update the state from there.
The only drawback from using JSON is that you do not copy functions and references, keep that in mind.
For your example, to modify the gamesPlayed property:
let newClubs = JSON.parse(JSON.stringify(this.state.clubs))
newClubs.find(x => x.id === team.id).gamesPlayed.concat([gamesPlayedData])
this.setState({clubs: newClubs})
I am assuming you want to append new gamesPlayedData each time from your API where you are given a team.id along with that data.