Consider having a base class Foo and two derived clases Bar and Baz.
I now store instances of Bar and Baz in RavenDB and can query Bar and
Baz docs.
Now I would like to make RavenDB give me all Foo docs (= Bar AND Baz).
Is there any indexing "trick" that would make this possible?
Raven doesn't record the base class CLR types, so a normal query
wouldn't find any Foo documents at all.
Tobias
> I don't know if it's applicable to your situation, but I've spent some
> timing implementing StackOverflow in RavenDB and it has a similar
> situation. See this blog post for the full details
> http://thelonedeveloper.blogspot.com/2010/05/stackoverflow-in-ravendb-question-and.html.
Nice reading, thanks!
But I'm not sure, how this Post/Question/Answer use case can work at all.
You are querying questions and answers with:
session.Query<Question>("PostIndex").Where(x => x. PostTypeId == 1)
session.Query<Answer>("PostIndex").Where(x => x. PostTypeId == 2)
If Question and Answer are classes derived from a class Post, then I
don't see, how this can work. If PostIndex looks like this:
documentStore.DatabaseCommands.PutIndex("PostIndex", new
IndexDefinition<Post>
{
Map = docs => from doc in docs
select new { PostTypeId = doc.PostTypeId }
}
Then storing a question or an answer would create no index entries,
because the PostIndex only indexes Post docs.
Tobias
> Yes, you need to set the convention for FindTypeTagName.
>
> new DocumentConvention
> {
> FindTypeTagName = type => typeof(Foo).IsAssignableFrom(type)
> ? DocumentConvention.DefaultTypeTagName(typeof(Foo))
> : DocumentConvention.DefaultTypeTagName(type);
> }
Ok. But I'm not sure how to use this.
If I do:
documentStore.Conventions.FindTypeTagName = type =>
typeof(Foo).IsAssignableFrom(type) ?
DocumentConvention.DefaultTypeTagName(typeof(Foo)) :
DocumentConvention.DefaultTypeTagName(type);
Then I can indeed create and index on Foo and query all Bar and Baz
instances. But I can't query for Bar or Baz anymore, because then it
will try to load a Baz as a Bar ore vice versa.
Doing this:
var documentConvention = new DocumentConvention
{
FindTypeTagName = type => typeof(Foo).IsAssignableFrom(type)
? DocumentConvention.DefaultTypeTagName(typeof(Foo))
: DocumentConvention.DefaultTypeTagName(type)
};
documentStore.DatabaseCommands.PutIndex("Foo", new IndexDefinition<Foo>
{
Map = docs => from doc in docs select new { Value = doc.Value }
}.ToIndexDefinition(documentConvention));
...doesn't seem to work either. The FindTypeTagName is only evaluated
once during PutIndex and nothing else seems to change - this index
doesn't give me any results.
Tobias
> Everything is stored as a Post which is a C# class that contains all
> the fields that questions and answers can have.
>
> The class hierarchy is then
>
> BasePost
> Question : BasePost
> Answer : BasePost
Ok. So this is basically:
public class Foo
{
public string Id { get; set; }
public string Type { get; set; }
public string FooValue { get; set; }
public string BarValue { get; set; }
public string BazValue { get; set; }
}
public class Bar : Foo
{
public Bar()
{
Type = "Bar";
}
}
public class Baz : Foo
{
public Baz()
{
Type = "Baz";
}
}
And I create this index:
documentStore.DatabaseCommands.PutIndex("Foo", new IndexDefinition<Foo>
{
Map = docs => from doc in docs select new {Type = doc.Type}
});
And I store the docs as Foos:
session.Store(new Foo {Type = "Bar", ...});
session.Store(new Foo {Type = "Baz", ...});
And when I query it this way;
session.Query<Bar>("Foo").Where(x => x.Type == "Bar")
... I will get a casting exception, because it tries to cast a Foo to
Bar. I think, I'm still missing something from your solution.
I can get it woking, if I globally set this document convention:
documentStore.Conventions.FindTypeTagName =
type => typeof (Foo).IsAssignableFrom(type)
? DocumentConvention.DefaultTypeTagName(typeof (Foo))
: DocumentConvention.DefaultTypeTagName(type);
And then I can even move the properties back to Bar and Baz again and
store Bar and Baz directly:
public class Foo
{
public string Id { get; set; }
public string Type { get; set; }
public string FooValue { get; set; }
}
public class Bar : Foo
{
public Bar()
{
Type = "Bar";
}
public string BarValue { get; set; }
}
public class Baz : Foo
{
public Baz()
{
Type = "Baz";
}
public string BazValue { get; set; }
}
session.Store(new Bar {FooValue = "fooValue", BarValue = "barValue"});
session.Store(new Baz {FooValue = "fooValue", BazValue = "bazValue"});
session.Query<Bar>("Foo").Where(x => x.Type == "Bar")
The Type property and it's index are still required. But I can even do
(which was my original question):
session.Query<Foo>("Foo")
...which returns a list of Bar and Baz instances.
If I know could get rid of the Type property somehow...
Tobias
> I am not sure what you are attempting to do.
What I would like to do is:
abstract class Foo { ... }
class Bar : Foo { ... }
class Baz : Foo { ... }
session.Store(new Bar {...});
session.Store(new Baz {...});
session.Query<Bar>("IndexA").Where(...)
session.Query<Baz>("IndexB").Where(...)
// This one should return an IEnumerabl<Foo> containing a Bar and
// a Baz instance:
session.Query<Foo>("IndexC").Where(...)
> Each document has a single entity tag, which you can use to limit searches.
> If you want to do a cross tag search, you can use:
>
> from doc in docs
> let tag = doc["@metadata"]["Raven-Entity-Name"]
> where tag == "Foo" || tag == "Bar"
> select new { doc.Name}
Thanks, this indeed works! I've never looked beyond IndexDefinition<T>.
Tobias