FireStore - LimitToLast is not a function - javascript

I've been trying to make a prevButton for a pagination, using firestore and trying to use limitToLast().
When I run the code I get an error saying limitToLast is not a function.
Uncaught TypeError: db.collection(...).orderBy(...).endBefore(...).limitToLast is not a function
My code:
prevButton.addEventListener('click', () => {
db.collection('posts')
.orderBy('likes', 'asc')
.endBefore(firstDocument) // <-- The first document on view
.limitToLast(3) // <-- LimitToLast is not a function ???
.onSnapshot((snapshot) => loadDocuments(snapshot));
});
If I remove limitToLast() my code runs without error, but of course it doesn't work as intended.
Gods of stackoverflow, what am I doing wrong here?

This is the code I made based on Frank's answer, in case someone finds it useful.
prevButton.addEventListener('click', () => {
db.collection('posts')
.orderBy('likes', 'desc') // Reverse the sort order
.startAfter(firstDoc) // First doc in view
.limit(postPorPagina) // Limit the results
.onSnapshot((snapshot) => {
const documents = snapshot.docs.reverse(); // Reverse the documents again
renderDocuments(documents); // Function that renders my documents on screen.
});
});

limitToLast is a method on the Firebase Realtime Database API. You are accessing Cloud Firestore, which has a separate (though sometimes similar) API.
The only method to limit results on Firestore is limit(...). If you want the same behavior as limitToLast from Realtime Database, you'll have to:
Reverse the sort order
Limit to the correct number of results
Reverse the results client-side

Related

Getting values from single Firebase Firestore Document

This is definitely a newbie question, and in part answered in the Firebase documentation, but for the life of me it's not working when implementing it in my own code - so I'm hoping the community can help me understand what I am doing wrong, and how to fix it.
When getting documents from Firestore, I can't access the actual values within, due to its structure, so when setting e.g. "var name = doc.name" it just gives me undefined. For getting MULTIPLE documents, I've already found apiece of code that works:
// Getting the document
docRef.collection(collectionRef).get()
.then((snapshots) => cleanData(snapshots))
.then((items) => items.map((item) => sampleFunction(item)));
// Firebase Utility cleaning documents (array)
function cleanData(snapshots) {
let data = [];
snapshots.forEach(function(doc) {
data.push({
id: doc.id,
...doc.data()
});
});
return data;
}
But when using this piece of code with e.g. collection("x").doc("id"), then it throws the error:
"Uncaught (in promise) TypeError: snapshots.forEach is not a function"
So I went ahead to modify the function as follows:
// Firebase Utility cleaning document (single)
function cleanDoc(snap) {
let data = [];
data.push({
id: doc.id,
...doc.data()
});
return data;
}
But that gives me "undefined" when attempting to access the values in my function again...
The documentation (in the city example) says to define a class. When I did that, I was able to get values from one document, but it gave me undefined the second time I called the same function on one page.
For context, I'm trying to display a User Profile, which displays people they work with on a project, which means I call these profiles as well, the data structure just callsa reference to the "worked with" profiles, and I get their ID's just fine, but when attempting to render an HTML item for each, the values within their profiles are undefined....Its confusing the hell out of me anyways.
If your function is an async function:
collectionSnap = await docRef.collection(collectionRef).get();
val items=[]
await Promise.all(querySnap.docs.map(async (doc) => {
// Do your your work and populate items
}));
// Do your work with items
You can try this approach to processing your documents.

How to access snapshot of Firebase database within Firebase Function?

