firestore snapshot of certain documents - javascript

Any suggestions on how to get a snapshot to only return certain
documents in a collection?
db.collection('locations-data').get().then(snapshot => {
setupLocations(snapshot.docs);
The JS setupLocations function loops through and adds data to an ul/li on the UI.It Works but it loads every document and I only want a specific document based on a users access listed in a user collection.
In my "locations-data" collection I have documents named by location (e.g. ca1001, ca1002, etc.)
And a user collection > userID > locations array to set which locations a user can view.
As mentioned above, The Setup works great on the front-end but loads all documents and I only want certain users to get certain locations (documents) to show in the UI.
I get the locations array from the users collection into a var, and have tried using map and other things on the code side, also tried a few things via security rules, etc. Just not having much luck.
Here's the code on the UI side that loads the display for the user.
// show locations
const setupLocations = (data) => {
if (data.length) {
let html = '';
data.forEach((doc) => {
const location = doc.data();
const li = `
<li>
<div class="collapsible-header grey lighten-4">
${location.shortName}, Total Income: ${location.todayIncome}
</div>
<div class="collapsible-body white">${location.totalIncome}</div>
</li>
`;
html += li;
});
locationList.innerHTML = html;
} else {
locationList.innerHTML = '<h5 class="center-align">Login to view data</h5>';
}
};

I ended up using "array-contains" and just added a users field to each document I want to include. .where('_users', 'array-contains', user.email). Will just have to create a simple admin to manage users this way to avoid having to add all users to each document manually. Thanks all for suggestions.

Any suggestions on how to get a snapshot to only return certain documents in a collection?
Since your first option is to get all document based on the users location you can use what #pepe said, the where in query filter to get a specific or certain documents inside your collection. The other option is store all the data of location from user collection to a array variable then display it directly to your UI. With that you don't need to query for location-data collection because you already got all the specific location data based on the user's data.

Related

How to sort and display data from 2 documents in Firebase in one console.log?

I'm trying to fetch info from 2 collections and display them together using JavaScript Query.
Collection users contains email entry and collection invited contains code and email. I am trying to list each email from users that also present in invited and if so, also display code.
The logic behind that I use right now is to
let x = db.collection('users').orderBy('email', 'desc').get()
let y = db.collection('invited').orderBy('email', 'desc').get()
for (const invitedEmail of y.docs()) {
if (x.docs().email === y.docs().email) {
console.log('${x.docs().email} and ${y.docs().code}')
}
}
I know that there is a better algorithm, however, I am new to JS and I am not sure how to solve this.

How to assign a collection or document to a specific user in firebase?

Hi I'm learning how to use firebase and now I'm trying to make something like a school managment system using html, css, javascript and firebase.
This is how I add homeworks
const formHomework = document.querySelector('#formHW');
formHomework.addEventListener('submit', (e) => {
e.preventDefault();
db.collection('university').doc('offers').collection('list').add({
nameHW: formHW['nameHW'].value,
nameOffer: formHW ['nameOffer'].value,
nameTopic: formHW['nameTopic'].value,
purpHW: formHW['purpHW'].value,
descriptionHW: formHW['descriptionHW'].value,
knowField: formHW['knowField'].value,
date: formHW['date'].value
}).then(()=>{
formHW.reset();
window.alert("Homework added");
})
})
I already know how to create documents and collections by submitting a form
What I want to do now is:
for the teachers to create content and homeworks for their own classes only
I'm clueless as to how achieve this, any help is gladly appreciate
This is typically done with a collection for all users that hold documents for each user named by their auth UID. this typically contains their respective sub-collections and any meta properties you want to associate with that user such as roles, permissions, tags, etc.
You can also extend this to also associate the homework with a class ID, so all users are able to query for any homework that contains a property classID with the ID and sort by date. This does mean each user will need a list of classes they are in and the respective class ID's

How to create a sharable url containing dynamic html (javascript)

What is the best practice to create unique shareable urls for some text lists users create?
It's a single page website with a content div where users create text lists. Once they click share, how can I store those values inside a shareable url so that another user going to that address loads the same list?
I'm using html, js, jquery, php.
EDIT: as suggested below i'm already saving the lists on a database (firebase), and each have an unique ID, so I'd need to understand how I can create urls with a list id in it, and how to read the url back.
EDIT 2: so this is the code i'm using right now, combining answers from marzelin and the Alchemist Shahed in my other question about my database structure (Firebase how to find child knowing its id but not its parent's id (js)):
//js inside window load function:
const keyOfDynamicHtmlItemRef = new URL(window.location).searchParams.get("share")
if (keyOfDynamicHtmlItemRef) {
var dynamicHtmlListRef = firebase.database().ref('users');
// var dynamicHtmlItemRef = dynamicHtmlListRef.child(keyOfDynamicHtmlItemRef);
// console.log(keyOfDynamicHtmlItemRef);
// dynamicHtmlItemRef.once("value").then(dynamicHtmlSnap => {
// texta.innerHTML = dynamicHtmlSnap.val();
// });
dynamicHtmlListRef.once('value').then((snapshot)=>{
snapshot.forEach(function(data) {
if (data.key == keyOfDynamicHtmlItemRef) {
myVar = data.c;
myContentDiv.innerHTML = myVar;
}
});
});
}
and i'm simply trying to manually write the url in the searchbar as a first step, as https://example.com/?share=<random list id i copied from db>, but it does nothing.
So the way I would to this is I would have the users share click trigger a save to database saving all the dynamically generated content into a table.
One of the table values would be a randomly generated unique identifier of some sort that I would use as a query in the url like https://www.example.org/?share=skd822475
Then when a user visits the site and that query is in the url id use the unique identifier to look up the database and publish the dynamic content back on the page.
I would also put a half life on the database entry's of say no more than 30 days so that it doesn't clog up the db.
Saving data and creating shareable link:
document.querySelector(".share").addEventListener("click" => {
var dynamicHtmlListRef = firebase.database().ref('dynamic_html');
var dynamicHtmlItemRef = dynamicHtmlListRef.push();
dynamicHtmlItemRef.set(userCreatedDynamicHtml);
var keyOfDynamicHtmlItem = dynamicHtmlItemRef.key;
var linkToDynamicHtmlItem = `${window.location}?share=${keyofDynamicHtmlItem}`;
alert(`link: ${linkToDynamicHtmlItem}`)
})
Showing the dynamic HTML based on query parameters:
const keyOfDynamicHtmlItemRef = new URL(window.location).searchParams.get("share")
if (keyOfDynamicHtmlItemRef) {
var dynamicHtmlListRef = firebase.database().ref('dynamic_html');
var dynamicHtmlItemRef = dynamicHtmlListRef.child(keyOfDynamicHtmlItemRef);
keyOfDynamicHtmlItemRef.once("value").then(dynamicHtmlSnap => {
document.querySelector(".dynamic-html-mountpoint").innerHTML = dynamicHtmlSnap.val();
});
}
Let's start with the first question "How to create urls with a list id in it?"
The thing is that to answer this one we need to answer the second question first witch is
"How to read the url back?"
Consider that you have a php page named "draft". when a user visit https://www.example.com/draft?listId=an_id you will get listId using php like so $_GET("listId") and use that value to retrieve the list data and display the page content.
Now coming back to the first question, if the user share the draft like in social media (ex: facebook) then there is no problem because he will share a link and all his followers and any other user can access it easily. but if the user just save the draft then you will have to change the page url dynamically like this window.history.pushState(null, null, '/draft?listId=your_newly_created_id'); and so the user will copy the url and do whatever he wnt with it (sharing it in stackoverflow maybe example using jsfiddle http://jsfiddle.net/F2es9/ (you can change the url to look like this using 'htaccess' file)) at the end I would like to tell you that we don't "create" urls.
Edit
without using php code (or any other server side code). the difference will be in retrieving the data.
instead of using $_GET("listId") you will use new URL(window.location).searchParams.get("listId") to get the list id in javascript then using this value you can retrieve data from firebase and display your content

Retrieve count data from Firebase like MySQL

When trying to pull data from a MySQL database, I can do something like:
SELECT * FROM users ORDER BY id WHERE vehicle = car
That should get me all the users that drives a car and not show users that drives a motorcycle for instance.
Is there something like this for Firebase? I can only retrieve specific data from one user?
My firebase database is like this: user -> user info (name, age, vehicle etc..)
I want to query every user that drives a car and display them in a row. How do I do that?
I have tried the following, but I didn't succeed with what I tried to do, since after users the users id is the next child. Is there a way to query past that?
var recents = firebase.database().child('users').orderByChild('department').equalTo(department);
recents.on('child_added', function(snapshot) {
var countOfUserInDepartment = snapshot.count;
document.querySelector("#cphCount").innerHTML = countOfUserInDepartment;
});
There are no count queries (nor other aggregation queries) in the Firebase Database. Your options are:
Retrieve all data matching your query and count client-side
Keep a separate count-node that you update whenever you add/remove items.
For #2 you may find it convenient to use Cloud Functions, for which there an an example of keeping such a counter.
Also see:
Firebase count group by
Database-style Queries with Firebase
In Firebase, is there a way to get the number of children of a node without loading all the node data?
How to get size of an element/list in Firebase without get it all?

Firebase get all values of specific key

I have a users table on Firebase and each user has an email prop.
Structure looks like:
Users -> User UID (looks like n8haBbjgablobA2ranfuabu3aaaga2af) -> User Obj which includes email prop.
I'd like to get an array of all the users' emails (~1m).
How can I most efficiently do this?
Ps.:
I tried:
usersRef.startAt(0).endAt(20).once("value", function(snapshot) {
console.log('FIRST 20');
console.log(snapshot.val()); // null
});
But that fails.
Probably the most efficient approach in terms of data reads would be to denormalize your data. You could store the email addresses both in the individual user nodes and in an emailAddresses node. Then you could just query the emailAddresses node directly for your list of emails.
Still ~1m email address nodes would probably be too much all at once. I'd probably grab it in chunks... I'm guessing.
Update
"Grabbing in chunks" is essentially pagination. I would try to use something off the shelf before trying to roll my own pagination solution.
Pagination libraries to check out:
Firebase Utils Pagination: This is developed by Firebase, but they say it is experimental and not ready for production use. But, it's probably still worth messing around with.
firebase-paginator: this is developed by a community member and it seems pretty solid.
If you want to roll your own pagination, check out:
#kato's response in this StackOverflow answer He makes an interesting point about the potential problem with paginating a real time data set, but then provides some good starter code
Here's a good blog entry that talks about the code that I think is a part of the firebase-paginator library I linked to above
Everybody in their answers said that it was an easy thing, yet had no working solutions. Here's what I came up with:
usersRef.orderByChild('uid').limitToFirst(100).once('value', function (snapshot) {
var users = snapshot.val()
var uids = Object.keys(users);
var lastUid = uids[uids.length - 1];
// could be another property than uid, for example email, or username. Ps.: Consider that the last ID from the previous chunk will be duplicated.
usersRef.orderByChild('uid').startAt(lastUid).limitToFirst(100).once('value', function (snapshot) {
var users = snapshot.val()
var uids = Object.keys(users);
console.log(uids);
var lastUid = uids[uids.length - 1];
// re-run function until done
})
})
Since this is a one-time deal, an option is to simply iterate over each node in the parent 'data' node capturing the child data, stripping out the email address and dumping that to a file.
the event you want is
child_added: retrieve lists of items or listen for additions to a list
of items. This event is triggered once for each existing child and
then again every time a new child is added to the specified path. The
listener is passed a snapshot containing the new child's data.
and the code to iterate all of the child nodes in the data node is
var dataRef = firebase.database().ref('myRootRef/data');
datRef.on('child_added', function(data) {
//data.val() will contain the child data, such as the email address
//append it to a text file here (for example), save to disk etc.
});
The key here is that this event is triggered once for each child, similar to iterating over all of the indexes in an array.
This will retrieve each child and present it to your app, one child at a time, iterating over all the children within the node.
It's going to take a while with that many nodes to chew through.

Categories

Resources