如何使用 MongoDB C# Driver 2.0 创建流畅的聚合
How to create a fluent Aggregation using MongoDB C# Driver 2.0
我对 MongoDB 很陌生,我在 Web Api 中使用它来为移动应用程序提供服务。
现在,我需要 运行 一个聚合,因为我使用的是 C#,我想通过对 return 的集合使用 Aggregate
命令来流畅地完成它我是 IAggregateFluent
.
但是,我被卡住了,我在 SO 上找到的信息对我没有帮助,因此有一个新问题。
我建立了一个小型集合,其中包含具有一些基本属性的智能手机,智能手机集合中的单个项目看起来像:
{
"name" : "LG Nexus 5",
"description" : "A Nexus 5 device, created by Google.",
"typenr" : "LG-NEX-5/WHITE",
"props" : [
{
"type" : "os",
"value" : "Android"
},
{
"type" : "storage",
"value" : "8"
},
{
"type" : "storage",
"value" : "16"
},
{
"type" : "storage",
"value" : "32"
},
{
"type" : "storage",
"value" : "64"
}
]
}
现在,我在 shell 中创建了一个聚合,如下所示:
// Get all the amount of filters that are defined.
db.smartphones.aggregate([
// Unwind the "props".
{ "$unwind" : "$props" },
// Grouping phase.
// Group by unique properties, add a count for the amount of articles, and add articles to an element named "articles".
// We use the function "$addToSet" here to ensure that only unique articles are being added.
{
"$group" : {
"_id" : "$props",
count : { "$sum" : 1 },
articles: {
"$addToSet": {
name: "$name",
description: "$description",
typenr: "$typenr"
} x =>
}
}
},
// Sort the results based on the "_id" field.
{ "$sort" : { "_id" : 1 } }
]);
现在我需要将它翻译成 C#。
首先,我创建了以下代码(纯 C# 代码,它只是 return 一个 IMongoCollection<Article>
)。
var collection = context.ArticleRepository;
这是集合所做的模型 return:
public class Article
{
#region Properties
/// <summary>
/// Unique identifier for the article.
/// </summary>
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
/// <summary>
/// Name of the article.
/// </summary>
[BsonElement("name")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Name { get; set; }
/// <summary>
/// Name of the element but in lowercase.
/// </summary>
/// <remarks>
/// We'll create this field to enable text-search on this field without respecting capital letters.
/// </remarks>
[BsonElement("namelc")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString LowercaseName { get; set; }
/// <summary>
/// Specification of the article.
/// </summary>
[BsonElement("specification")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Specificiation { get; set; }
/// <summary>
/// Brand of the article.
/// </summary>
[BsonElement("brand")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Brand { get; set; }
/// <summary>
/// Supplier of the article.
/// </summary>
[BsonElement("supplier")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public Supplier Supplier { get; set; }
/// <summary>
/// Number of the article.
/// </summary>
[BsonElement("nr")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString ArticleNumber { get; set; }
/// <summary>
/// Gtin number of the article.
/// </summary>
[BsonElement("gtin")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public string ArticleGtin { get; set; }
/// <summary>
/// type number of the article.
/// </summary>
[BsonElement("typeNr")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public string TypeNumber { get; set; }
/// <summary>
/// Properties of the article.
/// </summary>
/// <remarks>
/// This field can be used to ensure that we can filter on the articles.
/// By default, this is an empty list, this avoids initialization logic.
/// </remarks>
[BsonElement("props")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public List<ArticleProperty> Properties { get; set; } = new List<ArticleProperty>();
#endregion
}
/// <summary>
/// Class representing a single supplier in the database.
/// </summary>
/// <remarks>
/// This class is not used as a "root" document inside our database.
/// Instead, it's being embedded into our "Articles" document.
/// </remarks>
public class Supplier
{
#region Properties
/// <summary>
/// Name of the supplier.
/// </summary>
[BsonElement("supplier")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Name { get; set; }
/// <summary>
/// Gln of the supplier.
/// </summary>
[BsonElement("gln")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Gln { get; set; }
#endregion
}
/// <summary>
/// Class representing a single property for an article in the database.
/// </summary>
/// <remarks>
/// This class is not used as a "root" document inside our database.
/// Instead, it's being embedded into our "Articles" document.
/// </remarks>
public class ArticleProperty
{
#region Properties
/// <summary>
/// Type of the property.
/// </summary>
[BsonElement("type")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Type { get; set; }
/// <summary>
/// Value of the property.
/// </summary>
[BsonElement("value")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Value { get; set; }
#endregion
}
现在,我需要汇总这个集合,我已经在为基础知识苦苦挣扎了:
// Build the aggregation using the fluent api.
var aggregation = collection.Aggregate()
.Unwind(x => x.Properties)
.Group(x => new { x.Properties );
现在,我只尝试对属性进行分组,就像在聚合中一样,但这会导致以下错误:
CS0411 The type arguments for method 'IAggregateFluent<BsonDocument>.Group<TNewResult>(ProjectionDefinition<BsonDocument, TNewResult>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
但即使这样可行,我也需要 count
和 addToSet
等额外属性。有人可以帮我解决这个问题吗?
我已经为此搜索了 2 天,快把我逼疯了。
编辑
我发现在 C# 中确实可以在一个组后跟一个展开,但为什么它不能先与展开一起使用?我真的需要先放松一下。
编辑 2
我设法让一小部分工作,包括 group
命令。请看下面的代码:
var aggregation = collection.Aggregate()
.Unwind<Smartphone, UnwindedSmartphone>(x => x.Properties)
.Group(key => key.Property, g => new
{
Id = g.Key,
Count = g.Count()
});
但是,我需要更多关于如何从聚合命令推送文章 属性 的信息。
我找到了问题的解决方案。
应使用以下 C# 代码:
var aggregation = collection.Aggregate()
.Unwind<Smartphone, UnwindedSmartphone>(x => x.Properties)
.Group(key => key.Property, g => new
{
Id = g.Key,
Count = g.Count(),
Articles = g.Select(x => new
{
Name = x.Name
}).Distinct()
})
.SortBy(x => x.Id);
这给了我以下聚合:
db.smartphones.aggregate([{ "$unwind" : "$props" }, { "$group" : { "_id" : "$props", "Count" : { "$sum" : 1 }, "Articles" : { "$addToSet" : { "Name" : "$name" } } } }, { "$sort" : { "_id" : 1 } }])
我对 MongoDB 很陌生,我在 Web Api 中使用它来为移动应用程序提供服务。
现在,我需要 运行 一个聚合,因为我使用的是 C#,我想通过对 return 的集合使用 Aggregate
命令来流畅地完成它我是 IAggregateFluent
.
但是,我被卡住了,我在 SO 上找到的信息对我没有帮助,因此有一个新问题。
我建立了一个小型集合,其中包含具有一些基本属性的智能手机,智能手机集合中的单个项目看起来像:
{
"name" : "LG Nexus 5",
"description" : "A Nexus 5 device, created by Google.",
"typenr" : "LG-NEX-5/WHITE",
"props" : [
{
"type" : "os",
"value" : "Android"
},
{
"type" : "storage",
"value" : "8"
},
{
"type" : "storage",
"value" : "16"
},
{
"type" : "storage",
"value" : "32"
},
{
"type" : "storage",
"value" : "64"
}
]
}
现在,我在 shell 中创建了一个聚合,如下所示:
// Get all the amount of filters that are defined.
db.smartphones.aggregate([
// Unwind the "props".
{ "$unwind" : "$props" },
// Grouping phase.
// Group by unique properties, add a count for the amount of articles, and add articles to an element named "articles".
// We use the function "$addToSet" here to ensure that only unique articles are being added.
{
"$group" : {
"_id" : "$props",
count : { "$sum" : 1 },
articles: {
"$addToSet": {
name: "$name",
description: "$description",
typenr: "$typenr"
} x =>
}
}
},
// Sort the results based on the "_id" field.
{ "$sort" : { "_id" : 1 } }
]);
现在我需要将它翻译成 C#。
首先,我创建了以下代码(纯 C# 代码,它只是 return 一个 IMongoCollection<Article>
)。
var collection = context.ArticleRepository;
这是集合所做的模型 return:
public class Article
{
#region Properties
/// <summary>
/// Unique identifier for the article.
/// </summary>
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
/// <summary>
/// Name of the article.
/// </summary>
[BsonElement("name")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Name { get; set; }
/// <summary>
/// Name of the element but in lowercase.
/// </summary>
/// <remarks>
/// We'll create this field to enable text-search on this field without respecting capital letters.
/// </remarks>
[BsonElement("namelc")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString LowercaseName { get; set; }
/// <summary>
/// Specification of the article.
/// </summary>
[BsonElement("specification")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Specificiation { get; set; }
/// <summary>
/// Brand of the article.
/// </summary>
[BsonElement("brand")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Brand { get; set; }
/// <summary>
/// Supplier of the article.
/// </summary>
[BsonElement("supplier")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public Supplier Supplier { get; set; }
/// <summary>
/// Number of the article.
/// </summary>
[BsonElement("nr")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString ArticleNumber { get; set; }
/// <summary>
/// Gtin number of the article.
/// </summary>
[BsonElement("gtin")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public string ArticleGtin { get; set; }
/// <summary>
/// type number of the article.
/// </summary>
[BsonElement("typeNr")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public string TypeNumber { get; set; }
/// <summary>
/// Properties of the article.
/// </summary>
/// <remarks>
/// This field can be used to ensure that we can filter on the articles.
/// By default, this is an empty list, this avoids initialization logic.
/// </remarks>
[BsonElement("props")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public List<ArticleProperty> Properties { get; set; } = new List<ArticleProperty>();
#endregion
}
/// <summary>
/// Class representing a single supplier in the database.
/// </summary>
/// <remarks>
/// This class is not used as a "root" document inside our database.
/// Instead, it's being embedded into our "Articles" document.
/// </remarks>
public class Supplier
{
#region Properties
/// <summary>
/// Name of the supplier.
/// </summary>
[BsonElement("supplier")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Name { get; set; }
/// <summary>
/// Gln of the supplier.
/// </summary>
[BsonElement("gln")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Gln { get; set; }
#endregion
}
/// <summary>
/// Class representing a single property for an article in the database.
/// </summary>
/// <remarks>
/// This class is not used as a "root" document inside our database.
/// Instead, it's being embedded into our "Articles" document.
/// </remarks>
public class ArticleProperty
{
#region Properties
/// <summary>
/// Type of the property.
/// </summary>
[BsonElement("type")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Type { get; set; }
/// <summary>
/// Value of the property.
/// </summary>
[BsonElement("value")]
[BsonIgnoreIfNull]
[BsonIgnoreIfDefault]
public BsonString Value { get; set; }
#endregion
}
现在,我需要汇总这个集合,我已经在为基础知识苦苦挣扎了:
// Build the aggregation using the fluent api.
var aggregation = collection.Aggregate()
.Unwind(x => x.Properties)
.Group(x => new { x.Properties );
现在,我只尝试对属性进行分组,就像在聚合中一样,但这会导致以下错误:
CS0411 The type arguments for method 'IAggregateFluent<BsonDocument>.Group<TNewResult>(ProjectionDefinition<BsonDocument, TNewResult>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
但即使这样可行,我也需要 count
和 addToSet
等额外属性。有人可以帮我解决这个问题吗?
我已经为此搜索了 2 天,快把我逼疯了。
编辑
我发现在 C# 中确实可以在一个组后跟一个展开,但为什么它不能先与展开一起使用?我真的需要先放松一下。
编辑 2
我设法让一小部分工作,包括 group
命令。请看下面的代码:
var aggregation = collection.Aggregate()
.Unwind<Smartphone, UnwindedSmartphone>(x => x.Properties)
.Group(key => key.Property, g => new
{
Id = g.Key,
Count = g.Count()
});
但是,我需要更多关于如何从聚合命令推送文章 属性 的信息。
我找到了问题的解决方案。 应使用以下 C# 代码:
var aggregation = collection.Aggregate()
.Unwind<Smartphone, UnwindedSmartphone>(x => x.Properties)
.Group(key => key.Property, g => new
{
Id = g.Key,
Count = g.Count(),
Articles = g.Select(x => new
{
Name = x.Name
}).Distinct()
})
.SortBy(x => x.Id);
这给了我以下聚合:
db.smartphones.aggregate([{ "$unwind" : "$props" }, { "$group" : { "_id" : "$props", "Count" : { "$sum" : 1 }, "Articles" : { "$addToSet" : { "Name" : "$name" } } } }, { "$sort" : { "_id" : 1 } }])