Home  >  Article  >  Backend Development  >  Do I need an extra round trip to firestore to read the created and updated timestamp fields?

Do I need an extra round trip to firestore to read the created and updated timestamp fields?

PHPz
PHPzforward
2024-02-11 18:36:091194browse

我是否需要额外往返 firestore 来读取创建和更新的时间戳字段?

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.

Question content

  1. Okay, I have a rest api in go that uses firestore to store ticket resources. For this I use: firestore go client

  2. 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.

  3. 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.

  4. 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,
    }
}

Solution

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!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete