When using Firestore, you may wonder whether additional round trips are required to read the created and updated timestamp fields. The answer is no. Firestore automatically provides creation and update timestamps for each document, and you can obtain the corresponding time information by referencing these fields. In this way, you don't need additional operations to read the timestamp field, and you can more easily obtain the creation and update time of the document. This design makes the development process more efficient and simplified, avoiding unnecessary code and requests.
Okay, I have a rest api
in go
that uses firestore
to store ticket
resources. For this I use: firestore go client
I want to be able to sort my documents by date created/updated date
so as per the document I am storing these 2 fields as timestamps in the document.
I use the tag servertimestamp
on both fields. By doing this, the value should be the time it took for the firestore server to process the request.
The http response for the update operation should contain the following body:
{ "ticket": { "id": "af41766e-76ea-43b5-86c1-8ba382edd4dc", "title": "ticket updated title", "price": 9, "date_created": "2023-01-06 09:07:24", "date_updated": "2023-01-06 10:08:24" } }
This means that after I update the ticket document, in addition to the updated title or price
, I also need to update the value of the date_updated
field.
It currently works, but I'm curious if the way I'm coding is the way to do it. As you can see in the code example, I use a transaction to update the ticket. I don't find a way to retrieve the updated value of the dateupdated
field other than reading the updated ticket again.
Domain entities are defined as follows:
package tixer import ( "context" "time" "github.com/google/uuid" ) type ( // ticketid represents a unique identifier for a ticket. // it's a domain type. ticketid uuid.uuid // ticket represents an individual ticket in the system. // it's a domain type. ticket struct { id ticketid title string price float64 datecreated time.time dateupdated time.time } )
I will attach here the communication with firestore from create and update perspective:
// Storer persists tickets in Firestore. type Storer struct { client *firestore.Client } func NewStorer(client *firestore.Client) *Storer { return &Storer{client} } func (s *Storer) CreateTicket(ctx context.Context, ticket *tixer.Ticket) error { writeRes, err := s.client.Collection("tickets").Doc(ticket.ID.String()).Set(ctx, createTicket{ Title: ticket.Title, Price: ticket.Price, }) // In this case writeRes.UpdateTime is the time the document was created. ticket.DateCreated = writeRes.UpdateTime return err } func (s *Storer) UpdateTicket(ctx context.Context, ticket *tixer.Ticket) error { docRef := s.client.Collection("tickets").Doc(ticket.ID.String()) err := s.client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error { doc, err := tx.Get(docRef) if err != nil { switch { case status.Code(err) == codes.NotFound: return tixer.ErrTicketNotFound default: return err } } var t persistedTicket if err := doc.DataTo(&t); err != nil { return err } t.ID = doc.Ref.ID if ticket.Title != "" { t.Title = ticket.Title } if ticket.Price != 0 { t.Price = ticket.Price } return tx.Set(docRef, updateTicket{ Title: t.Title, Price: t.Price, DateCreated: t.DateCreated, }) }) if err != nil { return err } updatedTicket, err := s.readTicket(ctx, ticket.ID) if err != nil { return err } *ticket = updatedTicket return nil } func (s *Storer) readTicket(ctx context.Context, id tixer.TicketID) (tixer.Ticket, error) { doc, err := s.client.Collection("tickets").Doc(id.String()).Get(ctx) if err != nil { switch { case status.Code(err) == codes.NotFound: return tixer.Ticket{}, tixer.ErrTicketNotFound default: return tixer.Ticket{}, err } } var t persistedTicket if err := doc.DataTo(&t); err != nil { return tixer.Ticket{}, err } t.ID = doc.Ref.ID return toDomainTicket(t), nil } type ( // persistedTicket represents a stored ticket in Firestore. persistedTicket struct { ID string `firestore:"id"` Title string `firestore:"title"` Price float64 `firestore:"price"` DateCreated time.Time `firestore:"dateCreated"` DateUpdated time.Time `firestore:"dateUpdate"` } // createTicket contains the data needed to create a Ticket in Firestore. createTicket struct { Title string `firestore:"title"` Price float64 `firestore:"price"` DateCreated time.Time `firestore:"dateCreated,serverTimestamp"` DateUpdated time.Time `firestore:"dateUpdate,serverTimestamp"` } // updateTicket contains the data needed to update a Ticket in Firestore. updateTicket struct { Title string `firestore:"title"` Price float64 `firestore:"price"` DateCreated time.Time `firestore:"dateCreated"` DateUpdated time.Time `firestore:"dateUpdate,serverTimestamp"` } ) func toDomainTicket(t persistedTicket) tixer.Ticket { return tixer.Ticket{ ID: tixer.TicketID(uuid.MustParse(t.ID)), Title: t.Title, Price: t.Price, DateCreated: t.DateCreated, DateUpdated: t.DateUpdated, } }
If I understand correctly, the DateUpdated
field is a server-side timestamp, which means that its value is written by the server when the value is Determined when entering the storage layer (the so-called field conversion). Since a write operation in the Firestore SDK does not return the result data of the operation, the only way to get that value back into the application is actually to perform an additional read operation after the write to obtain it.
The SDK will not automatically perform this read because it is a chargeable operation and is not needed in many cases. So by having your code perform that read, you can decide whether to incur this cost.
The above is the detailed content of Do I need an extra round trip to firestore to read the created and updated timestamp fields?. For more information, please follow other related articles on the PHP Chinese website!