0
votes

I'm struggling with RavenDB's multi map/reduce concept and recently asked this question regarding how to properly write a multi map/reduce index.

I got the simple index in that question working but when I tried to make it a bit more complicated, I cannot make it work. What I want to do is to have the result of the index to contain a list of string, i.e:

class RootDocument {
  public string Id { get; set; }
  public string Foo { get; set; }
  public string Bar { get; set; }
  public IList<string> Items { get; set; }
}

public class ChildDocument {
  public string Id { get; set; }
  public string RootId { get; set; }
  public int Value { get; set; }
}

class RootsByIdIndex: AbstractMultiMapIndexCreationTask<RootsByIdIndex.Result> {
  public class Result {
    public string Id { get; set; }
    public string Foo { get; set; }
    public string Bar { get; set; }
    public IList<string> Items { get; set; }
    public int Value { get; set; }
  }

  public RootsByIdIndex() {
    AddMap<ChildDocument>(
      children => from child in children
        select new {
          Id = child.RootId,
          Foo = (string)null,
          Bar = (string)null,
          Items = default(IList<string>),
          Value = child.Value
      });
      AddMap<RootDocument>(
        roots => from root in roots
          select new {
           Id = root.Id,
           Foo = root.Foo,
           Bar = root.Bar,
           Items = root.Items,
           Value = 0
        });
      Reduce = 
        results => from result in results
          group result by result.Id into g
            select new {
              Id = g.Key,
              Foo = g.Select(x => x.Foo).Where(x => x != null).FirstOrDefault(),
              Bar = g.Select(x => x.Bar).Where(x => x != null).FirstOrDefault(),
              Items = g.Select(x => x.Items).Where(
                x => x != default(IList<string>).FirstOrDefault(),
                Value = g.Sum(x => x.Value)
              };
    }
}

Basically I tried to set the Items property to default(IList) when mapping the ChildDocuments and to value of the RootDocument's Items property. This doesn't work, however. It gives the error message

Error on request Could not understand query:

-- line 2 col 285: invalid Expr

-- line 2 col 324: Can't parse double .0.0

when uploading the index. How do I handle lists in multi map/reduce indexes?

2

2 Answers

3
votes

Don't use List in your indexes, instead, use arrays.

AddMap<ChildDocument>(
  children => from child in children
    select new {
      Id = child.RootId,
      Foo = (string)null,
      Bar = (string)null,
      Items = new string[0],
      Value = child.Value
  });
  AddMap<RootDocument>(
    roots => from root in roots
      select new {
       Id = root.Id,
       Foo = root.Foo,
       Bar = root.Bar,
       Items = root.Items,
       Value = 0
    });

Reduce = 
    results => from result in results
      group result by result.Id into g
        select new {
          Id = g.Key,
          Foo = g.Select(x => x.Foo).Where(x => x != null).FirstOrDefault(),
          Bar = g.Select(x => x.Bar).Where(x => x != null).FirstOrDefault(),
          Items = g.SelectMany(x=>x.Items),
          Value = g.Sum(x => x.Value)
          };
2
votes

David, you need to understand that RavenDB stores its indexes with Lucene.NET. That means, you cannot have any complex .net type inside your index.

In your example, I suggest you use a simple string instead of IList<string>. You can then join your string-items:

AddMap<ChildDocument>(
  children => from child in children
    select new {
      Id = child.RootId,
      Foo = (string)null,
      Bar = (string)null,
      Items = (string)null,
      Value = child.Value
  });
  AddMap<RootDocument>(
    roots => from root in roots
      select new {
       Id = root.Id,
       Foo = root.Foo,
       Bar = root.Bar,
       Items = string.Join(";", root.Items),
       Value = 0
    });