在C#的MongoDB驱动程序中使用$ addFields

时间:2019-01-22 10:06:44

标签: c# mongodb

我知道这不是一项功能,因此不会按照此处所述进行实现:https://jira.mongodb.org/browse/CSHARP-1750

但是,我确实需要使用$ addFields运算符执行聚合。 根据对How to use Addfields in MongoDB C# Aggregation Pipeline

的评论
  

jira.mongodb.org/browse/CSHARP-1750。阅读解决方案“无法解决”。   给出了推理,但显然根本不在路线图上。如果   您真的想要它,然后手动指定管道   BsonDocument构建器,因为这实际上是所有API方法   无论如何。和/或投票支持JIRA问题,并提供足够的支持   那么也许有人会认为值得为以后的工作考虑。 –   Neil Lunn 18年11月3日

您可以手动构建管道。我将如何去做,是否可以使用之前,甚至可能在手动字符串之后的聚合方式,所以我不必手工构建整个东西,而只需构建addFields部分?

我尝试过

StringBuilder addFieldsDefinition = new StringBuilder();
addFieldsDefinition.AppendLine("{");
addFieldsDefinition.AppendLine("\"values"":{$reduce: {");
addFieldsDefinition.AppendLine("input: \"$values\",");
addFieldsDefinition.AppendLine("initialValue: {timeStamp: ISODate(\"0000-01-01T00:00:00.000+0000\")},");
addFieldsDefinition.AppendLine("in: {$cond: [{$and : [");
addFieldsDefinition.AppendLine("{$gte : [\"$$this.timeStamp\", \"$$value.timeStamp\"]},");
addFieldsDefinition.AppendLine("{$lte : [\"$$this.timeStamp\", ISODate(\"" + dt.ToString("yyyy-MM-dd") + "T" + dt.ToString("HH:mm:ss.fff") + "\")]}");
addFieldsDefinition.AppendLine("]}, \"$$this"", \"$$value\"]}");
addFieldsDefinition.AppendLine("}}");
addFieldsDefinition.AppendLine("}");

IAggregateFluent<BsonDocument> aggregate = col.Aggregate()
  .Match(filterDef)
  .Project(projectDef);
aggregate.Stages.Add("$addFields : " + addFieldsDefinition .ToString());

想要获取数组中的元素,其中子文档中的“ timeStamp”字段最高,但在指定的dateTime下。 但是,当我尝试添加阶段时,该代码向我抛出了一个异常,表示无法将字符串转换为IPipelineStageDefinition。

我不想做这样的内置聚合(伪代码)

.Unwind(values).Match(timestamp < dt).Sort(timeStamp).Limit(1)

因为那太慢了。

编辑:

我现在使用MongoDB.Bson对象创建舞台: VB.NET代码(对不起,我不愿意手动转换该烂摊子)

Dim stage As New BsonDocument(New BsonElement("$addFields", New BsonDocument(New BsonElement("value",
    New BsonDocument(New BsonElement("$reduce", New BsonDocument(New List(Of BsonElement) From
        {
            New BsonElement("input", New BsonString("$" + FieldNames.VALUES_FIELDNAME)),
            New BsonElement("initialValue", New BsonDocument(New BsonElement("timeStamp", New BsonDateTime(DateTime.MaxValue)))),
            New BsonElement("in", New BsonDocument(New List(Of BsonElement) From
                {
                    New BsonElement("$cond", New BsonArray() From
                    {
                        New BsonDocument(New BsonElement("$and", New BsonArray() From
                        {
                            New BsonDocument(New BsonElement("$lte", New BsonArray() From {New BsonString("$$this.timeStamp"), New BsonString("$$value.timeStamp")})),
                            New BsonDocument(New BsonElement("$gte", New BsonArray() From {New BsonString("$$this.timeStamp"), New BsonDateTime(dt)}))
                        })),
                        New BsonString("$$this"),
                        New BsonString("$$value")
                    })
                }
            ))
        }
    ))))
)))

1 个答案:

答案 0 :(得分:2)

BsonDocument可以转换为IPipelineStageDefinition。要获取您想要的BsonDocument:

var addFieldsDefinitionDoc = BsonDocument.Parse(addFieldsDefinition.ToString());
var stageElement = new BsonElement("$addFields", addFieldsDefinitionDoc);
var stage = new BsonDocument(stageElement)

然后添加它只需使用:

aggregate = aggregate.AppendStage(stage);

我不确定aggregate.Stages.Add是否会做同样的事情,但是我认为 AppendStage可能是更好的方法(尽管我没有找到任何文档可以告诉我做大多数事情的正确方法是什么,所以这是通过反复试验并检查源代码以了解事情如何工作而发现的)。

您甚至可以使用常规的阶段构建器功能添加其他阶段,如下所示:

aggregate = aggregate.Project(projectionDefinition);

甚至

aggregate = aggregate
    .AppendStage(stage)
    .Project(projectionDefinition);