How to get collections and docs from Firestore Database? - database

In my firestore database,
there is a users list contains information for name, email
I want to read data and display it...
const admin = require('firebase-admin');
const serviceAccount = require("C:/Users/santo/Downloads/sample.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
let fs = admin.firestore();
const santosh = await fs.collection('users').doc('ANCyBKH2yeh3njsiDJ6vegFUr2').get();
console.table([santosh.data()]);
but I need all docs data i.e., I need the email and name of all the docs
Hwo can I get it?

You just need to fetch all the documents in users collection and iterate through the snapshot.
const db = admin.firestore();
(async function () {
const snapshot = await db.collection("users").get();
for (const doc of snapshot.docs) {
console.log(doc.id, "=>", doc.data().name, doc.data().email);
}
})();

Related

Problems with Snapshot for Firebase, need workaround

I am working on react project and am new to using firebase. Basically I am doing a query to get user's data and I want to reference it in the html but it does not let me because I am using a snapshot. I can't set data from the snapshot of the query to a global variable because I don't think it exists out of the snapshot. Is there a way to get the query's data outside of snapshot?
let usersname = "";
const myFunction = async () => {
let user = window.sessionStorage.getItem("user");
const q = query(accountsCollectionRef, where("username", "==", user));
onSnapshot(q, (snapshot) => {
let queryData = [];
snapshot.docs.forEach((doc) => {
queryData.push({ ...doc.data(), id: doc.id })
})
usersname = queryData[0].name;
})
}
myFunction();
console.log(usersname);
You need to check are response exits, this is how you do it.
onSnapshot(q, snapshot => {
if(!snapshot.empty) {
usersname = snapshot.docs[0].data().name
} else {
usersname = ""
}
})
I just shortened the thing you did in your function. Check are it work, most important is if(snapshot.exists()) this is way you check are response have data. Remember to unsubscribe snapshot.

hardhat tasks don't persist data on local network

I create a little NFT marketplace using solidity and hardhat. I have a JSON file with the NFT details and I wrote a hardhat task for automating the process.
task("populate-market", "populate market with nfts").setAction(async function (
taskArguments,
hre
) {
const [owner] = await hre.ethers.getSigners();
const Market = await hre.ethers.getContractFactory("NFTMarket");
const market = await Market.deploy(owner.address);
await market.deployed();
const marketAddress = market.address;
/* deploy the NFT contract */
const Item = await hre.ethers.getContractFactory("Item");
const nft = await Item.deploy(marketAddress);
await nft.deployed();
for (const item of nfts) {
const transaction = await nft.createToken(item.url);
const tx = await transaction.wait();
const event = tx.events[0];
const tokenId = event.args[2].toNumber();
const price = hre.ethers.utils.parseUnits(item.price.toString(), "ether");
await market.createMarketItem(nft.address, tokenId, price, item.supply);
}
console.log(await market.fetchMarketItems());
console.log("done!");
});
the problem it's when I load the data in my react app; I created an function getNfts() like this:
useEffect(() => getNfts(), []);
const getNfts = async () => {
const provider = new ethers.providers.JsonRpcProvider();
const nftContract = new ethers.Contract(NFT_ADDRESS, NFT.abi, provider);
const marketContract = new ethers.Contract(
MARKET_ADDRESS,
Market.abi,
provider
);
const data = await marketContract.fetchMarketItems();
console.log(data);
};
in this function data it returns empty array but, in task the console.log(await market.fetchMarketItems()); it returns all nfts. I don't understand why in the task it returns data but, in react it shows me an empty array. How to fix this?
Try to do this:
async function getNfts() {
// The rest of the code goes here
}
Basically use a function instead a variable asigned to a function. Sometimes there are errors with the this keyword that are not being passed correctly and it breaks the functionality of the library.

Firebase firestore WEB v.9 retrieve data ID-s from collection where that current user ID contains

I am trying to retrieve specific data from firebase, in my redux store I have uniq id that I can get in any page like this
const currentUser = useSelector(selectLoggedInUser);
console.log(currentUser.id) // "71dc954d-d2a4-4892-8257-98696fe776cd" this is peace of doc name in "dms" collection
I want all doc-s that contains this ID "71dc954d-d2a4-4892-8257-98696fe776cd", how can I query it???
This is how I'm setting "dms" messages
export const sentDirectMsg = async ({ toUid, currentUid, message, name }) => {
const collecitonRef = await dmCollection(toUid, currentUid);
await addDoc(collecitonRef, {
timestamp: serverTimestamp(),
message,
name,
});
};
const dmCollection = async (toUid, currentUid) => {
const idPair = [currentUid, toUid].sort().join("_");
return collection(db, "dms", idPair, "messages");
};
I'm not enough clear sorry for that(just don't have enough experience), I'll try my best.
I'm trying to create Slack like app(I have many pages and function that I exporting from one place to another), I will show how I implement the channels messages sent & get from firebase, then explain how I make direct messages
//Function that sent message to exact channelId /channels/someChannelId/messages
// channelId is literal with dynamic id
export const sentMsg = async ({ name, message, channelId }) => {
await addDoc(collection(db, "channels", channelId, "messages"), {
timestamp: serverTimestamp(),
message,
name,
});
};
//Getting data from channel
const messagesRef = query(
collection(db, `channels/${channelId}/messages`),
orderBy("timestamp")
);
onSnapshot(messagesRef, (snapshot) => {
setMessages(snapshot.docs);
});
Now as I need DM I can't make it same way because it need some privacy, only 2 person should see the messages, so I need 2 uniq person that has uniq id and their collection of messages also is uniq(so that only they can see each other messages),in general when I register the users in my app I also save with them uniq ID for example this "71dc954d-d2a4-4892-8257-98696fe776cd",
//This is how I sent direct messages
// toUid - to whom I should sent
// currentUid - is who is sent
const sentDirectMsg = async ({
toUid,
currentUid,
message,
name,
}) => {
const collecitonRef = await dmCollection(toUid, currentUid);
await addDoc(collecitonRef, {
timestamp: serverTimestamp(),
message,
name,
});
};
const dmCollection = async (toUid, currentUid) => {
const idPair = [currentUid, toUid].sort().join("_");
return collection(db, "dms", idPair, "messages");
};
// As I'm sorting this two uniq ID-s from where person sent-s the message it is always same collection reference. My question is can I somehow by "query" or by "where" get all docs that contains current user ID???
Edited:
If I understood correctly, you want to get a document which id contains a part of the id you are looking for.
Using array-contains should do the trick:
const dmsRef = collection(db,"dms");
const docRef = query(dmsRef, where("idPair", "array-contains", id)); //for example id = "71dc954d-d2a4-4892-8257-98696fe776cd"
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Document data:", docSnap.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
I based my example on this link from the official documentation.
If your data model is centered toward users being identified through their unique IDs, then you can store your data first hand to reflect his model:
const userData = {
name: 'Marouane',
state: 'BN',
country: 'TUN'
};
// Add the user document in collection `dms`, with the id being the user ID
const res = await db.collection('dms').doc('71dc954d-d2a4-4892-8257-98696fe776cd').set(userData);
You can then query the user document using its unique identifier:
Firebase v8
const userRef = db.collection('dms').doc('71dc954d-d2a4-4892-8257-98696fe776cd');
const doc = await userRef();
if (!doc.exists) {
console.log('No such user!');
} else {
console.log('User dms data:', doc.data());
}
EDIT (Added firebase v9 - modular):
import { getFirestore, ref, onValue } from "firebase/firestore";
const db = getFirestore(firebaseApp);
const userRef = ref(db, 'dms/71dc954d-d2a4-4892-8257-98696fe776cd');
onValue(userRef, (snapshot) => {
const data = snapshot.val();
console.log(data);
});
In case your document id is not known in advance, you can query for all available documents and filter out ones that does not match your user id:
import { getFirestore, collection, query, where, getDocs } from "firebase/firestore";
const db = getFirestore(firebaseApp);
const q = query(collection(db, "dms"));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
if (doc.id.startsWith('71dc954d-d2a4-4892-8257-98696fe776cd')) {
console.log(doc.data());
}
});
Meanwhile, this approach will cause great performance degradation and would be better traded with a storage re-design.

