【问题标题】:How to run an aggregate query via mongo_go_driver that has javascript in it?如何通过其中包含 javascript 的 mongo_go_driver 运行聚合查询?
【发布时间】:2019-02-13 13:07:45
【问题描述】:

我正在使用 mongo-go-driver (https://godoc.org/github.com/mongodb/mongo-go-driver/mongo),我正在尝试做相当于

db.getCollection('mycollection').aggregate([
    { $lookup: {
        from: "anothercollection",
        localField: "_id",
        foreignField: "foreignID",
        as: "matched_docs"
    }},
    { $match: { "matched_docs": { $eq: [] } } },
    { $project: { "matched_docs": 0 } },
    { $match: {"dateTimeGMT":{$lt: (new Date(Date.now()-1000*60*60*24)).toISOString()}} }
])

我不知道如何使用这种方法放置 Javascript 命令。

pipeline := bson.NewArray(
    bson.VC.DocumentFromElements(
        bson.EC.SubDocumentFromElements( 
        //yada, yada, yada...
cursor, err := collection.Aggregate(ctx, pipeline)

(总的来说,无论如何我都不喜欢这种方法。我希望能够在 Robo 3T 中设计查询并将它们复制到我的代码中,就像我使用 MySQL Workbench 和 PHP 一样)

此方法在管道中产生一个空的 *bson.Array

pipelineJSON := `[
    { $lookup: {
        from: "anothercollection",
        localField: "_id",
        foreignField: "interactionID",
        as: "matched_docs"
    }},
    { $match: { "matched_docs": { $eq: [] } } },
    { $project: { "matched_docs": 0 } },
    { $match: {"dateTimeGMT":{$lt: (new Date(Date.now()-1000*60*60*24)).toISOString()}} }
]`
pipeline, err = bson.ParseExtJSONArray(pipelineJSON)

如果有一种方法可以将 Mongo 的命令作为字符串发送(就像我在 Robo 3T 中键入它)并返回 *mongo.Cursor,我会非常喜欢它。我应该使用更好的驱动程序(仍然有人支持)吗?我需要自己编写代码吗?

谢谢!

【问题讨论】:

  • 你不能。你需要在 Go 中找到一种等效的方法。那个 JS 没有什么特别的——它只是返回一个过去某个时间的日期值。在没有 JS 的情况下做同样的事情应该是绝对微不足道的。
  • Flimzy:你说 JS 没什么特别的,我可以用 time.Now().AddDate(0, 0, -1).UTC().Format( time.RFC3339) 在 golang 中。对所有人:为了澄清我的驾驶目的,有谁知道我在哪里可以找到有关驱动程序正在使用的底层 mongodb api 的文档,以便我可以确定该级别或仅在 mongo shell 中是否有可用的 javascript 解析器?
  • 另外,澄清一下,JS 并不是唯一在 Robo3T/Mongo shell 中工作并且在 ParseExtJSONArray 中失败的东西。 ParseExtJSONArray 要求所有聚合阶段和函数都用引号括起来(而不是裸引号)。客户端golang程序有没有办法像shell一样查询mongo?
  • 如果您的请求是“我在哪里可以找到 MongoDB 文档?”不幸的是,这里的问题是题外话,因为资源请求是题外话。但是,应该很容易找到 MongoDB API 文档。
  • 很公平。我想我的问题的主题元素现在已经解决了,我正在转向题外话。我会用我学到的东西来回答这个问题。

标签: javascript mongodb go aggregate


【解决方案1】:

我不知道如何使用这种方法放置 Javascript 命令。

虽然我知道这不是您用例的首选方法,但这是使用 mongo-go-driver v1.0 将 aggregation pipeline 构造为 bson.D 的方法:

pipeline := mongo.Pipeline{
    {{"$lookup", bson.D{
        {"from", "tour"}, 
        {"localField", "_id"}, 
        {"foreignField", "foreignID"}, 
        {"as", "matched_docs"},
    }}},
    {{"$match", bson.D{
        {"matched_docs", bson.D{
            {"$eq", bson.A{}}},
        }, 
    }}},
    {{"$project", bson.D{
        {"matched_docs", 0}, 
    }}},        
    {{"$match", bson.D{
        {"dateTimeGMT", bson.D{
            {"$lt", time.Now().AddDate(0, 0, -1).UTC().Format(time.RFC3339)},
            }, 
        },
    }}},  
}

{ $project: { "matched_docs": 0 } },

您也可以将两个$match 组合成一个管道阶段,然后在最后附加$project。例如:

db.collection.aggregate([
    { "$lookup":{
        "from":"anothercollection",
        "localField":"_id",
        "foreignField":"foreignID",
        "as":"matched_docs"}
    }, 
    { "$match": { "matched_docs": { "$eq": [] }, 
                  "datetimegmt": { "$lt": (new Date(Date.now()-1000*60*60*24)).toISOString() } 
                } 
    }, 
    { "$project": { "matched_docs": 0 } }
]);

{ $match: {"dateTimeGMT":{$lt: (new Date(Date.now()-1000*60*60*24)).toISOString()}} }

根据 $match 值,您似乎将日期存储为 string 而不是 Date 对象。我建议将日期存储为正确的 Date 对象,以获得更好的索引性能。

使用 ParseExtJSONArray 确实有效,但与 Mongo Shell 不同,您不能包含 JS 或未加引号的聚合阶段/表达式运算符

mongo shell 提供了一些方便的方法/类型,即ObjectID() 等来构造MongoDB Extended JSONmongo shell 不仅仅是一个 JavaScript shell。

如果您的目标只是评估一个 JavaScript 表达式,您可以为 Go (otto) 使用 JavaScript 解释器。一个粗略的例子是:

// Note the unquoted fields, as JSON is JavaScript native. 
raw := `[
    { "$lookup": {
        from: "anothercollection",
        localField: "_id",
        foreignField: "foreignID",
        as: "matched_docs"
    }},
    { $match: { "matched_docs": { $eq: [] },
                "dateTimeGMT":{$lt: (new Date(Date.now()-1000*60*60*24)).toISOString() }, 
              }
    },
    { $project: { "matched_docs": 0 } },
]`
vm := otto.New()
// Evaluate JS expression
jsvalue, err := vm.Eval(raw)

// Export to Go interface{}
output, err := jsvalue.Export()

// Convert interface{} to bson.Document bytes 
bsonbytes, err := bson.Marshal(output)

// Convert bson.Document bytes to bson.Document
pipeline, err := bson.UnmarshalDocument(bsonbytes)

如上所述,有些对象无法被普通的 JavaScript 解释器识别,即ObjectId()

我希望能够在 Robo 3T 中设计查询并将它们复制到我的代码中,就像我使用 MySQL Workbench 和 PHP 一样

虽然目前不支持Go,但值得一提的是MongoDB CompassExport Query to Language有一个特性。当前版本 (1.15) 支持 Java、Node、C# 和 Python3。希望 Go 会在未来出现。

【讨论】:

  • 非常感谢。我有几个澄清问题。第一个与作为 *bson.Document 的聚合管道有关。一旦我构建了 *bson.Array,就说我想重用它并更改日期。有没有一种简单的方法来解决它,还是我必须构造和迭代器并进行一些 Lookup() 调用?每次都构建整个东西会更容易吗? (或从部分构造的部分组装 *bson.Array)。
  • 第二个问题与您的建议有关,即我使用 Date 对象而不是日期字符串。我不能将 ISODate("...") 放入 ParseExtJSONArray 方法的字符串中。我了解到 ISODate("...") 导出到 {"$date":"..."},但是如果我将它放在 JSON 字符串中并尝试在 dateTimeGMT.$date 上进行 $match,它不会不行。 *bson.Document 方法是我使用日期对象而不是日期作为字符串的唯一选择吗?
  • 提出这两个问题。
  • 1) 通常每个阶段构建bson.Document,然后在发送到聚合之前组装成单个bson.Array。因此,您可以只构建 $match 文档而不是所有阶段。
  • 2) 应该可以。 ParserExtJSONArray 应该能够理解扩展的 JSON 格式 $date,你能用示例打开一个新问题并在此处发布链接吗?
【解决方案2】:

使用 ParseExtJSONArray 确实有效,但与 Mongo Shell 不同,您不能包含 JS 或未加引号的聚合阶段/表达式运算符。请注意,比较的 ISO 日期是在 golang 中计算的,并且聚合阶段和聚合表达式运算符已用引号括起来。

pipelineJSON := fmt.Sprintf(`[
    { "$lookup": {
        "from": "another_collection",
        "localField": "_id",
        "foreignField": "interactionID",
        "as": "matched_docs"
    }},
    { "$match": { "matched_docs": { "$eq": [] } } },
    { "$project": { "matched_docs": 0 } },
    { "$match": {"dateTimeGMT":{"$lt": "%s"}} }
]`, time.Now().AddDate(0, 0, -1).UTC().Format(time.RFC3339))
pipeline, err = bson.ParseExtJSONArray(pipelineJSON)

对于我询问的司机,这就是答案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-24
    • 1970-01-01
    • 2019-12-04
    • 2017-04-10
    • 1970-01-01
    • 2021-06-26
    相关资源
    最近更新 更多