How to extract data from a nested map in firestore? - javascript

I have a collection users and it has multiple documents. Inside each document, there is a field called contacts. It is of map type. Inside contacts, I have another map-type data.
My database:
I am trying to store my contacts data in contactDetailsArr array like:
[{userId: GA3yfqLaaTaDugSrFOQujnj34Y13,
lastMessage:"Here there!!!",
time: t {seconds: 1663220632, nanoseconds: 36000000}
},
{userId: TZjQb8yoYfQbowloQk1uLRCCPck1,
lastMessage:"How are you?",
time:t {seconds: 1663306634, nanoseconds: 859000000}
}]
but I am getting my array as
[{userId: GA3yfqLaaTaDugSrFOQujnj34Y13,
lastMessage:undefined,
time:undefined
},
{userId: TZjQb8yoYfQbowloQk1uLRCCPck1,
lastMessage:undefined,
time:undefined
}]
Here is my code:
export const getUserContacts= () =>{
const contactDetailsArr=[];
db.collection("users").doc(userId).get() //userId is equal to "3aTGU..."
.then(docs=>{
const contactsObject= docs.data().contacts;
for(let contact in contactsObject){
contactDetailsArr.push({userId: contact,
lastMessage: contact.lastMsg,
time: contact.lastMsgTime
})
}
})
.catch(err=>{
console.log(err);
})
}
If maps are objects then why I am able to extract data as we do in the case of objects.
Please guide what I am doing wrong.

