简体   繁体   English

MongoDB Linq OfType()在字段上

[英]MongoDB Linq OfType() on fields

This is my MongoDB document structure: 这是我的MongoDB文档结构:

{
    string _id;
    ObservableCollection<DataElement> PartData;
    ObservableCollection<DataElement> SensorData;
    ... 
    other ObservableCollection<DataElement> fields
    ...
    other types and fields
    ...
}

Is there any possibility to retrieve a concatenation of fields with the type ObservableCollection<DataElement> ? 是否有可能检索类型为ObservableCollection<DataElement>的字段的串联? Using LINQ I would do something like 使用LINQ,我会做类似的事情

var query = dbCollection
  .AsQueryable()
  .Select(x => new { 
     data = x
       .OfType(typeof(ObservableCollection<DataElement>))
       .SelectMany(x => x)
       .ToList()
   });

or alternatively 或者

data = x.Where(y => typeof(y) == typeof(ObservableCollection<DataElement>)
.SelectMany(x => x).ToList()

Unfortunately .Where() and .OfType() do not work on documents, only on queryables/lists, so is there another possibility to achieve this? 不幸的是.Where().OfType()不适用于文档,仅适用于可查询的对象/列表,那么还有另一种方法可以实现此目的吗? The document structure must stay the same. 文档结构必须保持不变。

Edit: After dnickless answer I tried it with method 1b), which works pretty well for getting the fields thy way they are in the collection. 编辑:经过一丝不苟的回答后,我尝试使用方法1b)进行操作,该方法对于以您所希望的方式将字段放入集合中的效果很好。 Thank you! 谢谢!

Unfortunately it wasn't precisely what I was looking for, as I wanted to be all those fields with that specific type put together in one List, at it would be returned by the OfType or Where(typeof) statement. 不幸的是,这并不是我真正想要的,因为我希望将所有具有该特定类型的字段放到一个List中,这将由OfTypeWhere(typeof)语句返回。

eg data = [x.PartData , x.SensorData, ...] with data being an ObsverableCollection<DataElement>[] , so that I can use SelectMany() on that to finally get the concatenation of all sequences. 例如data = [x.PartData , x.SensorData, ...] ,数据是ObsverableCollection<DataElement>[] ,因此我可以在上面使用SelectMany()来最终获得所有序列的串联。

Sorry for asking the question unprecisely and not including the last step of doing a SelectMany()/Concat() 很抱歉无法精确地提出问题,并且不包括执行SelectMany()/Concat()的最后一步

Finally I found a solution doing this, but it doesn't seem very elegant to me, as it needs one concat() for every element (and I have more of them) and it needs to make a new collection when finding a non-existing field: 最终,我找到了一个解决方案,但是对我来说似乎并不优雅,因为每个元素都需要一个concat() (而且我有更多的元素),并且在查找非元素时需要创建一个新集合。现有领域:

query.Select(x => new
          {
            part = x.PartData ?? new ObservableCollection<DataElement>(),
            sensor = x.SensorData ?? new ObservableCollection<DataElement>(),
          }
      )
      .Select(x => new
          {
             dataElements = x.part.Concat(x.sensor)                                 
           }
      ).ToList()

In order to limit the fields returned you would need to use the MongoDB Projection feature in one way or the other. 为了限制返回的字段,您将需要以一种或另一种方式使用MongoDB Projection功能

There's a few alternatives depending on your specific requirements that I can think of: 根据您的具体要求,我可以考虑几种选择:

Option 1a (fairly static approach): Create a custom type with only the fields that you are interested in if you know them upfront. 选项1a (相当静态的方法):如果您事先知道您感兴趣的字段,则创建一个自定义类型。 Something like this: 像这样:

public class OnlyWhatWeAreInterestedIn
{
    public ObservableCollection<DataElement> PartData { get; set; }
    public ObservableCollection<DataElement> SensorData { get; set; }
    // ...
}

Then you can query your Collection like that: 然后,您可以像这样查询您的收藏集:

var collection = new MongoClient().GetDatabase("test").GetCollection<OnlyWhatWeAreInterestedIn>("test");
var result = collection.Find(FilterDefinition<OnlyWhatWeAreInterestedIn>.Empty);

Using this approach you get a nicely typed result back without the need for custom projections. 使用这种方法,您可以得到很好的输入结果,而无需自定义投影。

Option 1b (still pretty static): A minor variation of Option 1a, just without a new explicit type but a projection stage instead to limit the returned fields. 选项1b (仍然非常静态):选项1a的一个细微变化,只是没有新的显式类型,而是一个投影级来限制返回的字段。 Kind of like that: 有点像:

var collection = new MongoClient().GetDatabase("test").GetCollection<Test>("test");
var result = collection.Find(FilterDefinition<Test>.Empty).Project(t => new { t.PartData, t.SensorData }).ToList();

Again, you get a nicely typed C# entity back that you can continue to operate on. 再次,您会得到一个类型正确的C#实体,您可以继续对其进行操作。

Option 2: Use some dark reflection magic in order to dynamically create a projection stage. 选项2:使用一些深色反射魔术来动态创建投影台。 Downside: You won't get a typed instance reflecting your properties but instead a BsonDocument so you will have to deal with that afterwards. 缺点:您将不会得到一个反映您的属性的类型化实例,而是一个BsonDocument,因此您随后将不得不对其进行处理。 Also, if you have any custom MongoDB mappings in place, you would need to add some code to deal with them. 另外,如果您有任何自定义的MongoDB映射,则需要添加一些代码来处理它们。

Here's the full example code: 这是完整的示例代码:

First, your entities: 首先,您的实体:

public class Test
{
    string _id;
    public ObservableCollection<DataElement> PartData { get; set; }
    public ObservableCollection<DataElement> SensorData { get; set; }
    // just to have one additional property that will not be part of the returned document
    public string TestString { get; set; }
}

public class DataElement
{
}

And then the test program: 然后测试程序:

public class Program
{
    static void Main(string[] args)
    {
        var collection = new MongoClient().GetDatabase("test").GetCollection<Test>("test");

        // insert test record
        collection.InsertOne(
            new Test
            {
                PartData = new ObservableCollection<DataElement>(
                    new ObservableCollection<DataElement>
                    {
                        new DataElement(),
                        new DataElement()
                    }),
                SensorData = new ObservableCollection<DataElement>(
                    new ObservableCollection<DataElement>
                    {
                        new DataElement(),
                        new DataElement()
                    }),
                TestString = "SomeString"
            });

        // here, we use reflection to find the relevant properties
        var allPropertiesThatWeAreLookingFor = typeof(Test).GetProperties().Where(p => typeof(ObservableCollection<DataElement>).IsAssignableFrom(p.PropertyType));

        // create a string of all properties that we are interested in with a ":1" appended so MongoDB will return these fields only
        // in our example, this will look like
        // "PartData:1,SensorData:1"
        var mongoDbProjection = string.Join(",", allPropertiesThatWeAreLookingFor.Select(p => $"{p.Name}:1"));

        // we do not want MongoDB to return the _id field because it's not of the selected type but would be returned by default otherwise
        mongoDbProjection += ",_id:0";

        var result = collection.Find(FilterDefinition<Test>.Empty).Project($"{{{mongoDbProjection}}}").ToList();

        Console.ReadLine();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM