Search code examples
c#mongodbaggregation-frameworkmongodb-.net-driver

C# MongoDB: Querying a $match on an array after an $unwind


I have an aggregation pipeline to be performed using IAggregateFluent.

Here is the class mapping with the DB

public class Card
    {
        public ObjectId Id { get; set; }
        public string CardNumber { get; set; }
        public string CustomerId { get; set; }
        public decimal BalanceAmount { get; set; }
        public string Currency { get; set; }
        public List<CardTransaction> Transactions { get; set; }
    }

And CardTransaction as

public class CardTransaction
    {
        //public ObjectId Id { get; set; }
        public DateTime TransactionDate { get; set; }
        public decimal InitialBalance { get; set; }
        public decimal FinalBalance { get; set; }
        public decimal TransactionAmount { get; set; }
        public string OrderNumber { get; set; } //Unique for every transaction
    }

How do I query to get only the CardTransaction (as a projection at the end) matching a particular OrderNumber using Fluent Aggregation.

This works perfectly on the shell but translating it to C# doesnt seem to work without using BsonDocument

db.cards.aggregate([
    {
        $match:{
            CardNumber:"00000001"
        }
    },
    {
        $unwind:"$Transactions"
    },
    {
        $match:{
            "Transactions.OrderNumber":"123456789-1"
        }
    },
    {
        $project:{
            _id:0,
            Transactions:1
        }
    }
])

So far:

    Cards.Aggregate()
    .Match(x => x.CardNumber == someCardNumber)
    .Unwind<Card,Card>(x => x.Transactions)
    .Match(x => x.Transactions.MethodThatReturnsBool
(y=>y.OrderNumber == someOrderNumber))
    .Project(p=>p.Transactions).ToList();

Solution

  • You can run .AsQueryable() on your IMongoCollection and then run following LINQ query:

    var q = from card in Cards.AsQueryable()
            where card.CardNumber == "00000001"
            select card.Transactions.Where(tran => tran.OrderNumber == "123456789-1");
    
    var data = q.ToList();
    

    It generates below aggregation pipeline:

    {
        "$match" : {
            "CardNumber" : "00000001"
        }
    },
    {
        "$project" : {
            "__fld0" : {
                "$filter" : {
                    "input" : "$Transactions",
                    "as" : "tran",
                    "cond" : {
                        "$eq" : [
                            "$$tran.OrderNumber",
                            "123456789-1"
                        ]
                    }
                }
            },
            "_id" : 0
        }
    }