Re: [RavenDB] can't get reduce to give a total from a sum on a multip map index

12 views
Skip to first unread message

Daniel Dar

unread,
Nov 14, 2012, 8:34:40 AM11/14/12
to rav...@googlegroups.com
Hi,
there are several issues in the index you have written:
1) You should use "FirstOrDefault" instead if "First"
2) couldn't really understand what you wanted to achieve with this index, but the reduce statement didn't make much sense
try doing it more along the way of:
Reduce = docs => from doc in docs
                                 group doc by doc.SaleId
                                     into deals
                                     let title = deals.FirstOrDefault(x => x.Title != null).Title
                                     let starting = deals.FirstOrDefault(x => x.StartingAt != DateTime.MinValue).StartingAt
                                     let ending = deals.FirstOrDefault(x => x.EndingAt != DateTime.MinValue).EndingAt
                                     select new SiteSale
                                     {
                                         SaleId = deals.Key,
                                         Title = title,
                                         StartingAt = starting,
                                         EndingAt = ending,
                                         Price = 100,
                                         IsNotSoldout = deals.Any(x => x.IsNotSoldout),
                                         TotalSold = deals.Sum(x => x.TotalSold)
                                     };


On Wed, Nov 14, 2012 at 9:02 AM, afif <afifmo...@gmail.com> wrote:
Hi Guys,
I have a sale that can be across many schedules (not overlapping), and can have many products. I have a sale product stock document for every product on sale that has the total quantity allocated for that sale, and tells when the product was sold out. I have order receipts for every successful sale. for the user I have to project a sale advertising the price on the sale as the price of its least expensive product, and also displaying whether its sold out or not. I have a multi map index below that looks like it should work. But for some reason the test  shows the soldout and the total sold values are never getting assigned right. Its something silly, but I have gone over this again and again and can't find what I am missing.

