Go语言MongoDB API开发:嵌套时间戳对比、指定字段查询及Token更新方案咨询
Go操作MongoDB的三个问题解决方案
针对你用Go开发MongoDB API遇到的这三个问题,结合你的文档结构,我给你具体的实现方案:
1. 按_id和tokens.owner查询,仅返回匹配的token和created_at
要实现这个需求,我们可以利用MongoDB的**投影(Projection)**和数组位置操作符$,它会返回数组中第一个匹配条件的元素。在Go中使用mongo-go-driver的代码示例如下:
import ( "context" "time" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) func getMatchingToken(collection *mongo.Collection, docID string, owner string) (map[string]interface{}, error) { // 转换_id为ObjectID类型 objID, err := primitive.ObjectIDFromHex(docID) if err != nil { return nil, err } // 构造查询条件:匹配文档_id和tokens数组中owner字段 filter := bson.M{ "_id": objID, "tokens.owner": owner, } // 投影:只返回匹配的token和created_at,不返回_id projection := bson.M{ "tokens.$.token": 1, "tokens.$.created_at": 1, "_id": 0, } var result bson.M err = collection.FindOne(context.TODO(), filter, options.FindOne().SetProjection(projection)).Decode(&result) if err != nil { return nil, err } return result, nil }
说明:
tokens.$中的$会定位到tokens数组中第一个匹配tokens.owner的元素- 投影里明确指定只返回该元素的
token和created_at字段,排除了其他无关内容
2. 判断updated_at是否大于匹配的tokens.created_at,返回对应结果
需求是:如果文档的updated_at大于匹配的tokens.owner对应的created_at,则返回false,否则返回true。我们可以通过聚合管道来实现这个逻辑,先过滤文档,再展开数组并比较时间:
func checkTimestampCondition(collection *mongo.Collection, docID string, owner string) (bool, error) { objID, err := primitive.ObjectIDFromHex(docID) if err != nil { return false, err } // 聚合管道:先匹配文档,再展开tokens数组,过滤目标owner,最后比较时间 pipeline := []bson.M{ {"$match": bson.M{ "_id": objID, "tokens.owner": owner, }}, {"$unwind": "$tokens"}, // 展开tokens数组为单个文档 {"$match": bson.M{"tokens.owner": owner}}, // 过滤出目标owner的token记录 {"$project": bson.M{ // 判断updated_at是否大于tokens.created_at,结果存入shouldReturnFalse "shouldReturnFalse": bson.M{"$gt": []interface{}{"$updated_at", "$tokens.created_at"}}, "_id": 0, }}, } cursor, err := collection.Aggregate(context.TODO(), pipeline) if err != nil { return false, err } defer cursor.Close(context.TODO()) var result struct { ShouldReturnFalse bool `bson:"shouldReturnFalse"` } if cursor.Next(context.TODO()) { err := cursor.Decode(&result) if err != nil { return false, err } // 按需求返回:若updated_at>created_at,返回false;否则返回true return !result.ShouldReturnFalse, nil } // 未找到匹配的记录,可根据业务逻辑返回默认值 return false, mongo.ErrNoDocuments }
说明:
- 聚合管道先定位到目标文档,再展开数组确保我们能单独处理匹配的token记录
$gt操作符用于比较两个时间戳,最终根据比较结果返回符合需求的布尔值
3. 更新指定tokens.owner的token,而非追加新元素
要直接更新数组中匹配条件的元素,我们可以使用$set操作符结合数组位置操作符$(更新第一个匹配元素),或者数组过滤器arrayFilters(更新所有匹配元素)。
方案一:更新第一个匹配的token元素
func updateMatchingToken(collection *mongo.Collection, docID string, owner string, newToken string) (*mongo.UpdateResult, error) { objID, err := primitive.ObjectIDFromHex(docID) if err != nil { return nil, err } filter := bson.M{ "_id": objID, "tokens.owner": owner, } // 使用$set更新匹配的token元素,同时更新updated_at update := bson.M{ "$set": bson.M{ "tokens.$.token": newToken, "updated_at": time.Now().UTC(), }, } result, err := collection.UpdateOne(context.TODO(), filter, update) return result, err }
方案二:更新所有匹配的token元素(如果有多个相同owner的记录)
如果你的文档中可能存在多个tokens.owner相同的元素,可以使用数组过滤器来批量更新:
func updateAllMatchingTokens(collection *mongo.Collection, docID string, owner string, newToken string) (*mongo.UpdateResult, error) { objID, err := primitive.ObjectIDFromHex(docID) if err != nil { return nil, err } filter := bson.M{ "_id": objID, } update := bson.M{ "$set": bson.M{ "tokens.$[elem].token": newToken, "updated_at": time.Now().UTC(), }, } // 设置数组过滤器:匹配所有owner为指定值的元素 opts := options.Update().SetArrayFilters(options.ArrayFilters{ Filters: []interface{}{ bson.M{"elem.owner": owner}, }, }) result, err := collection.UpdateOne(context.TODO(), filter, update, opts) return result, err }
说明:
tokens.$.token中的$会定位到第一个匹配tokens.owner的元素- 数组过滤器
$[elem]可以匹配所有符合条件的元素,适合批量更新场景 - 同时更新
updated_at字段,符合文档的时间戳维护逻辑
内容的提问来源于stack exchange,提问作者NicK
相关产品推荐
相关产品推荐

