在 C# 中将 MongoDB 文档导出到 CSV
Exporting MongoDB Documents to CSV in C#
我想使用 C# 从 MongoDB.Driver 的 IMongoCollection 的项目中导出 CSV table。
我怎样才能有效地做到这一点?我正在考虑通过从集合中检索文档并将它们转换为类似 JSON 的格式或使用 StringBuilder 创建 CSV 文件并使用 PropertyInfo 数组来访问检索到的对象的字段。
有人可以举例说明我如何做到这一点吗?
似乎最明显的方法是以某种方式获取所有 header 数据(见下文),然后遍历 collection,如果你要手写(人们不会t鼓励),字符串构建,批量写入文件(如果你的collection非常大)。
HashSet<string> fields = new HashSet<string>();
BsonDocument query = BsonDocument.Parse(filter);
var result = database.GetCollection<BsonDocument>(collection).Find(new BsonDocument());
// Populate fields with all unique fields, see below for examples how.
var csv = new StringBuilder();
string headerLine = string.Join(",", fields);
csv.AppendLine(headerLine);
foreach (var element in result.ToListAsync().Result)
{
string line = null;
foreach (var field in fields)
{
BsonValue value;
if (field.Contains("."))
{
value = GetNestedField(element, field);
}
else
{
value = element.GetElement(field).Value;
}
// Example deserialize to string
switch (value.BsonType)
{
case BsonType.ObjectId:
line = line + value.ToString();
break;
case BsonType.String:
line = line + value.ToString();
break;
case BsonType.Int32:
line = line + value.AsInt32.ToString();
break;
}
line = line + ",";
}
csv.AppendLine(line);
}
File.WriteAllText("D:\temp.csv", csv.ToString());
对于您自己的 objects,您必须使用自己的反序列化器。
HOWEVER 如果可以的话,我建议您使用 mongoexport 工具。
您可以简单地 运行 来自您的应用程序的 exe,根据需要输入参数。但请记住,它需要显式字段。
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "C:\mongodb\bin\mongoexport.exe";
startInfo.Arguments = "-d testDB -c testCollection --type csv --fields name,address.street,address.zipCode --out .\output.csv";
startInfo.UseShellExecute = false;
Process exportProcess= new Process();
exportProcess.StartInfo = startInfo;
exportProcess.Start();
exportProcess.WaitForExit();
有关 mongoexport 的更多信息,例如分页、附加查询和字段文件:
https://docs.mongodb.com/manual/reference/program/mongoexport/
获取唯一字段名称
要查找所有字段名称,您可以通过多种方式进行。使用 BsonDocument 作为通用数据示例。
- 递归遍历您的 IMongoCollection 结果。这将必须贯穿整个 collection,因此性能可能不会很好。
示例:
HashSet<string> fields = new HashSet<string>();
var result = database.GetCollection<BsonDocument>(collection).Find(new BsonDocument());
var result = database.GetCollection<BsonDocument>(collection).Find(new BsonDocument());
foreach (var element in result.ToListAsync().Result)
{
ProcessTree(fields, element, "");
}
private void ProcessTree(HashSet<string> fields, BsonDocument tree, string parentField)
{
foreach (var field in tree)
{
string fieldName = field.Name;
if (parentField != "")
{
fieldName = parentField + "." + fieldName;
}
if (field.Value.IsBsonDocument)
{
ProcessTree(fields, field.Value.ToBsonDocument(), fieldName);
}
else
{
fields.Add(fieldName);
}
}
}
- 对 return 所有字段执行 MapReduce 操作。但是,使用此方法扫描嵌套字段变得更加复杂。参见 this。
示例:
string map = @"function() {
for (var key in this) { emit(key, null); }
}";
string reduce = @"function(key, stuff) { return null; }";
string finalize = @"function(key, value){
return key;
}";
MapReduceOptions<BsonDocument, BsonValue> options = new MapReduceOptions<BsonDocument, BsonValue>();
options.Finalize = new BsonJavaScript(finalize);
var results = database.GetCollection<BsonDocument>(collection).MapReduceAsync(
new BsonJavaScript(map),
new BsonJavaScript(reduce),
options).Result.ToListAsync().Result;
foreach (BsonValue result in results.Select(item => item["_id"]))
{
Debug.WriteLine(result.AsString);
}
- 执行聚合操作。您需要根据需要展开多次以获得所有嵌套字段。
示例:
string[] pipeline = new string[3];
pipeline[0] = "{ '$project':{ 'arrayofkeyvalue':{ '$objectToArray':'$$ROOT'}}}";
pipeline[1] = "{ '$unwind':'$arrayofkeyvalue'}";
pipeline[2] = "{ '$group':{'_id':null,'fieldKeys':{'$addToSet':'$arrayofkeyvalue.k'}}}";
var stages = pipeline.Select(s => BsonDocument.Parse(s)).ToList();
var result = await database.GetCollection<BsonDocument>(collection).AggregateAsync<BsonDocument>(stages);
foreach (BsonValue fieldName in result.Single().GetElement("fieldKeys").Value.AsBsonArray)
{
Debug.WriteLine(fieldName.AsString);
}
这里没有完美的东西,我无法告诉你哪个是最有效的,但希望能有所帮助。
我想使用 C# 从 MongoDB.Driver 的 IMongoCollection 的项目中导出 CSV table。
我怎样才能有效地做到这一点?我正在考虑通过从集合中检索文档并将它们转换为类似 JSON 的格式或使用 StringBuilder 创建 CSV 文件并使用 PropertyInfo 数组来访问检索到的对象的字段。
有人可以举例说明我如何做到这一点吗?
似乎最明显的方法是以某种方式获取所有 header 数据(见下文),然后遍历 collection,如果你要手写(人们不会t鼓励),字符串构建,批量写入文件(如果你的collection非常大)。
HashSet<string> fields = new HashSet<string>();
BsonDocument query = BsonDocument.Parse(filter);
var result = database.GetCollection<BsonDocument>(collection).Find(new BsonDocument());
// Populate fields with all unique fields, see below for examples how.
var csv = new StringBuilder();
string headerLine = string.Join(",", fields);
csv.AppendLine(headerLine);
foreach (var element in result.ToListAsync().Result)
{
string line = null;
foreach (var field in fields)
{
BsonValue value;
if (field.Contains("."))
{
value = GetNestedField(element, field);
}
else
{
value = element.GetElement(field).Value;
}
// Example deserialize to string
switch (value.BsonType)
{
case BsonType.ObjectId:
line = line + value.ToString();
break;
case BsonType.String:
line = line + value.ToString();
break;
case BsonType.Int32:
line = line + value.AsInt32.ToString();
break;
}
line = line + ",";
}
csv.AppendLine(line);
}
File.WriteAllText("D:\temp.csv", csv.ToString());
对于您自己的 objects,您必须使用自己的反序列化器。
HOWEVER 如果可以的话,我建议您使用 mongoexport 工具。 您可以简单地 运行 来自您的应用程序的 exe,根据需要输入参数。但请记住,它需要显式字段。
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "C:\mongodb\bin\mongoexport.exe";
startInfo.Arguments = "-d testDB -c testCollection --type csv --fields name,address.street,address.zipCode --out .\output.csv";
startInfo.UseShellExecute = false;
Process exportProcess= new Process();
exportProcess.StartInfo = startInfo;
exportProcess.Start();
exportProcess.WaitForExit();
有关 mongoexport 的更多信息,例如分页、附加查询和字段文件: https://docs.mongodb.com/manual/reference/program/mongoexport/
获取唯一字段名称
要查找所有字段名称,您可以通过多种方式进行。使用 BsonDocument 作为通用数据示例。
- 递归遍历您的 IMongoCollection 结果。这将必须贯穿整个 collection,因此性能可能不会很好。
示例:
HashSet<string> fields = new HashSet<string>();
var result = database.GetCollection<BsonDocument>(collection).Find(new BsonDocument());
var result = database.GetCollection<BsonDocument>(collection).Find(new BsonDocument());
foreach (var element in result.ToListAsync().Result)
{
ProcessTree(fields, element, "");
}
private void ProcessTree(HashSet<string> fields, BsonDocument tree, string parentField)
{
foreach (var field in tree)
{
string fieldName = field.Name;
if (parentField != "")
{
fieldName = parentField + "." + fieldName;
}
if (field.Value.IsBsonDocument)
{
ProcessTree(fields, field.Value.ToBsonDocument(), fieldName);
}
else
{
fields.Add(fieldName);
}
}
}
- 对 return 所有字段执行 MapReduce 操作。但是,使用此方法扫描嵌套字段变得更加复杂。参见 this。
示例:
string map = @"function() {
for (var key in this) { emit(key, null); }
}";
string reduce = @"function(key, stuff) { return null; }";
string finalize = @"function(key, value){
return key;
}";
MapReduceOptions<BsonDocument, BsonValue> options = new MapReduceOptions<BsonDocument, BsonValue>();
options.Finalize = new BsonJavaScript(finalize);
var results = database.GetCollection<BsonDocument>(collection).MapReduceAsync(
new BsonJavaScript(map),
new BsonJavaScript(reduce),
options).Result.ToListAsync().Result;
foreach (BsonValue result in results.Select(item => item["_id"]))
{
Debug.WriteLine(result.AsString);
}
- 执行聚合操作。您需要根据需要展开多次以获得所有嵌套字段。
示例:
string[] pipeline = new string[3];
pipeline[0] = "{ '$project':{ 'arrayofkeyvalue':{ '$objectToArray':'$$ROOT'}}}";
pipeline[1] = "{ '$unwind':'$arrayofkeyvalue'}";
pipeline[2] = "{ '$group':{'_id':null,'fieldKeys':{'$addToSet':'$arrayofkeyvalue.k'}}}";
var stages = pipeline.Select(s => BsonDocument.Parse(s)).ToList();
var result = await database.GetCollection<BsonDocument>(collection).AggregateAsync<BsonDocument>(stages);
foreach (BsonValue fieldName in result.Single().GetElement("fieldKeys").Value.AsBsonArray)
{
Debug.WriteLine(fieldName.AsString);
}
这里没有完美的东西,我无法告诉你哪个是最有效的,但希望能有所帮助。