获取 listOfStrings 中 subDocument.value 处的所有项目
Get all items where subDocument.value in listOfStrings
我在 dotnetcore 2.1 中使用 MongoDB.Driver nuget 包。我正在尝试 return 集合中的文档列表,其中子文档字段等于我拥有的列表中包含的任何项目。理想情况下,我需要在 dotnetcore 2.1 的 MongoDB.Driver nuget 包的 C# 语法中使用它。
文件
{
"_id" : "e29628a65e914c1e91b3fd9cbf6f2353",
"Enabled" : true,
"Name" : "Document123",
"DisplayName" : "Some Document",
"Description" : "Some Description",
"Data" : [
"lastname",
"email",
"firstname",
"zipcode"
],
"Items" : [
{
"_id" : "1",
"Name" : "bob"
},
{
"_id" : "2",
"Name" : "smith"
}
]
}
如果这是 SQL,这就是我正在尝试做的事情:
SELECT *
FROM Document a, Item b
WHERE a.Id = b.DocumentId AND
b.Name IN ('bob', 'smith', 'alex')
以下是我们所拥有的无法与 MongoDB 驱动程序一起使用的内容:
string[] names = new [] { "bob", "smith", "alex" };
var document = new BsonDocument()
{
new BsonElement("Items.Name", new BsonDocument()
{
new BsonElement("$in", new BsonArray(names))
})
};
var itemsQuery = collection
.Aggregate()
.Match(document)
;
var items = itemsQuery.ToList();
提前致谢。
事实证明我们必须在这之后 "unwind" 和 运行 匹配查询。这是对我们有用的代码。
var collection = GetCollection<Document>();
var document = new BsonDocument()
{
new BsonElement("Name", "Document123"),
new BsonElement("Items.Name", new BsonDocument()
{
new BsonElement("$in", new BsonArray(new [] { "bob", "smith", "alex"}))
})
};
var itemsQuery = collection
.Aggregate()
.Unwind(d => d.Items)
.Match(document)
;
var items = itemsQuery.ToList();
两个初始说明:
a) 您的查询应在开始时包含一个 $match
阶段以加快速度。两次 $match
可能感觉很奇怪,但是对于大量文档,您会发现一个主要差异,因为在 $unwind
阶段您最终得到的文档要少得多。
b) 您可以使用更少的代码编写基于字符串的查询,如下所示:
var itemsQuery = collection
.Aggregate()
.Unwind(document => document.Items)
.Match("{ 'Name' : 'Document123', 'Items.Name' : { $in: [ '" + string.Join("', '", names) + "' ] } }");
但是您可以将所有这些写成一种类型安全的版本(如 "not using strings or BsonDocument
"),如下所示:
/// <summary>
/// This would be just to avoid having to create two almost identical types which, however, could be done as well if you don't like abstract base types
/// </summary>
public abstract class DocumentBase
{
public string Id { get; set; }
public string Name { get; set; }
// this is where all other fields would go like "Enabled", "DisplayName", etc...
}
/// <summary>
/// Represents the <see cref="DocumentBase"/> type but with an unwound <see cref="Items"/> property which is actually a single <see cref="Item"/>,
/// </summary>
public class UnwoundDocument : DocumentBase
{
public Item Items { get; set; }
}
/// <summary>
/// This is the real "Document" type with a <see cref="List{Item}"/> property called <see cref="Items"/>.
/// </summary>
public class Document : DocumentBase
{
public List<Item> Items { get; set; }
}
/// <summary>
/// This would hold all properties of an Item - I've dropped the "Id" property since it's not needed for this sample
/// </summary>
public class Item
{
public string Name { get; set; }
}
有了这些类型,您可以 运行 下面的代码:
public class Program
{
static void Main(string[] args)
{
var collection = new MongoClient().GetDatabase("just_a_test").GetCollection<Document>("Document");
// clear out all existing documents to allow for multiple runs of this sample app
collection.DeleteMany(FilterDefinition<Document>.Empty);
// insert our test document
collection.InsertOne(new Document { Id = "some id", Name = "Document123", Items = new List<Item> { new Item { Name = "bob" }, new Item { Name = "smith" } } });
// create a bunch of filters
var namesToSearchFor = new List<string> { "bob", "smith", "alex" };
Expression<Func<Item, bool>> itemFilter = item => namesToSearchFor.Contains(item.Name);
FilterDefinitionBuilder<Document> fdb = Builders<Document>.Filter;
FilterDefinition<Document> documentFilter = fdb.Eq(f => f.Name, "Document123") & fdb.ElemMatch(f => f.Items, itemFilter);
// construct the aggregation pipeline
IAggregateFluent<UnwoundDocument> pipeline = collection
.Aggregate()
.Match(documentFilter) // filter first for performance reasons (technically not needed)
.Unwind(document => document.Items) // flatten the "Items" array
.As(BsonSerializer.SerializerRegistry.GetSerializer<UnwoundDocument>()) // this is to tell the driver that the resulting document will look like our "UnwoundDocument" type
.Match(d => namesToSearchFor.Contains(d.Items.Name)); // just so we can filter again and use the result in a nicely type-safe manner
// just print out all results for demo purposes
foreach (var result in pipeline.ToList())
{
Console.WriteLine(result.ToJson());
}
}
}
我在 dotnetcore 2.1 中使用 MongoDB.Driver nuget 包。我正在尝试 return 集合中的文档列表,其中子文档字段等于我拥有的列表中包含的任何项目。理想情况下,我需要在 dotnetcore 2.1 的 MongoDB.Driver nuget 包的 C# 语法中使用它。
文件
{
"_id" : "e29628a65e914c1e91b3fd9cbf6f2353",
"Enabled" : true,
"Name" : "Document123",
"DisplayName" : "Some Document",
"Description" : "Some Description",
"Data" : [
"lastname",
"email",
"firstname",
"zipcode"
],
"Items" : [
{
"_id" : "1",
"Name" : "bob"
},
{
"_id" : "2",
"Name" : "smith"
}
]
}
如果这是 SQL,这就是我正在尝试做的事情:
SELECT *
FROM Document a, Item b
WHERE a.Id = b.DocumentId AND
b.Name IN ('bob', 'smith', 'alex')
以下是我们所拥有的无法与 MongoDB 驱动程序一起使用的内容:
string[] names = new [] { "bob", "smith", "alex" };
var document = new BsonDocument()
{
new BsonElement("Items.Name", new BsonDocument()
{
new BsonElement("$in", new BsonArray(names))
})
};
var itemsQuery = collection
.Aggregate()
.Match(document)
;
var items = itemsQuery.ToList();
提前致谢。
事实证明我们必须在这之后 "unwind" 和 运行 匹配查询。这是对我们有用的代码。
var collection = GetCollection<Document>();
var document = new BsonDocument()
{
new BsonElement("Name", "Document123"),
new BsonElement("Items.Name", new BsonDocument()
{
new BsonElement("$in", new BsonArray(new [] { "bob", "smith", "alex"}))
})
};
var itemsQuery = collection
.Aggregate()
.Unwind(d => d.Items)
.Match(document)
;
var items = itemsQuery.ToList();
两个初始说明:
a) 您的查询应在开始时包含一个 $match
阶段以加快速度。两次 $match
可能感觉很奇怪,但是对于大量文档,您会发现一个主要差异,因为在 $unwind
阶段您最终得到的文档要少得多。
b) 您可以使用更少的代码编写基于字符串的查询,如下所示:
var itemsQuery = collection
.Aggregate()
.Unwind(document => document.Items)
.Match("{ 'Name' : 'Document123', 'Items.Name' : { $in: [ '" + string.Join("', '", names) + "' ] } }");
但是您可以将所有这些写成一种类型安全的版本(如 "not using strings or BsonDocument
"),如下所示:
/// <summary>
/// This would be just to avoid having to create two almost identical types which, however, could be done as well if you don't like abstract base types
/// </summary>
public abstract class DocumentBase
{
public string Id { get; set; }
public string Name { get; set; }
// this is where all other fields would go like "Enabled", "DisplayName", etc...
}
/// <summary>
/// Represents the <see cref="DocumentBase"/> type but with an unwound <see cref="Items"/> property which is actually a single <see cref="Item"/>,
/// </summary>
public class UnwoundDocument : DocumentBase
{
public Item Items { get; set; }
}
/// <summary>
/// This is the real "Document" type with a <see cref="List{Item}"/> property called <see cref="Items"/>.
/// </summary>
public class Document : DocumentBase
{
public List<Item> Items { get; set; }
}
/// <summary>
/// This would hold all properties of an Item - I've dropped the "Id" property since it's not needed for this sample
/// </summary>
public class Item
{
public string Name { get; set; }
}
有了这些类型,您可以 运行 下面的代码:
public class Program
{
static void Main(string[] args)
{
var collection = new MongoClient().GetDatabase("just_a_test").GetCollection<Document>("Document");
// clear out all existing documents to allow for multiple runs of this sample app
collection.DeleteMany(FilterDefinition<Document>.Empty);
// insert our test document
collection.InsertOne(new Document { Id = "some id", Name = "Document123", Items = new List<Item> { new Item { Name = "bob" }, new Item { Name = "smith" } } });
// create a bunch of filters
var namesToSearchFor = new List<string> { "bob", "smith", "alex" };
Expression<Func<Item, bool>> itemFilter = item => namesToSearchFor.Contains(item.Name);
FilterDefinitionBuilder<Document> fdb = Builders<Document>.Filter;
FilterDefinition<Document> documentFilter = fdb.Eq(f => f.Name, "Document123") & fdb.ElemMatch(f => f.Items, itemFilter);
// construct the aggregation pipeline
IAggregateFluent<UnwoundDocument> pipeline = collection
.Aggregate()
.Match(documentFilter) // filter first for performance reasons (technically not needed)
.Unwind(document => document.Items) // flatten the "Items" array
.As(BsonSerializer.SerializerRegistry.GetSerializer<UnwoundDocument>()) // this is to tell the driver that the resulting document will look like our "UnwoundDocument" type
.Match(d => namesToSearchFor.Contains(d.Items.Name)); // just so we can filter again and use the result in a nicely type-safe manner
// just print out all results for demo purposes
foreach (var result in pipeline.ToList())
{
Console.WriteLine(result.ToJson());
}
}
}