3
votes

Interroger Firestore pour les éléments avec horodatage avant aujourd'hui

Dans le nouveau Cloud Firestore, vous pouvez définir un horodatage du serveur sur mon objet message en utilisant la ligne suivante lors de la création de l'objet:

  var query = firebase.firestore()
                  .collection('messages')
                  .where('owner','==',userID)
                  .where('showtime','<',timenow)
                  .orderBy('showtime', 'desc')
                  .limit(25);

Ceci est utile pour les messages de n'importe quel utilisateur, comme il le fait ne comptez pas sur l'horloge locale de chaque utilisateur.

Le défi que je rencontre consiste à interroger et à définir le champ showtime par la suite. Ce que je veux faire, c'est rechercher tout message dont l'heure de diffusion est antérieure à «maintenant». Dans mon application, de nombreux messages sont poussés dans le futur. Je veux seulement que ceux avec une heure de spectacle avant aujourd'hui soient renvoyés et affichés.

Voici ma requête:

showtime: firebase.firestore.FieldValue.serverTimestamp()

Le défi est que je ne le fais pas savoir comment obtenir l'heure actuelle (sur le serveur) à utiliser dans la requête. Il y a un appel now () sur le type Timestamp dans Firebase, mais je ne sais pas comment l'appeler ET je ne suis pas sûr sur la base d'autres questions ici si l'horodatage Firebase correspond à l'horodatage Cloud Firestore!

La question est donc la suivante: comment définir une variable appelée timenow pour qu'elle soit l'heure actuelle sur le serveur et l'utiliser dans une requête pour extraire 25 messages avant maintenant? (en Javascript sur le client et aussi un crédit supplémentaire pour une fonction sur le serveur)

Un suivi rapide sur la question est de savoir comment mettre à jour l'horodatage du showtime pour qu'il soit une semaine plus tard que maintenant?

J'espère que nous avons des mavens Firebase / Cloud Firestore sur Stackoverflow!

** Choisir une réponse ci-dessous avec la mise en garde d'une demande de fonctionnalité: Un appel dans Firebase au serveur pour demander un horodatage actuel afin que le code client peut fonctionner sur une horloge standard. **


2 commentaires

Je crois que je peux utiliser «new Date ()» comme valeur timenow. Je ne sais pas si la création d'une nouvelle date se produit chaque fois que la requête est mise à jour? Je crois alors que je peux mettre à jour le showtime d'un message à une semaine plus tard en utilisant toDate () sur le showtime renvoyé par firestore, puis en y ajoutant une semaine en utilisant date.getDate () + 7. J'ai fait les deux dans mon code et ça semble fonctionner mais je voudrais une confirmation officielle. Je pense aussi que 'new Date ()' obtient la date actuelle du client, donc j'aurai probablement besoin d'obtenir l'heure actuelle du serveur dans cette requête à la place pour éviter que le client ne fausse la date.


Est-ce que cela peut être fait sur le client pour obtenir l'horodatage du firestore cloud? horodatage const = firebase.firestore.FieldValue.serverTimestamp ()


3 Réponses :


-1
votes

Vous avez raison serverTimestamp () est exactement pour obtenir un horodatage sur le serveur et ne pas compter sur l'horloge locale des utilisateurs. Une chose à noter est qu'en général, l'envoi d'un message et l'obtention de l'horodatage à partir de l'horloge locale d'un utilisateur seront acceptables car l'horodatage d'un message n'est pas extrêmement sensible au temps. Bien sûr, vous souhaitez savoir quand le message est envoyé, mais si cela se produit dans les 1 à 2 secondes, ce n'est pas un problème dans la plupart des cas.

Si vous effectuez une requête côté client, votre requête ne doit pas être basée sur l'heure du serveur, elle doit être basée sur le côté client. Comme il s'agit d'une requête client et non d'une requête serveur.

Voici une requête client que vous recherchez.

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp()

exports.getUserMessages = functions.https.onCall((data, context) => {
    const uid = context.auth.uid;
    const firestore = admin.firestore();
    firestore.collection('messages')
             .where('owner', '==', uid)
             .where('showtime', '<', new Date())
             .orderBy('showtime', 'desc')
             .limit(25)
             .get()
             .then(snapshot => {
                return snapshot;
             });
});

Cette requête recevra 25 messages avec un 'showtime' après l'heure actuelle sur le client.

Maintenant, si les messages doivent être extrêmement sensibles au temps et que vous avez absolument besoin que les messages soient basés sur l'horodatage du serveur, je recommande qu'au lieu de faire une requête sur le client comme ci-dessus, vous configurez une api de fonction cloud. Jetez un œil à la documentation Firebase pour appeler directement les fonctions cloud si vous ne l'avez pas encore fait.

Voici à quoi vous voudriez que votre fonction cloud ressemble:

const currentTime = new Date();
const query = firebase.firestore()
                  .collection('messages')
                  .where('owner','==',userID)
                  .where('showtime','<',currentTime)
                  .orderBy('showtime', 'desc')
                  .limit(25);


7 commentaires

Merci pour la réponse détaillée. Ce n'est pas la petite différence entre l'heure du client et l'heure du serveur qui me préoccupe. C'est qu'un client pourrait régler son horloge il y a deux semaines ou la régler à deux semaines en avant et voir les messages de cette période. Dans mon application, j'ai des messages photo dans le futur qui ne devraient pas être vus avant les dates futures réelles. Il n'y a donc pas d'option dans la requête côté client pour lui dire d'utiliser l'horodatage du serveur pour currentTime?


