My project used #Nativescript/firebase(https://github.com/EddyVerbruggen/nativescript-plugin-firebase) ignores methods of firebase.firestore.timestamp, and returns undefined by properties.
The below is minimum reproduction
app.js
import Vue from "nativescript-vue";
import Home from "./components/Home";
var firebase = require("#nativescript/firebase").firebase;
firebase
.init({})
.then(
function () {
console.log("firebase.init done");
},
function (error) {
console.log("firebase.init error: " + error);
}
);
new Vue({
render: (h) => h("frame", [h(Home)]),
}).$start();
Home.vue
import { firebase } from "#nativescript/firebase";
export default {
computed: {
async message() {
const Ref = firebase.firestore
.collection("comments")
.doc("07bhQeWDf3u1j0B4vNwG");
const doc = await Ref.get();
const hoge = doc.data();
console.log("hoge.commented_at", hoge.commented_at); // CONSOLE LOG: hoge.commented_at Sat Oct 23 2021 22:44:48 GMT+0900 (JST)
console.log("hoge.commented_at.seconds", hoge.commented_at.seconds); // CONSOLE LOG: hoge.commented_at.seconds undefined
const hogeToDate = hoge.toDate();
console.log("hogeToDate", hogeToDate); // no console.log appear
return hogeToDate; // simulator shows "object Promise"
},
},
};
I also tried const hogeTimestampNow = firebase.firestore.Timestamp.now(); then no console.log appear...
Environment
vue.js
Node.js v14.17.6
nativescript v8.1.2
nativescript-vue v2.9.0
#nativescript/firebase v11.1.3
If you dive into the source of #nativescript/firebase, in particular looking at /src/firebase-common.ts, you can see that firebase is a custom implementation and not the object/namespace normally exported by the ordinary Firebase Web SDK.
It uses a custom implementation so that it can be transformed depending on the platform the code is running on as shown in /src/firebase.android.ts and /src/firebase.ios.ts.
Of particular importance, is that Firestore's Timestamp objects are internally converted to JavaScript Date objects when exposed to your code as each platform has its own version of a Timestamp object. Because the exposed JavaScript Date object doesn't have a seconds property, you get undefined when attempting to access hoge.commented_at.seconds.
The equivalent of Timestamp#seconds would be Math.floor(hoge.commented_at / 1000) (you could also be more explicit with Math.floor(hoge.commented_at.getTime() / 1000) if you don't like relying on JavaScript's type coercion).
function getSeconds(dt: Date) {
return Math.floor(dt.getTime() / 1000)
}
While you can import the Timestamp object from the Modular Web SDK (v9+), when passed into the NativeScript plugin, it would be turned into an ordinary object (i.e. { seconds: number, nanoseconds: number } rather than a Timestamp).
import { Timestamp } from 'firebase/firestore/lite';
const commentedAtTS = Timestamp.fromDate(hoge.commented_at);
docRef.set({ commentedAt: commentedAtTS.toDate() }) // must turn back to Date object before writing!
firebase.firestore.timestamp does not work via #nativescript/firebase as #samthecodingman said.(https://stackoverflow.com/a/69853638/15966408)
Just use ordinally javascript methods and edit.
I tried
get timestamp from firestore then convert to milliseconds
get date with new Date() then convert to milliseconds
and same miliseconds logged.
via firestore
const Ref = firebase.firestore.collection("comments").doc("07bhQeWDf3u1j0B4vNwG");
const doc = await Ref.get();
const hoge = doc.data();
console.log("hoge.commented_at in milliseconds: ", Math.floor(hoge.commented_at / 1000));
// CONSOLE LOG: hoge.commented_at in milliseconds: 1634996688
via javascript methods
const getNewDate = new Date("October 23, 2021, 22:44:48 GMT+0900");
// same as hoge.commented_at
console.log("getNewDate in milliseconds: ", getNewDate.getTime() / 1000);
// CONSOLE LOG: getNewDate in milliseconds: 1634996688
I am trying to set a variable that will contain a part of a URL (a UUID) which I would then like to use in separate test suites. This snippet of the URL will be different every time so I cannot set it in the cypress.json within the "env" options. Code is as follows -
cy.location().then(fullUrl => {
let pathName = fullUrl.pathname
let arr = pathName.split('/');
const teamsTeamID = arr[4]
cy.log(teamsTeamID)
})
I would then like to use teamsTeamID in a separate teardown test to delete the team at the end of every test run but the team ID will be different every time I run the test - Is there a way to do this?
You can use fixtures and then use readFile and writeFile to achieve this.
First create a json inside your fixtures folder urldata.json
{
"uuid": "17289-YEHBE-893"
}
Then in your test you can write:
var teamsTeamID;
cy.location().then(fullUrl => {
let pathName = fullUrl.pathname
let arr = pathName.split('/');
teamsTeamID = arr[4]
cy.log(teamsTeamID)
})
cy.readFile("cypress/fixtures/urldata.json", (err, data) => {
if (err) {
return console.error(err);
};
}).then((data) => {
data.uuid = teamsTeamID
cy.writeFile("cypress/fixtures/urldata.json", JSON.stringify(data))
})
So now every time you run the test, you will have different values of UUID in your json file. Next you can use the value from fixtures directly into other tests:
describe('Some page', () => {
beforeEach(function () {
// "this" points at the test context object
cy.fixture('urldata.json').then((urldata) => {
// "this" is still the test context object
this.urldata = urldata
})
})
// the test callback is in "function () { ... }" form
it('check uuid', function () {
// this.urldata exists
expect(this.urldata.uuid).to.equal('some uuid')
})
})
Point to be noted:
If you store and access the fixture data using this test context
object, make sure to use function () { ... } callbacks. Otherwise the
test engine will NOT have this pointing at the test context.
I'm trying to run a firestore query in my app which is is throwing up an error that an index is missing.
Firestore always used to also include a link to click for the console to create the missing index, but it's not there this time so I'm stuck!
Not sure what I've done differently this time.
This is the console error:
prebuilt-d16b955d-cdb9e87f.js:188 Uncaught (in promise) FirebaseError: no matching index found.
at new e (prebuilt-d16b955d-cdb9e87f.js:188)
at prebuilt-d16b955d-cdb9e87f.js:10416
at prebuilt-d16b955d-cdb9e87f.js:10414
at e.onMessage (prebuilt-d16b955d-cdb9e87f.js:10403)
at prebuilt-d16b955d-cdb9e87f.js:10356
at prebuilt-d16b955d-cdb9e87f.js:10387
at prebuilt-d16b955d-cdb9e87f.js:15180
This is my JS function:
loadcatalogues() {
console.log(this.clubID);
this.showspinner = true;
this.catalogues = [];
var today = moment().utc().unix();
let self = this;
fb.catalogueCollection
.where("clubID", "==", this.clubID)
.where("expiry", ">", today)
.orderBy("expiry", "asc")
.orderBy("price", "asc")
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
if (
doc.data().members &&
doc.data().members.indexOf(self.userProfile.id) !== -1
) {
return false;
}
var starttime = moment.unix(doc.data().start).utc();
var endtime = moment.unix(doc.data().expiry).utc();
var tempdata = {
title: doc.data().title,
description: doc.data().description,
start: starttime.format("DD-MM-YYYY"),
expiry: endtime.format("DD-MM-YYYY"),
price: doc.data().price,
purchased: false,
stripeid: doc.data().stripeid,
catalogueid: doc.id,
limited: doc.data().limited,
};
self.catalogues.push(tempdata);
});
if (self.catalogues.length == 0) {
self.shownooptions = true;
}
self.showspinner = false;
});
},
If I remove the first where clause (.where("clubID", "==", this.clubID)) then the query runs fine, so I'm guessing an index already exists for that query.
I've only recently added the clubID field, so could that be missing an index?
Thanks!!
Looks like there's currently a problem with Google firestore.
Raised a support call and they have confirmed they have received several other reports of this and their engineering team are investigating.
I am using the aurelia-store state management library for managing state. This question is not specific to Aurelia store, but actually to redux best practices in general since Aurelia store is very much the same thing.
I have an action that fetches unit updates from an API like so:
export const fetchNewUnits = async (state: State): Promise<State> => {
const fetchedUnits = await apiClient.getUnitsMarkers();
// no new updates so don't trigger change in units
// IS THIS ACCEPTABLE?
if (fetchedUnits.length === 0) {
return {
...state,
highwaterMark: new Date()
};
}
const units: UnitMarker[] = state.units.slice();
_.forEach(fetchedUnits, (newUnit) => {
// look for matching unit in store
const idx = _.findIndex(units, {
imei: newUnit.imei
});
// unit was found in store, do update
if (idx !== -1) {
// replace the unit in the store
const replacement = new UnitMarker({...newUnit});
units.splice(idx, 1, replacement);
}
});
// OR SHOULD I ALWAYS DEEP COPY THE ARRAY REFERENCE AND IT'S OBJECTS
return {
...state,
highwaterMark: new Date(),
units: [...units]
};
};
If I do not have any unit changes (i.e. my store is up to date) can I simply return the state with the spread operator as shown in the first return statement? Is this fine since I did not modify the objects?
Or do I always have to do deep replacements such as:
return {
...state,
highwaterMark: new Date(),
units: [...state.units]
};
even if the objects in the array did not change?
The reason why you’re supposed to create a new object is because React components check for prop changes in order to know when to re-render.
If you simply modify an object and pass it in as a prop again, React won’t know that something changed and will fail to rerender.
So in your case, the question is: do you want to rerender, or not? If you don’t, returning the same object is fine and a simple ‘return state’ will let React know that no rerenders are necessary.
See: Why is the requirement to always return new object with new internal references
I'm newbie to Firestore. Firestore docs says...
Important: Unlike "push IDs" in the Firebase Realtime Database, Cloud Firestore auto-generated IDs do not provide any automatic ordering. If you want to be able to order your documents by creation date, you should store a timestamp as a field in the documents.
Reference: https://firebase.google.com/docs/firestore/manage-data/add-data
So do I have to create key name as timestamp in document? Or created is suffice to fulfill above statement from Firestore documentation.
{
"created": 1534183990,
"modified": 1534183990,
"timestamp":1534183990
}
firebase.firestore.FieldValue.serverTimestamp()
Whatever you want to call it is fine afaik. Then you can use orderByChild('created').
I also mostly use firebase.database.ServerValue.TIMESTAMP when setting time
ref.child(key).set({
id: itemId,
content: itemContent,
user: uid,
created: firebase.database.ServerValue.TIMESTAMP
})
Use firestore Timestamp class, firebase.firestore.Timestamp.now().
Since firebase.firestore.FieldValue.serverTimestamp() does not work with add method from firestore. Reference
For Firestore
ref.doc(key).set({
created: firebase.firestore.FieldValue.serverTimestamp()
})
REALTIME SERVER TIMESTAMP USING FIRESTORE
import firebase from "firebase/app";
const someFunctionToUploadProduct = () => {
firebase.firestore().collection("products").add({
name: name,
price : price,
color : color,
weight :weight,
size : size,
createdAt : firebase.firestore.FieldValue.serverTimestamp()
})
.then(function(docRef) {
console.log("Document written with ID: ", docRef.id);
})
.catch(function(error) {
console.error("Error adding document: ", error);
});
}
All you need is to import 'firebase' and then call
firebase.firestore.FieldValue.serverTimestamp() wherever you need it. Be careful with the spelling though, its "serverTimestamp()". In this example it provides the timestamp value to 'createdAt' when uploading to the firestore's product's collection.
That's correct, like most database, Firestore doesn't store creation times. In order to sort objects by time:
Option 1: Create timestamp on client (correctness not guaranteed):
db.collection("messages").doc().set({
....
createdAt: firebase.firestore.Timestamp.now()
})
The big caveat here is that Timestamp.now()uses the local machine time. Therefore, if this is run on a client machine, you have no guarantee the timestamp is accurate. If you're setting this on the server or if guaranteed order isn't so important, it might be fine.
Option 2: Use a timestamp sentinel:
db.collection("messages").doc().set({
....
createdAt: firebase.firestore.FieldValue.serverTimestamp()
})
A timestamp sentinel is a token that tells the firestore server to set the time server side on first write.
If you read the sentinel before it is written (e.g., in a listener) it will be NULL unless you read the document like this:
doc.data({ serverTimestamps: 'estimate' })
Set up your query with something like this:
// quick and dirty way, but uses local machine time
const midnight = new Date(firebase.firestore.Timestamp.now().toDate().setHours(0, 0, 0, 0));
const todaysMessages = firebase
.firestore()
.collection(`users/${user.id}/messages`)
.orderBy('createdAt', 'desc')
.where('createdAt', '>=', midnight);
Note that this query uses the local machine time (Timestamp.now()). If it's really important that your app uses the correct time on the clients, you could utilize this feature of Firebase's Realtime Database:
const serverTimeOffset = (await firebase.database().ref('/.info/serverTimeOffset').once('value')).val();
const midnightServerMilliseconds = new Date(serverTimeOffset + Date.now()).setHours(0, 0, 0, 0);
const midnightServer = new Date(midnightServerMilliseconds);
The documentation isn't suggesting the names of any of your fields. The part you're quoting is just saying two things:
The automatically generated document IDs for Firestore don't have a natural time-based ordering like they did in Realtime Database.
If you want time-based ordering, store a timestamp in the document, and use that to order your queries. (You can call it whatever you want.)
This solution worked for me:
Firestore.instance.collection("collectionName").add({'created': Timestamp.now()});
The result in Cloud Firestore is:
Cloud Firestore Result
Try this one for Swift 4 Timestamp(date: Date())
let docData: [String: Any] = [
"stringExample": "Hello world!",
"booleanExample": true,
"numberExample": 3.14159265,
"dateExample": Timestamp(Date()),
"arrayExample": [5, true, "hello"],
"nullExample": NSNull(),
"objectExample": [
"a": 5,
"b": [
"nested": "foo"
]
]
]
db.collection("data").document("one").setData(docData) { err in
if let err = err {
print("Error writing document: \(err)")
} else {
print("Document successfully written!")
}
}
The way it worked with me, is just taking the timestamp from the snapshot parameter snapshot.updateTime
exports.newUserCreated = functions.firestore.document('users/{userId}').onCreate(async (snapshot, context) => {
console.log('started! v1.7');
const userID = context.params['userId'];
firestore.collection(`users/${userID}/lists`).add({
'created_time': snapshot.updateTime,
'name':'Products I ♥',
}).then(documentReference => {
console.log("initial public list created");
return null;
}).catch(error => {
console.error('Error creating initial list', error);
process.exit(1);
});
});
I am using Firestore to store data that comes from a Raspberry PI with Python. The pipeline is like this:
Raspberry PI (Python using paho-mqtt) -> Google Cloud IoT -> Google Cloud Pub/Sub -> Firebase Functions -> Firestore.
Data in the device is a Python Dictionary. I convert that to JSON.
The problem I had was that paho-mqtt will only send (publish) data as String and one of the fields of my data is timestamp. This timestamp is saved from the device because it accurately says when the measurement was taken regardless on when the data is ultimately stored in the database.
When I send my JSON structure, Firestore will store my field 'timestamp' as String. This is not convenient. So here is the solution.
I do a conversion in the Cloud Function that is triggered by the Pub/Sub to write into Firestore using Moment library to convert.
Note: I am getting the timestamp in python with:
currenttime = datetime.datetime.utcnow()
var moment = require('moment'); // require Moment
function toTimestamp(strDate){
return parsedTime = moment(strDate, "YYYY-MM-DD HH:mm:ss:SS");
}
exports.myFunctionPubSub = functions.pubsub.topic('my-topic-name').onPublish((message, context) => {
let parsedMessage = null;
try {
parsedMessage = message.json;
// Convert timestamp string to timestamp object
parsedMessage.date = toTimestamp(parsedMessage.date);
// Get the Device ID from the message. Useful when you have multiple IoT devices
deviceID = parsedMessage._deviceID;
let addDoc = db.collection('MyDevices')
.doc(deviceID)
.collection('DeviceData')
.add(parsedMessage)
.then ( (ref) => {
console.log('Added document ID: ', ref.id);
return null;
}).catch ( (error) => {
console.error('Failed to write database', error);
return null;
});
} catch (e) {
console.error('PubSub message was not JSON', e);
}
// // Expected return or a warning will be triggered in the Firebase Function logs.
return null;
});
Firestone method does not work. Use Timestamp from java.sql.Timestamp and don't cast to string.. Then firestone formats it properly. For example to mark a now() use:
val timestamp = Timestamp(System.currentTimeMillis())
multiple ways to store time in Firestore
firebaseAdmin.firestore.FieldValue.serverTimestamp() method. The actual timestamp will be computed when the doc is written to the Firestore.
while storing it looks like this:
firebaseAdmin.firestore.Timestamp.now() method.
while storing it looks like this:
For both the methods, next time you fetch data it will return Firestore Timestamp object:
So, you first need to convert it to native js Date object and then you can perform methods on it like toISOString().
export function FStimestampToDate(
timestamp:
| FirebaseFirestore.Timestamp
| FirebaseFirestore.FieldValue
): Date {
return (timestamp as FirebaseFirestore.Timestamp).toDate();
}
Store as unix timestamp Date.now, it'll be stored as number i.e. 1627235565028 but you won't be able to see it as readable Date in firestore db.
To query on this Firestore field, you need to convert the date to timestamp and then query.
Store as new Date().toISOString() i.e. "2021-07-25T17:56:40.373Z" but you won't be able to perform date range query on this.
I prefer the 2nd or 3rd way.
According to the docs, you can "set a field in your document to a server timestamp which tracks when the server receives the update".
Example:
import { updateDoc, serverTimestamp } from "firebase/firestore";
const docRef = doc(db, 'objects', 'some-id');
// Update the timestamp field with the value from the server
const updateTimestamp = await updateDoc(docRef, {
timestamp: serverTimestamp() // this does the trick!
});
Sharing what worked for me after googling for 2 hours, for firebase 9+
import { serverTimestamp } from "firebase/firestore";
export const postData = ({ name, points }: any) => {
const scoresRef = collection(db, "scores");
return addDoc(scoresRef, {
name,
points
date: serverTimestamp(),
});
};
Swift 5.1
...
"dateExample": Timestamp(date: Date()),
...
The newest version from Firestore you should use it as follow
import { doc, setDoc, Timestamp } from "firebase/firestore";
const docData = {
...
dateExample: Timestamp.fromDate(new Date("December 10, 1815"))
};
await setDoc(doc(db, "data", "one"), docData);
or for sever timestamp
import { updateDoc, serverTimestamp } from "firebase/firestore";
const docRef = doc(db, 'objects', 'some-id');
const updateTimestamp = await updateDoc(docRef, {
timestamp: serverTimestamp()
});