Question

I am using RavenDb in C# web project. I have an object that I need to query its child collection with 1 row per child object and some of the root/parent object properties.
Note: This is not the actual design, just simplified for this question.

    public class OrderLine
    {
        public string ProductName { get; set; }
        public int Quantity { get; set; }
        public DateTime? ShipDate { get; set; }
    }
    public class Order
    {
        public int OrderId { get; set; }
        public string CustomerName { get; set; }
        public DateTime OrderDate { get; set; }
        public List<OrderLine> OrderLines { get; set; }

    }

The order with the orderlines is one single document. ShipDate will be updated on each line because not all products are always in stock.

I need to be able to create a list of the last 10 products sent with the following columns:

OrderId
Customer
ProductName
ShipDate

This doesn't work because SelectMany is not supported:

        var query = from helper in RavenSession.Query<Order>()
                        .SelectMany(l => l.OrderLines, (order, orderline) => 
                            new { order, orderline })
                    select new 
                    {
                        helper.order.OrderId,
                        helper.order.CustomerName,
                        helper.orderline.ProductName,
                        helper.orderline.ShipDate

                    };
        var result = query.Where(x => x.ShipDate.HasValue)
            .OrderByDescending(x => x.ShipDate.Value).Take(10);

I believe the right thing to do isto create an Index that will flatten out the list but I haven't had any success. I don't believe a Map-Reduce situation will work because as I understand it will effectively does a group by which Reduces the number of documents to less rows (in the index). But in this case, I am trying to expand the number of documents to more rows (in the index).

I would rather not put each OrderLine in a separate document but I do not know what my options are.

Was it helpful?

Solution

Since you want to filter and sort by fields in the subclass, you'll need to make sure all the fields you want are indexed and stored.

public class ShippedItemsIndex
    : AbstractIndexCreationTask<Order, ShippedItemsIndex.Result>
{
    public class Result
    {
        public int OrderId { get; set; }
        public string CustomerName { get; set; }
        public string ProductName { get; set; }
        public int Quantity { get; set; }
        public DateTime ShipDate { get; set; }
    }

    public ShippedItemsIndex()
    {
        Map = orders => 
            from order in orders
            from line in order.OrderLines
            where line.ShipDate != null
            select new
            {
                order.OrderId,
                order.CustomerName,
                line.ProductName,
                line.Quantity,
                line.ShipDate
            };

        StoreAllFields(FieldStorage.Yes);
    }
}

Then you can project from the index into your results.

var query = session.Query<Order, ShippedItemsIndex>()
    .ProjectFromIndexFieldsInto<ShippedItemsIndex.Result>()
    .OrderByDescending(x => x.ShipDate)
    .Take(10);

var results = query.ToList();

Here is a complete test demonstrating.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top