英文:
Do I need extra round trip to firestore for reading Created & Updated timestamps fields?
问题
好的,以下是翻译的内容:
-
好的,我有一个使用
firestore
存储ticket
资源的GO REST API
。为此,我使用了:firestore go client -
我希望能够按照
创建日期/更新日期
对文档进行排序,所以按照文档的说明,我将这两个字段存储为文档中的时间戳。 -
我在这两个字段上使用了
serverTimestamp
标签。通过这样做,该值应该是firestore服务器处理请求的时间。 -
更新操作的HTTP响应应该具有以下主体:
{
"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"
}
}
这意味着在更新票据文档之后,除了更新的标题或价格
之外,我还需要更新的date_updated
字段的值。
目前这种方式是有效的,但我想知道我编写的方式是否正确。正如您在代码示例中看到的,我使用事务来更新票据。我没有找到一种方法来检索DateUpdated
字段的更新值,除非重新读取更新后的票据。
该域实体定义如下:
package tixer
import (
"context"
"time"
"github.com/google/uuid"
)
type (
// TicketID表示票据的唯一标识符。
// 这是一个领域类型。
TicketID uuid.UUID
// Ticket表示系统中的单个票据。
// 这是一个领域类型。
Ticket struct {
ID TicketID
Title string
Price float64
DateCreated time.Time
DateUpdated time.Time
}
)
我将在这里附上与创建和更新方面的firestore通信:
// Storer在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,
})
// 在这种情况下,writeRes.UpdateTime是文档创建的时间。
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表示在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包含在Firestore中创建票据所需的数据。
createTicket struct {
Title string `firestore:"title"`
Price float64 `firestore:"price"`
DateCreated time.Time `firestore:"dateCreated,serverTimestamp"`
DateUpdated time.Time `firestore:"dateUpdate,serverTimestamp"`
}
// updateTicket包含在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,
}
}
英文:
-
Ok so I have a
REST API in GO
which stores aticket
resource usingfirestore
. For this I use: firestore go client -
I want to be able to order my documents by
date created / date updated
, so by following the docs I store these 2 fields as timestamps in the document. -
I use the tag
serverTimestamp
on these 2 fields. By doing this, the value should be the time at which the firestore server processed the request. -
The HTTP response of the update operation should have this 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"
}
}
So it means after I update the ticket document, besides an updated title or price
I also need to have the updated value fo the date_updated
field.
This is working for the moment but I'm curious if the way I coded this is the way to do it. As you can see in the code samples, I use a transaction to update a ticket. I didn't find a way to retrieve the updated value for the DateUpdated
field, other than reading again the updated ticket.
The domain entity is defined as this:
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'll attach here the communication with firestore from the 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,
}
}
答案1
得分: 1
如果我理解正确,DateUpdated
字段是一个服务器端时间戳,这意味着它的值是由服务器确定的(作为所谓的字段转换),当该值被写入存储层时。由于Firestore SDK中的写操作不会返回该操作的结果数据,因此唯一的方法是在写入后执行额外的读操作来获取该值返回到应用程序中。
SDK不会自动执行这个读操作,因为它是一项收费操作,在许多情况下是不需要的。因此,通过让您的代码执行该读操作,您可以决定是否承担这个成本。
英文:
If I understand correctly, the DateUpdated
field is a server-side timestamp, which means that its value is determined by the server (as a so-called field transformation) when the value is written to the storage layer. Since a write operation in the Firestore SDK doesn't return the resulting data of that operation, the only way to get that value back into your application is indeed to perform an extra read operation after the write to get it.
The SDK doesn't automatically perform this read is because it is a charged operation, which in many cases is not needed. So by leaving it up to your code to perform that read, you can decide whether to incur this cost or not.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论