Firestore orderBy Timestamp object - javascript

I am trying to order a query by timestamp.
In my document I have a field called "date" which has this form:
date = {
nanoseconds: 963000000,
seconds: 1594917688
}
In my code I have this:
let photosArray = [];
firebase
.getDatabase()
.collection("photos")
.doc(firebase.getCurrentUser().uid)
.collection("userPhotos")
.orderBy("date", "asc") // Sorted by date in ascending direction
.onSnapshot((snapshot) => {
let changes = snapshot.docChanges();
changes.forEach((change) => {
if (change.type === "added") {
// Get the new photo
const photo = change.doc.data();
// Add the photo to the photos list
photosArray.push(photo);
}
});
// The last photo is at the top of the list
setPhotos(photosArray);
But when I render the list of photos, they are unsorted... For example: the first one taken 2 hours ago, the second one taken 1 minute ago, and the last one taken 2 years ago.
UPDATE
This is how I store the date in firestore
Firebase.js:
getTimestamp = () => firebase.firestore.FieldValue.serverTimestamp();
PhotoUploader.js
await firestore
.collection("photos")
.doc(userId)
.collection("userPhotos")
.add({
id,
date: firebase.getTimestamp(),
});

If your date field shows a map with two nested fields, that is not really a timestamp, and it won't sort the way you expect. You should take a look at the code that adds the date field to the document, and make sure it uses a timestamp correctly. Either that, or use a single timestamp numeric value that will sort the way you expect.

Related

how to create firestore composite index for conditional query?

I'm using react and fetching docs from my firestore collection. and when i fetch the docs i assigned a conditional query and the query direction changes depending on the value provided.
const getData = async () => {
const constraints = [];
if (price)
constraints.push(orderBy("price", price == "1" ? "desc" : "asc"));
if (date)
constraints.push(orderBy("postedDate", date == "1" ? "desc" : "asc"));
if (type)
constraints.push(orderBy("type", type == "1" ? "desc" : "asc"));
// there are more conditional queries here. 8 more to be exact
const livings = collection(db, "livingPosts");
let q = query(livings, ...constraints);
const qSnapshot = await getDocs(q);
const dataQ = qSnapshot.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}));
// console.log(dataQ);
setDatas(dataQ);
};
useEffect(() => {
getData();
}, []);
as you can see in the above code i've implemented conditional query. and there are more queries that i have not included. so my question is that how can i create indexes for all of these queries?
i created indexes with the link firebase provided me to create it. but it made me create 4 composite indexes just for 2 queries. (for price and date), the order is
price - asc , date - asc
price - desc , date - asc
price - asc , date - desc
price - desc , date - asc
these were the indexes. so do i have to create every possible indexes like these? if so there are number of combinations i have to do and the max number of indexes are 200. please show me the right way
So do i have to create every possible indexes like these
Yes you need to create all the indexes corresponding to the queries your app may potentially execute.
For that you can use the URL present in the error messages (which means that you need to execute all the possible queries, i.e. play all the scenarii) or you can use the Firebase (or Google Cloud) consoles to build them manually.
For the default limit of maximum 200 composite indexes you can actually contact the Firebase support to ask for an increase of this maximum number.

Array inside useEffect to use in if Statement

I'm trying to use an if statement in my code where I want it to 'open' a Calendar Box if the date of today has occurred as well as for the past days of my calendar to open.
Here is my code where I'm using an useEffect to post it on loading the React Component:
// Call on post method via axios
useEffect(async () => {
console.log(daysData);
const daysDay = daysData.map((day) => day.day);
console.log(daysDay);
if (date + 1 >= daysDay) {
// Url where to post
await axios.post(`http://localhost:5001/open/chocolate`, {
day: date,
});
alert('New day is available to eat!');
}
setOpenCalendarBox('');
}, []);
I'm trying to get an array I've initiated a few lines above of the useEffect function (daysData) and I want the value of the 'day' item inside of the objects inside of the array and then compare the date of today if it is equal to or less than daysDay (day item inside of daysData)
Here is the code for my array:
// Here I initalize the array with useState
const [daysData, setDaysData] = useState([]);
// Here is the port I'm fetching my array from.
useEffect(() => {
fetch('http://localhost:5001/chocolates')
.then((resp) => resp.json())
.then((data) => setDaysData(data));
}, []);
And here is the date code:
// Initiate new Date
const current = new Date();
// Retrieve current day of the month
const date = current.getDate();
I can't seem to get the effect I want. I basically only want to see if the day has passed or if it is today then I want it to post to '/open/chocolate'.
That's probably because the value of daysData is set asynchronously, yet the useEffect block that depends on it does not list it as a dependency. Therefore you are invoking logic, which requires daysData to be populated asynchronously, when the component is loaded at runtime. So daysData will be empty.
A solution is to simply add daysData in the dependency array, so that you will only execute whatever logic that is in there once the array is successfully populated.
On the other hand, you are comparing a number against an array: which will give an unexpected result. If you want any of the day to meet date + 1, use daysDay.some(d => date + 1 >= d). If you want all of the days to meet date + 1, use daysDate.every(d => date + 1 >= d).
useEffect(async () => {
const daysDay = daysData.map((day) => day.day);
// This needs to be fixed, see comment for options
if (daysDay.some(d => date + 1 > d)) {
// Url where to post
await axios.post(`http://localhost:5001/open/chocolate`, {
day: date,
});
}
setOpenCalendarBox('');
}, [daysData]);

