4

This query is failing (RavenDB 1.0.701)

var items= RavenSession.Query<Item>().Where(x => "161 193".StartsWith(x.MaterializedPath)).ToList();

Is there another way?

Before you ask, I'm trying to get the parents (ancestors) for a tree structured data with a materialized path (lineage column).

Community
  • 1
  • 1
W3Max
  • 3,328
  • 5
  • 35
  • 61

2 Answers2

2

You can't do that in RavenDB - the field you're querying must be on the left of the predicate, and the right of the predicate cannot refer to another field.

As for how to restructure this - Sorry, not sure.

Edit:

Okay, it took some experimentation - but I managed to make it work IF it's possible to either restructure MaterializedPath, or add a new property. I'll assume here it's a new property, to avoid any confusion.

// Sample class: 
public class Item 
{ 
  public string Name { get;set;}
  public Dictionary<int, string> Path { get;set;} // Zero-based key on path. 
}


// Query: Find nodes with path "A B"  
var query = session.Query<Item>().AsQueryable();
query = query.Where(item => item.Path[0] == "A");
query = query.Where(item => item.Path[1] == "B");

var found = query.ToList();

And here it is running:

IDocumentStore store = new EmbeddableDocumentStore { RunInMemory = true };
store.Initialize();

// Install Data
using (var session = store.OpenSession())
{
    session.Store(new Item("Foo1", "A")); // NB: I have a constructor on Item which takes the path and splits it up. See below. 
    session.Store(new Item("Foo2", "A B"));
    session.Store(new Item("Foo3", "A C D"));
    session.Store(new Item("Foo4", "A B C D"));
    session.Store(new Item("Foo5", "C B A"));
    session.SaveChanges();
}

using (var session = store.OpenSession())
{
    var query = session
        .Query<Item>().AsQueryable();

    query = query.Where(item => item.Path[0] == "A");
    query = query.Where(item => item.Path[1] == "B");

    var found = query.ToList();

    Console.WriteLine("Found Items: {0}", found.Count );

    foreach(var item in found)
    {
        Console.WriteLine("Item Name {0}, Path = {1}", item.Name, string.Join(" ", item.Path));
    }
}

The output from this is:

Found Items: 2
Item Name Foo2, Path = [0, A] [1, B]
Item Name Foo4, Path = [0, A] [1, B] [2, C] [3, D]

Hope that helps.

Edit 2:

The constructor I have on Item looks like this, just for ease of testing:

    public Item(string name, string materializedPath)
    {
        Name = name;
        var tmpPath = materializedPath.Split(' ');
        Path =
            tmpPath
                .Zip(Enumerable.Range(0, tmpPath.Count()), (item, index) => new {Item = item, Index = index})
                .ToDictionary(k => k.Index, v => v.Item);
    }
  • In addition - why not flip the sides in this predicate. It should effectively do the same, or did I miss something? – Daniel Lang Mar 30 '12 at 07:45
  • "A B C" starts with "A B", "A B" does not start with "A B C". –  Mar 30 '12 at 09:11
  • I hear you, but I'm sure there is a way to do this since RavenDB is based on Lucene (Lucene being a full text search engine must be able to do this)... Maybe with something like RavenDB advanced Lucene query? – W3Max Mar 30 '12 at 13:02
  • I was just doing some thinking... If it's possible for you to restructure your path to a dictionary or list, it might be possible to build a predicate based on position in the list. Would that work for you? –  Mar 30 '12 at 23:52
  • Updated my answer with a working example. If Lucene has some way of doing this, that'd be great - but I don't know it. –  Mar 31 '12 at 01:18
  • Also, Part of my experimenting was to try and get an AbstractIndex to take the existing MaterializedPath and rewrite it, but I wasn't successful. –  Mar 31 '12 at 01:21
1

I'll Suggested build an index that will contain each of the possibilities of search. This will create Lots of items in your index but same time it is the power of Lucene to search fast

Map = docs => from n in docs
              let allPaths = n.MaterializedPath.Split(new char[]{' '})
              from path in allPaths 
              select new
              {
                 Path = path
              };

It's important that "path" represent an Unique document ID

iam_martin
  • 86
  • 4
  • Very good answer. At first I thought it could work but, unfortunately, tags are a little bit different then trees. – W3Max Mar 30 '12 at 15:21