Every hour, I want my firebase function to look through my database, read a value, calculate a new value from this old value, and then update it in the database. I am having trouble accessing a snapshot of the data. Specificically,
exports.scheduledFunction = functions.pubsub.schedule('every 1 hour').onRun((context) => {
const ref = functions.database.ref('/users/test_user/commutes');
ref.once('value',function(snapshot) {
// do new calculation here
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
return null;
});
I am getting a : functions: TypeError: ref.once is not a function error.
How do I access a value from my firebase real time database and update it from a Firebase function?
You're trying to use the firebase-functions SDK to query the database. It can't do that. You will have to use the Firebase Admin SDK to make the query.
You will need to get started like this (not complete, but you should be able to see what you need to do). Import and initialize at the global scope:
const admin = require('firebase-admin')
admin.initializeApp()
Then in your function, use it. Be sure to work with promises correctly.
const ref = admin.database().ref('...')
return ref.once('value').then(snapshot => {
// work with the snapshot here, and return another promise
// that resolves after all your updates are complete
})
The firebase-functions is different from the client side. The ref() function according to the docs:
ref: function
ref(path: string): RefBuilder
Select Firebase Realtime Database Reference to listen to.
Path of the database to listen to.
Returns RefBuilder
The RefBuilder will contain the database triggers that you can call, onCreate(), onWrite(). To be able to use your database, then you need to use the admin sdk.
https://firebase.google.com/docs/reference/functions/providers_database_.refbuilder

Firebase onSnapshot() listener... Can I somehow get data from before change

I have a collection in a firebase. I'm listening to changes with onSnapshot method.
If any item from a collection is changed in any way listener is fired. I am getting new data, which was inserted into my collection with docChanges method
db.collection('collection')
.onSnapshot((snapshot) => {
snapshot.docChanges().forEach((change) => {
const payload = {
id: change.doc.id,
data: change.doc.data(),
};
...... some action
});
});
and now... I need to compare new data (which was just inserted) with old data (from before insert) and I am wondering if there is any way to do that?
The Firestore SDK doesn't give you any ways to detect the differences between the "before" and "after" states of a document's fields. You will have to remember both snapshots and look for differences in the fields yourself. You might want to do a search for some ideas on how to go about this, for example: Generic deep diff between two objects

Why is sequelize metata undefined on raw query update?

I am using sequelize to run a raw MSSQL update query. The documentation for raw update queries states that the message for number of rows updated will be contained in the metadata. In the following function both results and metadata are undefined on update statements. It works perfectly fine if I use a normal select statement.
Documentation
The section in reference is the very first example.
Could someone help me understand what I am doing incorrectly?
var query = 'UPDATE contacts SET aolid = 1 WHERE contactid = 804748'
connection.query(query).spread(([results, metadata]) => {
console.log(metadata) // will log undefined
console.log(results) // will log undefined
})
Either use
.then(([results, metadata]) =>
or
.spread((results, metadata) =>
but .spread(([results, metadata]) => will not work. See spread().

Firestore - get specific fields from document

What I need:
I want to save articles or notes in Firestore with their respective fields:
Title
Content (texts or paragraphs)
Creation date
Owners (to share that article with other
people and who can edit them like: https://firebase.google.com/docs/firestore/solutions/role-based-access)
But when I show the list of articles I don't need the "content" field (to save bandwidth). I've read that (maybe I'm wrong), it is not possible to make a query to get only specific fields from a document with Firestore.
If it were normal SQL to obtain specific columns from articles (without its content) It would be something like:
SELECT title, creation_date, ...
FROM table_name;
So I've opted to separate the content for two root-level collections (for flexibility and scalability)
My current structure:
Articles collection:
- `articles` [collection]
- `ARTICLE_ID` [document]
- `creatorId` [field]
- `title` [field]
- `date` [field]
- `owners` [obj field]
- {user1_id}: true
- {user2_id}: true
...
Contents collection:
- `contents` [collection]
- `{ARTICLE_ID}` [document]
- `content` [field]
To get articles list in realtime:
firebase.firestore().collection('articles')
.where(`owners.${user.uid}`, '==', true)
.onSnapshot(querySnapshot => {
const articles = []
querySnapshot.forEach((doc) => {
articles.push({
id: doc.id,
...doc.data()
})
})
// do something with articles array
})
To show in another view and get the entire article with its content:
const db = firebase.firestore()
const articleRef = db.collection('articles').doc(articleId)
const contentRef = db.collection('contents').doc(articleId) // same Id as article
articleRef.get().then(articleDoc => {
if (articleDoc.exists) {
contentRef.get().then(contentDoc => {
if (contentDoc.exists) {
const article = {
...articleDoc.data(),
...contentDoc.data()
}
// full article obj
}
})
}
})
My questions
Do you think it's better to do two queries (getArticle and getContent) at the same time and wait with Promise.all() instead of nesting the querys like I do?
Is there a better way to get the article and its content with one query or more efficiently? Some tips or ideas?
Thank you very much in advance!
According to the Firestore Query.select documentation you should be able to select the fields you want.
let collectionRef = firestore.collection('col');
let documentRef = collectionRef.doc('doc');
return documentRef.set({x:10, y:5}).then(() => {
return collectionRef.where('x', '>', 5).select('y').get();
}).then((res) => {
console.log(`y is ${res.docs[0].get('y')}.`);
});
Neither approach is pertinently better than the other. But there are a few key differences.
When you nest the reads, the second read only starts after the first read has completed. When you use Promise.all() both reads start at the same time, so can (partially) run in parallel.
On the other hand: when you use Promise.all() your completion handler (the code you run in then()) won't execute until both documents have loaded. If you nest the calls, you can update the UI after just the first document has loaded.
In the end, the differences are likely to be small. But since they may be significant to your use-case, measure the results and see what works best for you.
In order to output a single field from a Firestore document (version 9) - for example the 'title' in the articles collection you can use the following code snippet:
const q = query(collection(db, 'articles'))
let results = [];
await getDocs(q);
results = getLocation.docs.map((doc) => doc.data()['title']);
results.sort()
The results array will contain only the title field, sorted alphabetically
(Note you have to reference the Firestore db and import 'getDocs', 'query' and 'collection' modules from Firestore)
Firebase Hosting would be your best bet for static content such as articles. If you look at AMP-HTML for example, they strongly make the case for ultra-fast page loads and highlight benefits of edge caching. Firebase hosting is advertised to also support global edge caching.
Firestore and Firebase Realtime Database are database engines. These are not the proper tool for serving up articles.

Categories