Is there any way to auto increment a field in firebase

I want to auto increment a stock number for each new item added to the firestore database. (Not using the push() method)
The stock number is a controlled number and needs to be unique and incremented by 1 every time a new field is added without me having to manually specify the stock number.
Please keep in mind - im a beginner and do not have in depth knowledge of firebase.
You can do it easily using Cloud Functions
// Change '/COLLECTION/{DOC}' to which document do you want to increment the counter when it's created
exports.counter = functions.firestore.document('/COLLECTION/{DOC}').onCreate((snap, context) => {
const db = admin.firestore();
// Change 'counter/ref' to where do you want to increment
const countRef = db.doc('counter/ref');
return db.runTransaction(t => {
return t.get(countRef).then(doc => {
const counter = (doc.data().counter || 0) + 1;
t.update(countRef, {counter: counter});
});
});
});
You can learn more about Cloud Functions here https://firebase.google.com/docs/functions/

Checking for part of a key value pair

I am currently trying to develop an app that displays the parking tickets in NYC. The API I am using is https://data.cityofnewyork.us/resource/ati4-9cgt.json. I am trying to create a graph in which it shows how many tickets were distributed per month. There is a column called "issue_date", and it has year, month, followed by a bunch of numbers I am unsure of. Is it possible to only check the year and month part, instead of the whole value? For example, if I only want to see if the ticket was issued in January, I would check for 2017-01, but how would I make it so that the rest of the value doesn't get accounted and break the code? Thank you!
I forgot to include that I am a first year in college. I have limited knowledge on programming, sorry for the inconvenience.
You can just grab the first 7 characters from the date.
Here is some code to count the number of entries per month and put that in an array:
fetch("https://data.cityofnewyork.us/resource/ati4-9cgt.json")
.then(resp => resp.json()).then(data => {
const byMonth = Object.entries(data.reduce( (acc, row) => {
const month = row.issue_date.slice(0,7); // <--- just taking YYYY-MM
acc[month] = acc[month] || 0;
acc[month]++; // <--- counting
return acc;
}, {})).sort((a, b) => a[0].localeCompare(b[0])); // <--- sorting months
console.log(byMonth); // <--- output [[month, count], [month, count], ...]
});
Match issuedate with regexp, e.g. /^2017-01/
So if you have this whole array, you could:onlyOnJanuary2017 = wholeArray.filter(object => object.issue_date.match(/^2017-01/)); as here: https://jsfiddle.net/twpq6nvd/
Additionaly, if you want to bucket themm try this
var result = arr.reduce((accumulator, item) => {
const date = new Date(item.issue_date)
var key = `${date.getFullYear()}-${date.getMonth()}`
accumulator[key] = accumulator[key] || [];
accumulator[key].push(item);
return accumulator;
}, {})
https://jsfiddle.net/d3qa96bg/1/

Return most recent time in series of creationTimes

Not entirely sure how I word the question but my problem is Im doing an api call that returns a bunch of messages that have a creation time, now what I want to do is only return the latest creationTime for the messages with the same date so say If I have 30 messages on the 15/03/2018 I want to grab the latest time, and discard the rest.. and do that for each set of messages with the same date
So what Ive done so far is..
using lodash I have gotten all the messages, filtered out all the ones with a certain type, and I have ordered them by creationTime so the latest being at the top and going down.. now my question is how can I then make an array of the latest times for each date??
this._activityServiceProxy.getAllItems(start, end).subscribe(result => {
// this.messages = result;
// console.log(result);
let loginUnfiltered = _.filter(result, {'title': 'LOGIN'});
let loginFiltered = _.orderBy(loginUnfiltered, {'creationTime': 'desc'});
console.log(loginFiltered);
});
any help would be appreciated!
Thanks
Use .map(...) to get at array of only the latest creationTime:
this._activityServiceProxy.getAllItems(start, end).subscribe(result => {
// this.messages = result;
// console.log(result);
let loginUnfiltered = _.filter(result, {'title': 'LOGIN'});
let loginFiltered = _.orderBy(loginUnfiltered, {'creationTime': 'desc'});
const creationTimes = loginFiltered.map(l => l.creationTime);
console.log(creationTimes);
const latestTime = creationTimes[0];
console.log(latestTime);
});
You can use Underscore's groupBy function to achieve this:
const groups = _.groupBy(loginFiltered, (login) => {
const asDate = new Date(login.creationTime);
asDate.setHours(0, 0, 0, 0);
return asDate;
});
Object.keys(groups).forEach((key) => {
console.log(groups[key][0]);
});
You group by the creationDate property but remove the time component so all days get grouped together. You then loop through the result and just take the first entry per day.
Note that this assumes your creationTime property is a string, as it came from an API. If it's already a date, you don't need the new Date line.

Categories

Resources