How to do a cross join / cartesian product in RavenDB?

别来无恙 提交于 2020-01-02 15:03:13

问题


I have a web application that uses RavenDB on the backend and allows the user to keep track of inventory. The three entities in my domain are:

public class Location
{
    string Id
    string Name 
}

public class ItemType
{
    string Id
    string Name
}

public class Item
{
    string Id
    DenormalizedRef<Location> Location
    DenormalizedRef<ItemType> ItemType
}

On my web app, there is a page for the user to see a summary breakdown of the inventory they have at the various locations. Specifically, it shows the location name, item type name, and then a count of items.

The first approach I took was a map/reduce index on InventoryItems:

this.Map = inventoryItems =>
    from inventoryItem in inventoryItems
    select new
    {
        LocationName = inventoryItem.Location.Name,
        ItemTypeName = inventoryItem.ItemType.Name,
        Count = 1
    });

this.Reduce = indexEntries =>
    from indexEntry in indexEntries
    group indexEntry by new
    {
        indexEntry.LocationName,
        indexEntry.ItemTypeName,
    } into g
    select new
    {
        g.Key.LocationName,
        g.Key.ItemTypeName,
        Count = g.Sum(entry => entry.Count),
    };

That is working fine but it only displays rows for Location/ItemType pairs that have a non-zero count of items. I need to have it show all Locations and for each location, all item types even those that don't have any items associated with them.

I've tried a few different approaches but no success so far. My thought was to turn the above into a Multi-Map/Reduce index and just add another map that would give me the cartesian product of Locations and ItemTypes but with a Count of 0. Then I could feed that into the reduce and would always have a record for every location/itemtype pair.

this.AddMap<object>(docs =>
    from itemType in docs.WhereEntityIs<ItemType>("ItemTypes")
    from location in docs.WhereEntityIs<Location>("Locations")
    select new
    {
        LocationName = location.Name,
        ItemTypeName = itemType.Name,
        Count = 0
    });

This isn't working though so I'm thinking RavenDB doesn't like this kind of mapping. Is there a way to get a cross join / cartesian product from RavenDB? Alternatively, any other way to accomplish what I'm trying to do?

EDIT: To clarify, Locations, ItemTypes, and Items are documents in the system that the user of the app creates. Without any Items in the system, if the user enters three Locations "London", "Paris", and "Berlin" along with two ItemTypes "Desktop" and "Laptop", the expected result is that when they look at the inventory summary, they see a table like so:

| Location | Item Type | Count |
|----------|-----------|-------|
| London   | Desktop   | 0     |
| London   | Laptop    | 0     |
| Paris    | Desktop   | 0     |
| Paris    | Laptop    | 0     |
| Berlin   | Desktop   | 0     |
| Berlin   | Laptop    | 0     |

回答1:


Here is how you can do this with all the empty locations as well:

AddMap<InventoryItem>(inventoryItems =>
    from inventoryItem in inventoryItems
    select new
    {
        LocationName = inventoryItem.Location.Name,
        Items = new[]{
            {
                ItemTypeName = inventoryItem.ItemType.Name,
                Count = 1}
            }
    });
)

this.AddMap<Location>(locations=>
    from location in locations
    select new
    {
        LocationName = location.Name,
        Items = new object[0]
    });


this.Reduce = results => 
    from result in results
    group result by result.LocationName into g
    select new
    {
        LocationName = g.Key,
        Items = from item in g.SelectMany(x=>x.Items)
                group item by item.ItemTypeName into gi
                select new
                {
                    ItemTypeName = gi.Key,
                    Count = gi.Sum(x=>x.Count)
                }

    };


来源:https://stackoverflow.com/questions/29811876/how-to-do-a-cross-join-cartesian-product-in-ravendb

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!