Is this the correct way for storing a sub-collection?

I have a users collection and from there I wanted to have a sub-collection about the vaccination info of the specific user. This is how I save the sub-collection inside the user's collections.
const handleSubmit = (e) => {
e.preventDefault();
try {
const userRef = firestore
.collection("users")
.doc(uid)
.collection("vaccination")
.doc();
const ref = userRef.set(
{
vaccineType,
firstDoseDate,
secDoseDate,
firstVaccinator,
secondVaccinator,
},
{ merge: true }
);
console.log(" saved");
} catch (err) {
console.log(err);
}
};
This is what it looks like in firestore:
This is the correct way to create a subcollection, but note that you do not need the merge option here. merge is used when you are updating an existing document and don't want to fully overwrite it. In your case, you're not passing an existing subcollection doc id so set will create a new document and there's nothing to merge.
You also need to await the set function as it returns a promise that you need to await for it to resolve.
// This creates a new document and you don't need {merge: true}
const newDocRef = firestore
.collection("users")
.doc(uid)
.collection("vaccination")
.doc();
const newDoc = await newDocRef.set({
vaccineType,
firstDoseDate,
secDoseDate,
firstVaccinator,
secondVaccinator,
});
// This updates an existing document, in which case you might want
// {merge: true} if you don't want to overwrite the entire document
// but just want to write/update the fields in the set call.
const existingDocRef = firestore
.collection("users")
.doc(uid)
.collection("vaccination")
.doc(vaccinationDocId);
const updatedExistingDoc = await existingDocRef.set({
vaccineType,
firstDoseDate,
secDoseDate,
firstVaccinator,
secondVaccinator,
}, { merge: true });

How can I test my react js + firestore project if I didn't use cloud functions?

I'm learning full stack developement and I've just I finished my first react js + firestore project.
I want write some unit tests, but the only samples I found on the internet are testing firestore rules or testing cloud functions. I want to test for example my queries whether they return the expected value.
export async function doesUsernameExist(username) {
const result = await firebase
.firestore()
.collection('users')
.where('username', '==', username)
.get();
return result.docs.map((user) => user.data().length > 0);
}
I want to test this query with a fake data, but how can I make a fake data? To be more exact, I need a data structure I can query on.
Thank you in advance!
I solved my problem. I used firebase emulator. In my case the solution is:
instead of
const firebase = Firebase.initializeApp(config);
use this config
const firebase = require('#firebase/testing')
const db = firebase.initializeTestApp({ projectId: MY_PRIJECT_ID }).firestore()
async function doesUsernameExist(username) {
const result = await db
.collection('users')
.where('username', '==', username)
.get();
return result.docs.map((user) => user.data().length > 0);
}
describe('Testing username', () => {
it('Username does not exsitst in the database', async () => {
const username = "peter"
const res = await doesUsernameExist(username)
expect(res.length).toEqual(0)
})
})

Resources