【问题标题】:How to control the projection definition in MongoDb using C#如何使用 C# 在 MongoDb 中控制投影定义
【发布时间】:2021-06-14 15:18:51
【问题描述】:

我有一个这样的域类。

public class Thing
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public Dictionary<string, string> Stuff { get; set; }
}

我正在使用以下方法从我的数据库中检索它。

return await _db.Things
    .Find(x => x.Name == name)
    .SingleOrDefaultAsync(token);
    

我注意到,可能有大量不必要的数据,所以我使用了这样的投影。

ProjectionDefinition<Thing> projection = Builders<Thing>
    .Projection
    .Include(a => a.Id)
    .Include(a => a.Name);

BsonDocument projected = await _dbContext.Things
    .Find(x => x.Name == name)
    .Project(projection)
    .SingleOrDefaultAsync(token);

这可行,但自然会删减所有字典内容。我想更改投影的定义以包括该字段,但对构成元素进行过滤。假设我只想引入以 duck 开头的所述字典的键。尝试可能是这样的。

ProjectionDefinition<Thing> projection = Builders<Thing>
    .Projection
    .Include(a => a.Id)
    .Include(a => a.Name)
    .Include(a => a.Stuff.Where(b => b.Key.StartsWith("duck")));

导致异常如下。

System.NotSupportedException:不支持表达式树:{document}{configuration}

鉴于我对 MongoDb 的无知,我不知道是否应该添加一些东西、删除一些东西或完全忘记这个想法。我还尝试使用原始类型以能够以这种方式过滤内容,但我得到的唯一解决方案是后取,基本上是对检索到的数据进行处理。我想降低从数据库到我的服务的负载。

Thing projected = await _dbContext.Things
    .Find(x => x.Name == name)
    .Project<Thing>(projection)
    .SingleOrDefaultAsync(token);

它是否可行,如果可行,如何(或至少谷歌搜索)?

努力证明:code examplesgeneral operationstutorialswrong answers 等。它可能在某个地方,但我没能找到它(或者如果找到了就认出来)。

最后,我陷入了以下困境 - 上帝原谅我,因为我不知道自己在做什么。这是完全正确的方向还是一群疯狂的驴子会为此咬我的下背部?!

ProjectionDefinition<Thing, Thing> wtf = Builders<Thing>.Projection
    .Expression(a => new Thing
    {
        Id = a.Id,
        Name = a.Name,
        Stuff = a.Stuff
            .Where(b => b.Key == scope)
            .ToDictionary(b => scope, b => b.Value)
    });

【问题讨论】:

  • 这似乎有点棘手,我没有太多想法,但.NET 的事情。但我为你找到了一些可能对你有帮助的东西 - mongodb.github.io/mongo-csharp-driver/2.7/reference/driver/…
  • 另请参阅有关您遇到的错误的问题 - stackoverflow.com/questions/55597781/…
  • 请发布带有stuff 字段的示例文档以及您想要的项目内容。
  • @prasad_ 不确定您的要求。样本文件?描述它的类显示在顶部的第一个示例中。字段Stuff 是字符串作为键和字符串作为值的字典。我希望能够检索文档,但只提取字典部分的单个元素,而不是全部。如果我遗漏了什么,请详细说明。
  • 一般来说,根据键名过滤字典(在 MongoDB 中是对象或嵌入文档)的问题是使用聚合查询来解决的。您可以将字典的键值对转换为数组(使用聚合运算符$objectToArray)并通过键名过滤(在您的情况下可以使用正则表达式)数组。

标签: c# mongodb asp.net-core-3.1 projection .net-core-3.1


【解决方案1】:

这是一个使用 MongoDB v4.2.8 的 mongo shell 查询。

考虑这个输入文档:

{
        "_id" : 1,
        "name" : "john",
        "stuff" : {
                "mycolor" : "red",
                "fruit" : "apple",
                "mybook" : "programming gems",
                "movie" : "star wars"
        }
}

目标是投影namestuff 字段,但stuff 的字段名称仅以"my" 开头。

聚合查询:

db.test.aggregate([
  { 
      $project: { 
          _id: 0, 
          name: 1, 
          stuff: { 
              $filter: { 
                  input: { $objectToArray: "$stuff" }, 
                  as: "stf", 
                  cond: { $regexMatch: { input: "$$stf.k" , regex: "^my" } }
              }
          }
      }
  },
  { 
      $addFields: { stuff: { $arrayToObject: "$stuff" } } 
  }
])

还有,预计的输出:

{
        "name" : "john",
        "stuff" : {
                "mycolor" : "red",
                "mybook" : "programming gems"
        }
}

【讨论】:

  • 正则表达式的输入是什么(即 $$stf.k)?我猜它以某种方式与过滤器输出有关,但并不明显。此外, .k 在上下文中没有告诉我任何内容。我错过了重点还是一些特殊的芒果脚本语法?
  • { $objectToArray: "$stuff" } 的结果(见$objectToArray);它是一个数组。例如,每个数组元素类似于{"k" : "mycolor", "v" : "red"},对应键值对"mycolor" : "red"。接下来,使用$filter 迭代数组并匹配正则表达式。在下面的$addFields 中,将该过滤后的数组转换回键值字典。 "stf" 表示 $filter 中使用的每个数组元素。
  • 现在明白了。基于解决方案的复杂性,我将字典更改为数组。这不是我的决定,但我将敦促我的开发主管做出接受数组的决定,而不是威胁他们自己做。我假设你对 Mongo 驱动程序中的实际 C# 语法几乎没有什么贡献。如果我在这一点上错了,请随意使用一些 .NET'y 代码。否则,无论如何,谢谢你的东西。这是很大的帮助。
  • 一两天后提醒我,这样我就不会忘记奖励你。当信用到期时,信用消失很重要。
  • “基于解决方案的复杂性,我将字典更改为数组。” 我认为这是一个很好的设计决策。另请参阅:MongoDB Blog Post - Attribute Pattern。它还可以让您创建索引以进行高效搜索(数组字段上的索引称为多键索引)。
猜你喜欢
  • 2021-10-22
  • 1970-01-01
  • 1970-01-01
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
  • 2017-05-24
  • 1970-01-01
  • 2021-12-16
相关资源
最近更新 更多