namespace RavenIndexSandbox
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using FizzWare.NBuilder;
    using NUnit.Framework;
    using Raven.Client.Document;
    using Raven.Client.Embedded;
    using Raven.Client.Indexes;

    class Sale
    {
        public Sale()
        {
            Products = new List<Product>();
            Schedules = new List<Schedule>();
        }
        public string Id { get; set; }
        public string Title { get; set; }
        public IList<Product> Products { get; set; }
        public IList<Schedule> Schedules { get; set; }
        
        public class Schedule
        {
            public DateTime StartingAt { get; set; }
            public DateTime EndingAt { get; set; }
        }
        public class Product
        {
            public string ProductId { get; set; }
            public decimal Rrp { get; set; }
        }
    }

    class Product
    {
        public string Id { get; set; }
        public int Sku { get; set; }
        public decimal CostPrice { get; set; }
        public string Name { get; set; }
    }

    class SaleProductStock
    {
        public string Id { get; set; }
        public string SaleId { get; set; }
        public string ProductId { get; set; }
        public int Allocated { get; set; }
        public DateTime? SoldOutAt { get; set; }
        public bool IsNotSoldout { get { return SoldOutAt == null; } }
    }

    class OrderReceipt
    {
        public string Id { get; set; }
        public string SaleId { get; set; }
        public string ProductId { get; set; }
    }

    class SiteSale
    {
        public string SaleId { get; set; }
        public string Title { get; set; }
        public DateTime StartingAt { get; set; }
        public DateTime EndingAt { get; set; }
        public bool IsNotSoldout { get; set; }
        public int TotalSold { get; set; }
        public decimal Price { get; set; }
    }

    class Sales_Brochure : AbstractMultiMapIndexCreationTask<SiteSale>
    {
        public Sales_Brochure()
        {
            AddMap<Sale>(deals => 
                from deal in deals
                from sch in deal.Schedules
                select new SiteSale
                {
                    SaleId = deal.Id,
                    Title =deal.Title,
                    StartingAt = sch.StartingAt,
                    EndingAt = sch.EndingAt,
                    Price = deal.Products.First().Rrp,
                    IsNotSoldout = false,
                    TotalSold = 0
                });

            AddMap<OrderReceipt>(orderReceipts => 
                from orderReceipt in orderReceipts
                select new SiteSale
                {
                    SaleId = orderReceipt.SaleId,
                    Title = "",
                    Price = 0,
                    EndingAt = DateTime.MinValue,
                    StartingAt = DateTime.MinValue,
                    IsNotSoldout = false,
                    TotalSold = 1
                });
            
            AddMap<SaleProductStock>(stock => 
                from prod in stock
                select new SiteSale
                {
                    SaleId = prod.SaleId,
                    Title = "",
                    StartingAt = DateTime.MinValue,
                    EndingAt = DateTime.MinValue,
                    Price = 0,
                    IsNotSoldout = prod.SoldOutAt == null,
                    TotalSold = 0
                });

            Reduce = docs => from doc in docs
                             group doc by doc.SaleId
                             into deals
                             from deal in deals
                             select new SiteSale
                             {
                                 SaleId = deals.Key,
                                 Title = deal.Title,
                                 StartingAt = deal.StartingAt,
                                 EndingAt = deal.EndingAt,
                                 Price = deals.Where(x => x.Price != 0).Min(x => x.Price),
                                 IsNotSoldout = deals.Any(x => x.IsNotSoldout),
                                 TotalSold = deals.Sum(x => x.TotalSold)
                             };
        }
    }

    [TestFixture]
    class WhenQueryingSiteSales
    {
        protected IEnumerable<Sale> Sales
        {
            get
            {
                return Builder<Sale>.CreateListOfSize(3)
                  .All()
                      .With(x => x.Schedules = new List<Sale.Schedule>
                        {
                            new Sale.Schedule { StartingAt = DateTime.Now.AddDays(-1), EndingAt = DateTime.Now.AddDays(1)},
                            new Sale.Schedule { StartingAt = DateTime.Now.AddDays(2), EndingAt = DateTime.Now.AddDays(4)}
                        })
                  .TheFirst(1)
                      .With(x => x.Id = "sales/1")
                      .With(x => x.Title = "iphone")
                      .With(x => x.Products = new List<Sale.Product>
                        {
                            new Sale.Product { ProductId = "products/500", Rrp = 200},
                            new Sale.Product { ProductId = "products/120", Rrp = 800},
                        })
                  .TheNext(1)
                      .With(x => x.Id = "sales/2")
                      .With(x => x.Title = "samsung")
                      .With(x => x.Products = new List<Sale.Product>
                        {
                            new Sale.Product { ProductId = "products/100", Rrp = 100},
                            new Sale.Product { ProductId = "products/600", Rrp = 600},
                        })
                  .TheNext(1)
                      .With(x => x.Id = "sales/3")
                      .With(x => x.Title = "nokia")
                      .With(x => x.Products = new List<Sale.Product>
                        {
                            new Sale.Product { ProductId = "products/700", Rrp = 700},
                            new Sale.Product { ProductId = "products/900", Rrp = 900},
                        })
                  .Build();
            }
        }

        protected IEnumerable<SaleProductStock> SaleProductStock
        {
            get 
            {
                return
                    from deal in Sales
                    from prod in deal.Products
                    select new SaleProductStock
                    {
                        Allocated = int.Parse(prod.ProductId.Replace("products/", "")),
                        SaleId = deal.Id,
                        ProductId = prod.ProductId,
                        Id = string.Concat("saleproductstocks/", deal.Id.Replace("deals/", ""), ":", prod.ProductId.Replace("products/", "")),
                        SoldOutAt = prod.Rrp == int.Parse(prod.ProductId.Replace("products/", "")) ? new DateTime(2012,10,10,10,10,10,10) : (DateTime?)null
                    };
            }
        }

        protected IEnumerable<OrderReceipt> OrderReceipts
        {
            get
            {
                foreach (var stock in SaleProductStock.Where(x => !x.IsNotSoldout))
                {
                    for (int i = 0; i < stock.Allocated; i++)
                    {
                        yield return new OrderReceipt
                        {
                            SaleId = stock.SaleId,
                            ProductId = stock.ProductId
                        };
                    }
                }
            }
        }
        
        [Test]
        public void ShouldFindSales()
        {
            //using (var d = new EmbeddableDocumentStore() { RunInMemory  = true }.Initialize())
            using (var d = new DocumentStore() { Url = "http://localhost:1096/databases/spike" }.Initialize())
            using (var s = d.OpenSession())
            {
                d.ExecuteIndex(new Sales_Brochure());

                foreach (var deal in Sales)
                    s.Store(deal);

                foreach (var stock in SaleProductStock)
                    s.Store(stock);

                foreach (var scoopon in OrderReceipts.ToList())
                    s.Store(scoopon);

                s.SaveChanges();

                var sitesales = s.Query<SiteSale, Sales_Brochure>()
                    .Customize(x => x.WaitForNonStaleResults())
                    .Where(x => x.StartingAt < DateTime.Now && x.EndingAt > DateTime.Now)
                    .ToList();

                foreach (var sitesale in sitesales)
                {
                    var thissale = sitesale;
                    Assert.That(sitesale.TotalSold, Is.EqualTo(OrderReceipts.Count(x => x.SaleId == thissale.SaleId)));
                }

                foreach (var sitesale in sitesales)
                    Assert.That(sitesale.IsNotSoldout, Is.EqualTo(SaleProductStock.Any(x => x.IsNotSoldout)));
            }
        }

Reply all
Reply to author
Forward
0 new messages