If you log the values in your for loop, it is easy to see the problem:
for(let contact in contactsObject){
console.log(contact);
}
This logs:
0
1
So you need to still look up the object at the index:
for(let i in contactsObject){
console.log(contactsObject[i]);
}
Or with a forEach list comprehension:
contactsObject.forEach((contact) => {
console.log(contact, contact.userId, contact.lastMessage, contact.time);
}

Related

How to get access to the PromiseResult in React when calling Azure Cosmos DB api

I'm trying to pull some rows from a Cosmos DB and then map through them to display a component for each. All of the documentation I can find for the Azure Cosmos DB API logs rows to the console one by one but I can't find one then tells you how to return the whole string.
I'm new to this so am probably doing it horribly wrong but I am now stuck and cannot move on. Hope you can help ...
In my App.js I have this
function App() {
const [newMembers, setNewMembers] = useState(dataFetch());
In my dataFetch.js I have
export default async function dataFetch() {
const { endpoint, key, databaseId, containerId } = config;
const client = new CosmosClient({ endpoint, key });
const database = client.database(databaseId);
const container = database.container(containerId);
// Make sure Tasks database is already setup. If not, create it.
// await dbContext.create(client, databaseId, containerId);
try {
console.log(`Querying container: Items`);
// query to return all items
const querySpec = {
query: "SELECT * from c",
};
// read all items in the Items container
const { resources: items } = await container.items
.query(querySpec)
.fetchAll();
return items;
} catch (err) {
console.log(err.message);
}
}
When I console.log the result back it says
Promise {<pending>}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Array(3)
0: {id: '154', forename: 'Fred', surname: 'Robson', address1: 'Some address', address2: 'Kingsmead', …}
1: {id: '416', forename: 'Lee', surname: 'Robson', address1: 'Some address', address2: 'Kingsmead', …}
2: {id: '900', forename: 'Kathryn', surname: 'Robson', address1: 'Some address', address2: 'Kingsmead', …}
length: 3
[[Prototype]]: Array(0)
I saw something elsewhere about using .then but when I tried
const { resources: items } = await container.items
.query(querySpec)
.fetchAll()
.then(() => {
return items;
});
It said "dataFetch.js:33 Cannot access 'items' before initialization"
What you're doing in the first two code blocks is perfectly fine. However with putting the result of dataFetch() into the newMembers state, you're just storing the promise in there which just get's resolved at some point and you can retrieve the results at any point with newMembers.then(actualResult => ...).
However, I think you would rather like to keep the actual members array in the newMembers state. This can be done by e.g.:
function App() {
const [newMembers, setNewMembers] = useState([]); // initially set to an empty array
useEffect(() => {
dataFetch().then(members => setMembers(members));
}, []); // providing an empty array means: run this effect only once when this component is created.
// do something with the newMembers. Initially, it will be []
// but as soon as the data is retrieved from the DB, the actual data will be
// in the state and the component will be rerendered.
}

Using variable query operators only when they are not empty

Let's say that on a movie website you can set filters for the movies that will be shown to you.
As far as I am now these preferences are saved to the User model as a map. This map might look like the following object:
preferences: {
yearFrom: "2000"
yearTo: "2020"
actors: ["Denzel Washington", "Tom Hanks", "Morgan Freeman"]
}
Before the movies are shown to you, a view controller wil first get the current user:
const user = await User.findById(req.user.id);
Then get the preferences map from the user: (It's a map so I made it into into a string first before converting it to an object)
const preferencesObject = JSON.parse(JSON.stringify(user.preferences));
At this point the question arises: How to build up the query for all movies when one of the variable query operators comming from the preferencesObject might be empty? So far I've got:
const preferedMovies = await Movies.find({
year: { $gte: preferencesObject.yearFrom, $lte: preferencesObject.yearTo },
actor: { $in: JSON.parse(preferencesObject.actors) }
});
If actors is empty I would like to include all actors, but this point 0 movies will be shown. If yearFrom is empty I would like to default to 0. There might be many more preferences that can be set, so how can you check if a variable operator is empty and then not use it or set a default value?
You can create the query object from the preferencesObject first, then pass it to find(). Something like:
let query = {
year: { $gte: 0 } // default: 0
};
if (preferencesObject.yearFrom) query.year.$gte = preferencesObject.yearFrom;
if (preferencesObject.yearTo ) query.year.$lte= preferencesObject.yearTo ;
if (preferencesObject.actors && preferencesObject.actors.length) {
query.actor = { $in: preferencesObject.actors };
}
const preferedMovies = await Movies.find(query)...

Filtering away javascript objects into an array when using fetch

I have a react application, where I use the axios library, to get some values, and set them into an array of javascript objects in my state
componentDidMount(){
axios.get('http://localhost:8080/zoo/api/animals')
.then(res => this.setState({animals: res.data}))
}
Now I want to check if the objects, contains an Owner object, inside it, and filter out does that does,
First, I tried making a const, and then using the filter, to check if they contain the objects, and then set the state, but I can't save my values in a local variable
componentDidMount(){
const animals= [];
axios.get('http://localhost:8080/zoo/api/animals')
.then(res => animals=res.data)
console.log(animals) // logs empty array
console.log('mounted')
}
how can I make it so, that I can only get the animals that do NOT, have an owner object inside it?
Your animal array is empty in your second example because axios.get is asynchronous, what is in your then will be executed once the data is fetch, but the function will keep on going in the meantime.
To filter out your array, simply use filter right after fetching your data :
componentDidMount(){
axios.get('http://localhost:8080/zoo/api/animals')
.then(res => this.setState({animals: res.data.filter(animal => !animal.owner)}))
}
This function will filter out every animal object that does not have an owner property.
Working example :
const animals = [
{
name: 'Simba',
owner: {
some: 'stuff'
}
},
{
name: 1
}, ,
{
name: 2
}, ,
{
name: 3,
owner: {
some: 'stuff'
}
},
{
name: 'Bambi'
//status: 'dead'
}
]
console.log(animals.filter(animal => animal.owner))
EDIT: the answer was changed so that it only filters animals, that does not have an owner

Access last N documents of Google Firestore collection

I want to access last N documents from google firestore collection. The key for documents are timestamp in ISO format. The collection first needs to be sorted on timestamp keys and the last N documents updated are to be retrieved. This operation has to be realtime and fast.
events(collection) =>
documents as below:
2019-01-07T08:21:18.600Z => {
symbolname: '10900CE',
price: '10825.0',
timestamp: '2019-01-07T13:51:18Z',
quantity: '2.0',
side: 'SELL' }
2019-01-07T08:21:28.614Z => {
symbolname: '10800PE',
price: '10825.0',
timestamp: '2019-01-07T13:51:28Z',
quantity: '2.0',
side: 'BUY' }
2019-01-07T09:45:24.783Z => { side: 'SELL',
symbolname: '10800PE',
price: '10805.0',
timestamp: '2019-01-07T15:15:24Z',
quantity: '2.0' }
I currently loop through the collection and add the keys to an array and then sort the array and get last N document by those keys as following:
var ids = []
db.collection('events').get()
.then((snapshot) => {
snapshot.forEach((doc) => {
ids.push(doc.id)
});
})
.then(()=>{
//sortIds(ids);
console.log(JSON.stringify(ids[ids.length-2])) //second last entry 2019-01-07T08:21:28.614Z
})
.catch((err) => {
console.log('Error getting documents', err);
});
This operation takes a long time if collection size increases. Is there an optimal way of achieving this (sorting/orderBy-descending collection and fetch last N documents)?
Thanks
Perhaps something like this:
db.collection('events').orderBy("timestamp").limit(n)

Array of Empty Objects vs Array of Objects - Firebase and Polymer

I'm trying to use the Polymer Shop template to create an online store, replacing the standard category objects with objects from Firebase Cloud Firestore. After initializing the database, I'm trying to use the objects to show a list of categories in a drawer menu.
This top example is with Cloud Firestore. As well as the code, you can see via screenshot what the console prints out when categoryList is console logged.
Cloud Firestore Console Output
let categoryList = []
firebase.firestore().enablePersistence()
.then(function() {
// Initialize Cloud Firestore through firebase
var db = firebase.firestore();
db.collection("product-categories").where('active', '==', true)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
categoryList.push(doc.data())
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
});
Here's the code in the original Polymer Shop template, as well as a screenshot showing the output when categoryList is printed to the console.
Polymer Shop Template Console Output
(function() {
let categoryList = [
{
name: 'oils_and_tinctures',
title: 'Oils and Tinctures'
},
{
name: 'concentrates',
title: 'Concentrates'
},
{
name: 'Vape',
title: 'Vape'
},
{
name: 'topicals',
title: 'Topicals'
},
{
name: 'pet_products',
title: 'Pet Products'
}
];
It seems like I need an array of empty of objects and then to fill those objects. How do I get the data from Firebase Cloudstore to match the format of the original template data?
Thanks in advance for anyone's help!
querySnapshot.forEach(function (doc) => {
categoryList.push({ name: doc.name, title: doc.title })
}).
OR
function CategoryData(name, title) {
this.title = title;
this.name = name;
}
querySnapshot.forEach(function (doc) => {
categoryList.push(new CategoryData(doc.name, doc.title))
})
Using the second way you can define what ever structure you like.

Categories

Resources