D'accord, je pense que vous êtes un peu confus ici. Quel client est capable de régler son horloge en avant ou en arrière? C'est un cas très étrange dont vous parlez, il existe de nombreuses façons de contourner ce que vous expliquez, mais la question de savoir quel est le but et pour quelle valeur. Je pense qu'au lieu d'essayer de contourner l'utilisation de Firebase, vous devriez travailler 24 heures sur 24 en sautant (ce qui n'est pas courant si possible de nos jours). Par exemple, vous pouvez envoyer un ping à un serveur d'horodatage pour l'heure actuelle, mais c'est piraté.


Pour répondre à votre question s'il existe un moyen d'utiliser l'horodatage du serveur pour l'heure actuelle, non, il n'y en a pas. Vous devriez plutôt utiliser une fonction cloud. Vous pouvez enregistrer avec l'horodatage du serveur, mais vous pouvez interroger avec l'horodatage du serveur comme je l'ai montré avec une fonction cloud.


Je crois que je peux régler mon horloge MacOS à n'importe quelle heure et date. Je pense que je peux faire la même chose avec mon PC sous Windows10. Ainsi, la date renverrait l'heure et la date des horloges locales du PC ou du Mac qui pourraient être en 2014 ou 2024. La requête au serveur serait alors erronée. Dommage qu'il n'y ait pas d'appel dans Firebase pour obtenir l'heure / la date du serveur à utiliser dans les requêtes côté client. Ce serait certainement le plus efficace. Je vais regarder les fonctions et faire la recherche de message sur le serveur.


Que diriez-vous de ne rien faire, et si l'utilisateur étrange dit qu'il ne peut voir aucune donnée, ALORS vous déterminez si c'est son horloge. Ce n'est tout simplement pas un état normal (comme vous l'avez dit, une horloge dans deux semaines), et il y a de fortes chances qu'ils cassent de toute façon un tas d'autres applications.


Les utilisateurs peuvent obtenir des avantages dans de nombreuses applications en décalant leur date locale et en publiant des articles ou des commentaires. Dans la construction d'une application robuste, il est essentiel d'avoir une source de temps définitive et qui devrait être le serveur et non le client. Créer une application qui fonctionne bien comme SnapChat, Instagram ou tout autre réseau social moderne nécessite cette fonctionnalité.


On dirait que les gens manquent et que sa préoccupation est la sécurité. Alors à la question de "qui change son horloge"? La réponse est ... un utilisateur néfaste. En outre, le javascript lui-même pourrait être édité. Ne faites jamais confiance au client.



1
votes

En prolongeant l'excellente réponse d'Alex Dunlop, j'étais dans une situation similaire et cela avait du sens une fois que j'ai réalisé que Firestore fonctionne selon deux principes de conception (ou du moins, c'est mon analyse)

  1. L'accent est mis sur les requêtes simples qui vous diffusent des mises à jour en direct. Pour ce faire, les requêtes sont par conception limitées en complexité. Cela prend la charge du serveur de base de données Firestore et lui permet de diffuser (réexécuter efficacement) les requêtes rapidement.
    En raison du langage de requête limité et du peu d'opérateurs «côté serveur» ( FieldValue est l'un des rares), Google peut optimiser les requêtes sans pitié.

  2. Les développeurs ont besoin de nombreuses opérations plus complexes. Au lieu de les implémenter dans Firestore, les développeurs sont invités à créer une fonction cloud qui les rend responsables (en code et en coût) de cette complexité supplémentaire.

Si vous venez d'une conception de base de données conventionnelle comme Postgres, le manque d'expressivité des requêtes et d'opérations côté serveur est stupéfiant. Si vous pensez au cas d'utilisation de Firestore et aux principes, tout a du sens.


1 commentaires

Cela a vraiment du sens. J'ai passé de nombreuses années avec GAE et il y avait des compromis similaires auxquels je me suis habitué et apprécié une fois que je me suis ajusté. Avec Firebase, il serait très utile que le client puisse obtenir l'horodatage actuel du serveur. Il serait bon de savoir que l'horodatage Firebase et celui du Firestore étaient synchronisés et compatibles. Un appel client et des informations plus détaillées sur l'horodatage dans les documents n'affecteraient pas les performances des requêtes et aideraient énormément!



0
votes

Essayez les règles de sécurité Firestore avec validation des données:

  match /messages/{msgId} {
    // optionally: request.time + someBuffer
    allow read: if request.time > resource.data.showtime;
  }

Je ne pense pas que vous vouliez faire confiance au client (puisque vous avez mentionné que vous vouliez bloquer l'accès du client aux futures séances). En plus de changer leur horloge, ils pourraient simplement modifier le javascript.

Un tampon pourrait être nécessaire pour ne pas invalider la requête s'il y a une différence entre le client fourni Date.now () et Règles Firestore request.time , séparément si la request.time est antérieure à la date du client, alors la requête aurait des documents hors de la plage valide et échouerait.


2 commentaires

Je pense que c'est génial. Le point que vous faites valoir que le JS pourrait être piraté est bien compris. J'essaierai probablement de déplacer la publication sur le serveur, puis la requête.


Vous n'êtes pas sûr de l'ensemble de votre cas d'utilisation, mais si l'évolutivité et la simplicité sont importantes, en faire autant que vous le pouvez sur le client vous évite d'avoir à gérer une ressource distincte. Une fonction de cloud appelable peut être minimale si vous en avez besoin sur le serveur. Mais vous perdez le pouvoir des règles de sécurité et de validation des données pour gérer la validation des données d'autorisation à votre place. Structurer qui peut faire quoi et quand / avec quels types de données est implémenté de manière succincte dans les règles par rapport à un appel serveur.