Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added proper query serializer #75

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

AlexanderArvidsson
Copy link

@AlexanderArvidsson AlexanderArvidsson commented Dec 11, 2020

The current method of storing the query in the cache is a simple JSON.stringify and JSON.parse. This has some problems; you cannot serialize certain objects, most notably Dates. Querying for dates in firestore is impossible with the current method because it is converted into a string due to the JSON serializer.

This PR implements the base of a proper serializer, that will maintain dates in the query, allowing you to correctly query for dates in your database.
It also allows future objects to be serialized correctly, by simply adding specific logic to the serializer.

@@ -11,6 +11,7 @@ export class Fuego {
public auth: typeof firebase.auth
public functions: typeof firebase.functions
public storage: typeof firebase.storage
public DocumentReference = firebase.firestore.DocumentReference
Copy link
Author

@AlexanderArvidsson AlexanderArvidsson Dec 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I managed to fix compatibility with Firebase 8.x by placing the DocumentReference inside the Fuego class.
So, for those that use 8.x, all they need to do is add the DocumentReference to their Fuego instance.
It's not a breaking change if they don't add it, because people with a custom Fuego will still be able to use the library like usual, and their document references won't be serialized properly (which it didn't before anyway) until they add DocumentReference

Creating the document reference in my older comment is also done using fuego.db.doc instead now.
Does this seem like a good solution?

Copy link
Owner

@nandorojo nandorojo Dec 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this via the Fuego instance is ideal. It's nice to have an entry-point that manages all things compatibility. Does 8.x remove this type altogether or just rename it?

Copy link
Author

@AlexanderArvidsson AlexanderArvidsson Dec 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't remove it, but since imports are changed from import * as firebase from to import firebase from it means the version different messes things up. By providing the right firebase in Fuego, this doesn't happen.

I see Fuego as a very nice compatibility interface between different versions, and because we pass in the functions and storage that way, why not pass in firestore as well so we have access to the right variables.

*/
endBefore?: number

// THESE ARE NOT JSON SERIALIZABLE
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're at it, might be a good time to knock these out?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely yes! I'll look into that right now.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if there is a way to construct a snapshot?

Copy link
Author

@AlexanderArvidsson AlexanderArvidsson Dec 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've managed to get it working with snapshots. Instead of serializing and re-constructing the snapshot, I store it away in a map and then fetch it when deserializing.

I do not know how using multiple useCollection with the same snapshots will affect this. If it has problems, we could try changing the serializer from static to have one serializer per useCollection. Then, we can store the fuego inside the Serializer rather than passing it as a parameter, and we don't have to worry about other hooks interfering with the cache.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nandorojo Before merging, we need to investigate this above. Will it be a problem?

src/types/Query.ts Outdated Show resolved Hide resolved
src/helpers/serializer.ts Outdated Show resolved Hide resolved
@alistairholt
Copy link

What's the status on this PR? I noticed we can't use document refs in where conditions right now. This looks like it would fix it.

@nandorojo
Copy link
Owner

Hey, I've been really busy with work so I haven't had time to finish reviewing and testing it out. I'll do my best to get to it soon.

@alistairholt
Copy link

@nandorojo that would be amazing. Thank you for the extremely quick response.

@nandorojo
Copy link
Owner

You got it

@AlexanderArvidsson
Copy link
Author

As I said in one of my comments, we should investigate if the cache could cause problems when using multiple hooks at the same time before merging this.

I do not know how using multiple useCollection with the same snapshots will affect this. If it has problems, we could try changing the serializer from static to have one serializer per useCollection. Then, we can store the fuego inside the Serializer rather than passing it as a parameter, and we don't have to worry about other hooks interfering with the cache.

@nandorojo

This comment has been minimized.

@brunoalano
Copy link

While this PR don't get merged, there are alternatives to query with ReferenceDocument?

// Deserializer function for query
public static deserializeQuery<Data extends object = {}>(
queryString: string,
fuego: Fuego
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is technically a global variable, since it's set from the config step of this library. I wonder if we should just import that directly. I'll think about it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for passing the fuego is to ensure the same version of firebase is used, because there are differences in the typings. I had trouble using it with my project due to this.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should still be solved at the initialization step, with something like this: #59 (comment)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do use my own Fuego instance, but I ran into problems with DocumentReference not being available in different versions of firebase, or at least available in different locations. If you have any suggested changes to fix this then that is welcome, the sooner we can merge this the better.

@nandorojo
Copy link
Owner

@brunoalano at the moment, not that I know of

@oliver-ni
Copy link

Will this be merged?

}
// Date: Inject serializer options if not specified
if (where[2] instanceof Date && !where[3]) {
return [...where.slice(0, 3), { type: 'date' }] as WhereItem<Doc>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this turn the date into a serializable value, such as milliseconds or a string?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or I suppose JSON.stringify at the last step can handle Dates, never mind

@nandorojo
Copy link
Owner

I can give it a final look with comments I left

@nandorojo nandorojo mentioned this pull request May 9, 2021
4 tasks
@jckw
Copy link
Contributor

jckw commented Jul 14, 2021

How can I help with this PR?

@nandorojo
Copy link
Owner

You could test all the changes with real data in your app, and give input to my open comments to help push it along

jckw added a commit to Houston-App/swr-firestore that referenced this pull request Jul 14, 2021
@AlexanderArvidsson
Copy link
Author

AlexanderArvidsson commented Jul 19, 2021

How can I help with this PR?

To be honest, I do not see any reason to not merge this at this point. I've been using it in prod for a while.
The reason I guess that it has not been merged is because I have a slight worry that there may be cache conflicts. This can be solved quite easily as I have described before. However, I do not have time to do these myself unfortunately.

I do not know how using multiple useCollection with the same snapshots will affect this. If it has problems, we could try changing the serializer from static to have one serializer per useCollection. Then, we can store the fuego inside the Serializer rather than passing it as a parameter, and we don't have to worry about other hooks interfering with the cache.

However, this may only be a problem when serializing DocumentReference:

// THESE ARE NOT JSON SERIALIZABLE
// startAt?: number | DocumentSnapshot
// endAt?: number | DocumentSnapshot
// startAfter?: number | DocumentSnapshot
// endBefore?: number | DocumentSnapshot

@jckw
Copy link
Contributor

jckw commented Aug 5, 2021

Have you tried using listen: true with this branch? I think I'm having problems related to it when I use a where query (not necessarily using dates).

E.g. something like this from the examples doesn't seem to work

const { data } = useCollection('users', {
  where: ['name', '==', 'fernando'],
  limit: 10,
  orderBy: ['age', 'desc'],
  listen: true,
})

@jckw
Copy link
Contributor

jckw commented Aug 5, 2021

In fact, on further inspection, this looks like a more general problem with just using a where filter that uses the == operator.

Might be related to #77.

@jckw
Copy link
Contributor

jckw commented Aug 5, 2021

My bad. This looks like it was due to a missing index. The error for this isn't surfaced anywhere though, and would be passed as the second arg to this querySnapshot-handling function.

const data: Doc[] = await ref.get().then(querySnapshot => {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants