Im using Firestore with web javascript sdk.
Assume following scheme:
User Doc -> Friends collection
I want to know when someone change/remove/add data to it.
so what I wrote is something like this:
friendsCollectionRef.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
onChange(change);
});
});
The problem is that whenever I refresh the page, it keeps calling the onChange with data that was updated in my last session..
Is there a way to get only NEW data and not retroactively?
I would like to avoid store "LastUpdate" field on everything.
This, of course, should not be done in client side because then I pay for network which im never going to use..
So storing a boolean isFirstCall in out of the question.
As explained in the doc, when you listen to multiple documents in a collection with the onSnapshot() method:
The first query snapshot contains added events for all existing
documents that match the query. This is because you're getting a set
of changes that bring your query snapshot current with the initial
state of the query.
So each time you refresh your page you are calling again the onSnapshot() method "from scratch" and therefore you get the first query snapshot with all the collection docs.
In other words, I think you will have to implement your "home-made" mechanism to only get the documents you want (probably a "LastUpdate" field...).
You may be interested by this SO answer which shows how to add a createdAt timestamp to a Firestore document via a Cloud Function. You could easily adapt it to record the last update. It would be more complicated if you want to detect the Documents that were deleted since the last fetch.
Related
I am stuck with a very strange bug and I can't understand why it is happening.
I have a collection in the Google Firestore called previews, in the collection, I have 4 documents that I manually inserted into the Firestore with automatically generated ids.
When I try to get the documents by id, only one of them is retrievable from the JavaScript side. I've been able to reproduce the problem with the query builder of the Firestore:
You can clearly see the documents here with their ids.
When I query BEOEGqnl7wBXCB6G4RLP, it works:
But when I query any other document, I get no result, even though they do exist!
I tried to change the properties of the documents, I also thought it may be some invisible space, but I checked that too. I don't see any difference between the documents, except for the data that's in them.
Any idea of what could be wrong?
Thank you!
I have 4 documents that I manually inserted into the Firestore with automatically generated ids.
There's a high chance you added a space either at the start or end of the document ID in the data or while querying.
To confirm if the issue is with the document ID, you can open that document in panel view and check the URL. The document ID should be right after ~2F and then check for any encoded characters like %20 in this case.
Alternatively, print the document IDs with quotes:
const snap = await getDocs(collection(db, "previews"))
snap.forEach((d) => console.log(`'${d.id}'`))
I'm totally confused.. I finally got the SuiteTalk API working and am able to make authenticated calls from my JS server..
I'm calling the GET /invoice route, which documented here:
https://system.netsuite.com/help/helpcenter/en_US/APIs/REST_API_Browser/record/v1/2022.2/index.html#tag-invoice
Says that it should return to me an InvoiceCollection which is a collection of Invoices which, by the documentation, has all the fields I need as part of an Invoice.
However, the actual response I am getting back is just an array of items that only has the ID in it.. where are all the other invoice fields?!
I tried to just fire off subsequent calls to the /invoice/{id} route with each ID, but I am getting back some of them as unauthorized and some not.. assuming having to do with timestamp being to close together or something.
There has to be a way to call up to /invoice and get all of the data having to do with an invoice and not just the ID right?
I'm also wondering how I can utilize the "Q" param to filter to invoices only for a certain customer, or date, etc. Appreciate any help!
I recommend trying all these queries in postman (netsuite provides a collection for it). If you try it there, you'll see that {{REST_SERVICES}}/record/v1/invoice returns a list of id's and links to invoices.
If you dont see the details of an invoice, can that have something to do with the rights (to that subsidiary)?
e.g. GET: {{REST_SERVICES}}/record/v1/invoice/164198 shows me the entire record.
To get invoices from a certain customer i think i would go for a query in the suiteql restlet.
Hope that helps a bit. I'm just discovering suitetalk myself and it's not the easiest to get a grip of.
I am using the Firebase Cloud Firestore database with JavaScript and Node.js.
I accidentally modified a document inside collection that contained very important data.
I did this using the .update method as explained in the Update a document section of the official Firebase documentation.
The point is that since I was using test data, when modifying the document I did not update its data correctly, but rather I overwritten the original information with the test data.
Since I didn't do any kind of backup of that data, my question is ...
Is there a way to get back the original data that I overwritten using the '.update'?
Firestore does not automatically keep back ups of your data, so unless you set up an automatic export of the data yourself the previous contents of the document are lost.
If this is business critical data, you could try reaching out to Firebase support for personalized help in troubleshooting. But since you said it's test data, the better path is likely to figure out how to set up your test data automatically in a way that makes it reproducible in case of a coding mistake like this one.
I am using the Nodejs Cassandra driver and I want to be able to retrieve the previous and next pages. So far the documentation shows the retrieval of the next page, which is saving the pageState from the previous page and passing it as a parameter. Sadly there is no info on how to navigate to the previous page.
As I see it there are two options:
Save each pageState and page as a key-value pair and use the pageState for the page that you want to navigate to.
Save the retrieved data in an array and use the array to navigate to the previous page. (I don't think that this is a good solution as I'll have to store large chunks the data in the memory.)
Both methods does not seem to be an elegant solution to me, but if I have to choose I'll use the first one.
Is there any way to do this out of the box using the Nodejs Cassandra driver?
Another thing is that in the documentation the manual paging is used by calling the eachRow function. If I understand it correctly it gives you every row as soon as it is red from the database. The problem is that this is implemented in my API and I am returning the data for the current page in the HTTP response. So in order for me to do that I'll have to push each row to a custom array and then return the array when the data for the current page is retrieved. Is there a way to use execute with the manual paging as the above seems redundant?
Thanks
EDIT:
This is my data model:
CREATE TABLE store_customer_report (
store_id uuid,
segment_id uuid,
report_time timestamp,
sharder int,
customer_email text,
count int static,
first_name text,
last_name text,
PRIMARY KEY ((store_id, segment_id, report_time, sharder), customer_email)
) WITH CLUSTERING ORDER BY (customer_email ASC)
I am displaying the data in a grid, so that the user can navigate trough it.
As I write this I thought of a way to do this without needing the previous functionality, but nevertheless I think that this is a valid case and it will be great if there is an elegant solution to it.
Sadly there is no info on how to navigate to the previous page.
That is correct, when you make a query and there are more rows, Cassandra returns a paging state to fetch the next set of rows, but not the previous ones. While what the paging state represents is abstracted away, it is generally a pointer to where to continue reading the next set of data, there really isn't a concept of reading the previous set of data (because you just read it).
Save each pageState and page as a key-value pair and use the pageState for the page that you want to navigate to.
This is the strategy i'd recommend too, of course to get the paging state you actually have to make the queries.
Is there any way to do this out of the box using the Nodejs Cassandra driver?
Not that I am aware of unfortunately, if you want to go back to previous pages you are going to need to track the state.
Is there a way to use execute with the manual paging as the above seems redundant?
Yep, you can provide pageState in the options parameter with execute as well and it will be regarded, i.e.:
client.execute('SELECT * FROM table', [], {pageState: pageState}, function (err, result) {
...
});
There is an option to be "more" manual, this was common before paging features. You can store the last partition/clustering keys of the current page you are sending back in response (possibly encrypted depending on what it is, most likely generate the partition key and only send/receive the clustering keys to avoid security issues). Then when doing your "next page" response you just have to start your CQL query from there. To go back a page, change ORDER BY clause of clustering key and walk backwards. If you provide your schema it would be easier to give examples. Thats all the pageState really does anyway.
I'm trying to run a query that retrieves a specific set of users from the User table and presents them to a given logged in user, using the Parse JS SDK.
At first I was running a query like:
(new Parse.Query(Parse.Object.extends("_User"))).containedIn("objectId", [/* list of user ids */]).find(/* success & error blocks */);
but then I learned the more correct way was:
(new Parse.Query(Parse.User)).containedIn("objectId", [/* list of user ids */]).find(/* success & error blocks */);
But even with this, I would get back an empty list. I double-checked the db and made sure the ids I was passing were in there, and they were. Just for the hell of it I tried:
(new Parse.Query(Parse.User)).find(/* success & error blocks */);
And I got back the user object corresponding to the current user. So it seems my User queries are only able to access the current user. I also noticed that when I tried running an equivalent query from the Parse API Console, I got the same results! Is this some sort of global setting, or am I doing something wrong? Thanks!
I believe you have to use CloudCode due to User Security Settings in Parse. Try shifting this to a cloud code function and include
Parse.Cloud.useMasterKey()
prior to the cloudcode function. This is a security 'feature' of Parse to ensure people can't edit and access other user profiles and information.
See Parse security site here:
https://parse.com/docs/js/guide#security-object-level-access-control
and more on use and considerations of implementing useMasterKey() and its persistence.
https://parse.com/docs/js/guide#security-implementing-business-logic-in-cloud-code
I would recommend not condensing all these calls into one line for the sake of saving lines. It sacrifices readability. Although, it's technically not wrong, and the docs even say the calls return a query so you can chain calls, so to each their own.
I don't see anything wrong with your call, though. How are you accessing the retrieved objects? What is the list of objectIds that you are passing into containedIn? We need a bit more of your code here, I think.
edit - I would say I'm 80% sure, without more information, that this is an ACL / CLP issue. Go to the dashboard, hit the _User class, press Security, and see what the read/write settings are.
Looks like a CLP issue to me. Maybe Find permission on your User class is disabled. Go to your User class, tap on security, switch to advance and then check whether the Find permission is ticked off or not.