我可能会或可能不会正确地做事,所以如果问题不是可回答的问题,请随意提供更好的方法,但事情可以更好地组织。
我有一个课程:资源
public class Resources
{
public int Id....
...... simplified for readability.
public String Url......
public DateTime publishDateTime....
}
我还有一个标签列表
public class Tags
{
public int Id....
...... simplified for readability.
public String Tagname......
}
这是多对多的所以我还有更多类TagResources(简化)
public class TagResources
{
public int TagResourceId....
...... simplified for readability.
public int TagId......
public int ResourceId......
}
每个资源可以包含多个(最多3个)标记,但是我希望能够检索所有标记存在的所有资源。
示例表:
+-----+----------------+--------------------+
+ Id + TagId + ResourceId +
+-----+----------------+--------------------+
+ 1 + 1 + 1 +
+-----+----------------+--------------------+
+ 2 + 2 + 1 +
+-----+----------------+--------------------+
+ 3 + 3 + 1 +
+-----+----------------+--------------------+
+ 4 + 1 + 2 +
+-----+----------------+--------------------+
+ 5 + 2 + 2 +
+-----+----------------+--------------------+
+ 6 + 4 + 2 +
+-----+----------------+--------------------+
+ 7 + 1 + 3 +
+-----+----------------+--------------------+
+ 8 + 2 + 3 +
+-----+----------------+--------------------+
+ 8 + 3 + 3 +
+-----+----------------+--------------------+
+ 8 + 4 + 3 +
+-----+----------------+--------------------+
因此,例如,我想获取所有具有标记1,2和3的资源,它将返回ResourceId为1和3。
如果我想获得所有包含标签1,2和4的资源,它将返回ResourceId为2和3。
所以从本质上讲,我如何获得所有资源,相应的tagId位于所选列表中。
目前,我得到第一个标签匹配的所有行;
var resourceIds = _context.TagResources.Distinct()
.Include(m => m.Tag)
.Where(p => tags.Contains(p.Tag.Name)).Select(p => p.ResourceId);
每个资源最多有三个唯一标记,因此我使用它来仅检索具有第二个标记的标记,然后检索具有第三个标记的标记,然后将它们添加到新列表中。
这是非常令人费解的,我在这方面不是很有经验(你可以看到)。然而,除了剩下的,我需要OrderBy publishDateTime然后Take(20)。
有人能引导我朝着正确的方向前进吗?
我正在使用带有EF的ASP.NET MVC,但任何有关SQL的指针都会有所帮助。
答案 0 :(得分:1)
你应该可以使用group by来解决这个问题。我为TagResources创建了一个类,然后创建了一个与您提供的数据匹配的列表。
var resources = new List<TagResource>()
{
new TagResource() {Id = 1, TagId = 1, ResourceId = 1},
new TagResource() {Id = 2, TagId = 2, ResourceId = 1},
new TagResource() {Id = 3, TagId = 3, ResourceId = 1},
new TagResource() {Id = 4, TagId = 1, ResourceId = 2},
new TagResource() {Id = 5, TagId = 2, ResourceId = 2},
new TagResource() {Id = 6, TagId = 4, ResourceId = 2},
new TagResource() {Id = 7, TagId = 1, ResourceId = 3},
new TagResource() {Id = 8, TagId = 2, ResourceId = 3},
new TagResource() {Id = 9, TagId = 3, ResourceId = 3},
new TagResource() {Id = 10, TagId = 4, ResourceId = 3},
};
使用它作为我的数据源我想出了这个:
var input = new int[] { 1, 2, 3 }; // the ids to search for. you may change these anyway you want
var results = resources.GroupBy(x => x.ResourceId, r => new { TagId = r.TagId })
.Where(x => input.All(x.Select(r => r.TagId).Contains)); // performs the group by and filters out results that don't include all the keys
foreach (var result in results)
{
Console.WriteLine(result.Key);
}
outputs:
//1
//3
答案 1 :(得分:1)
这并不像看起来那么简单。您无法使用此查询...
var tagIds = new int[] { 1, 2, 3 };
var resources = _context.Resources
.Where(r => r.TagResources
.All(tr => tagIds.Contains(tr.TagId)));
...因为它只返回所有标签都在tagIds
的资源,所以资源3不符合条件。
相反,您必须找到其标记包含tagIds
的资源:
var resources = _context.Resources
.Where(r => tagIds
.All(id => r.TagResources
.Select(tr => tr.TagId)
.Contains(id)));
您会注意到这会产生不成比例的SQL,因为EF必须将tagIds
转换为各种SQL表。但是,有了这个有限的Id值,这绝对没有问题。