SlashJ
SlashJ

Reputation: 807

c# MongoDB Driver, retrieve object from a nested Array

In C# using the .NET MongoDB driver, I am trying to retrieve objects that are in a nested array of a document and that match some criteria.

Models:

  public class MainElement
  {
    [BsonId]
    public ObjectId DocumentId { get; set; }
    public string A { get; set; }
    public string B { get; set; }
    public List<NestedElement> NestedObjects { get; set; }
  }

  public class NestedElement
  {
    public DateTime Date { get; set; }

    public string W { get; set; }
    public long Y { get; set; }
  }

What I want to retrieve is the property A from MainElement and properties Date, W, Y from NestedElement for all documents thats matches my filters.

So far I tried this but it returns the full MainElement document:

  var filters = Builders<MainElement>.Filter.Eq("A", "Something") &
                Builders<MainElement>.Filter.Eq("B", "AnotherThing") &
                Builders<MainElement>.Filter.Gte("NestedObjects.Date", day) &
                Builders<MainElement>.Filter.Eq("NestedObjects.W", "ABC") &
                Builders<MainElement>.Filter.Gte("NestedObjects.Y", 1500);

  var projection = Builders<MainElement>.Projection.Include("A")
                                                   .Include("NestedObjects.Date")
                                                   .Include("NestedObjects.W")
                                                   .Include("NestedObjects.Y");

  var elements = Collection.Find(filters).Project(projection).ToList();

Any ideas?

Upvotes: 1

Views: 3615

Answers (2)

ProgrammingLlama
ProgrammingLlama

Reputation: 38880

You should be able to write your query like this:

// We need the NestedElement filter twice: once to filter documents and once to filter the array in the projection
var nestedFilter = Builders<NestedElement>.Filter.Gte(n => n.Date, day)
                    & Builders<NestedElement>.Filter.Eq(n => n.W, "ABC")
                    & Builders<NestedElement>.Filter.Eq(n => n.Y, 1500);



var elements = Collection
    .Find(
        Builders<MainElement>.Filter.Eq(e => e.A, "Something")
        & Builders<MainElement>.Filter.Eq(e => e.B, "AnotherThing")
        // match only documents that have matching nested array elements
        & Builders<MainElement>.Filter.ElemMatch(e => e.NestedObjects, nestedFilter)
    )
    .Project(
        Builders<MainElement>.Projection
            .Include(e => e.A)
            // include only matching elements in the resulting object
            .ElemMatch(e => e.NestedObjects, nestedFilter)
    )
    .ToList();

I've used ElemMatch in the document filter because, from your comments, that seems to be what you actually want. For example: one of the many NestedObjects should have Date=day,W=ABC,Y>=1500, rather than one NestedObject matching Date=day, and another on the same document matching W=ABC and Y>=1500.

Upvotes: 3

YuTing
YuTing

Reputation: 6629

You have to user $project and $filter the array.

This is what I try:mongoplayground

Upvotes: 0

Related Questions