Permalink
Browse files

Adding support for recursion in indexes

  • Loading branch information...
1 parent 3c60af6 commit 6c25bbfa943e616d76ac08a75a19fe397683f726 @ayende ayende committed Sep 20, 2011
@@ -50,6 +50,14 @@ public abstract class AbstractIndexCreationTask
#if !NET_3_5
/// <summary>
+ /// Allows to use lambdas recursively
+ /// </summary>
+ protected IEnumerable<TResult> Recurse<TSource,TResult>(TSource source, Func<TSource, TResult> func)
+ {
+ throw new NotSupportedException("This can only be run on the server side");
+ }
+
+ /// <summary>
/// Allows to use lambdas over dynamic
/// </summary>
protected IEnumerable<dynamic> Project<T>(IEnumerable<T> self, Func<T, object> projection)
@@ -113,6 +113,27 @@ protected IEnumerable<dynamic> Hierarchy(object source, string name)
}
}
+ protected IEnumerable<dynamic> Recurse(object item, Func<dynamic ,dynamic> func)
+ {
+ if (item == null)
+ return Enumerable.Empty<dynamic>();
+
+ var resultsOrdered = new List<dynamic>();
+
+ var results = new HashSet<object>();
+ item = func(item);
+ while (item != null)
+ {
+ if (results.Add(item) == false)
+ break;
+
+ resultsOrdered.Add(item);
+ item = func(item);
+ }
+
+ return new DynamicJsonObject.DynamicList(resultsOrdered.ToArray());
+ }
+
public void AddQueryParameterForMap(string field)
{
mapFields.Add(field);
@@ -0,0 +1,93 @@
+using System.Collections.Generic;
+using System.Linq;
+using Raven.Client.Indexes;
+using Xunit;
+using Raven.Client.Linq;
+
+namespace Raven.Tests.Bugs
+{
+ public class RecursiveQueries : RavenTest
+ {
+ [Fact]
+ public void ShouldBePossible()
+ {
+ using(var store = NewDocumentStore())
+ {
+ new CategoryWithParentsAndChildren().Execute(store);
+
+ using(var session = store.OpenSession())
+ {
+ var root = new Category
+ {
+ Name = "Root"
+ };
+ session.Store(root);
+ var category = new Category
+ {
+ Name = "Child",
+ ParentId = root.Id
+ };
+ session.Store(category);
+ session.Store(new Category
+ {
+ Name = "Grandchild",
+ ParentId = category.Id
+ });
+ session.SaveChanges();
+ }
+
+ using(var session = store.OpenSession())
+ {
+ List<CategoryHeaderWithParents> categoryHeaderWithParentses = session.Query<CategoryHeaderWithParents, CategoryWithParentsAndChildren>()
+ .Customize(x=>x.WaitForNonStaleResults())
+ .Where(x=>x.Name == "Grandchild")
+ .ToList();
+
+ Assert.Equal("Grandchild", categoryHeaderWithParentses[0].Name);
+ Assert.Equal("Child", categoryHeaderWithParentses[0].Parents[0].Name);
+ Assert.Equal("Root", categoryHeaderWithParentses[0].Parents[1].Name);
+ }
+ }
+ }
+
+ public class CategoryWithParentsAndChildren : AbstractIndexCreationTask<Category>
+ {
+ public CategoryWithParentsAndChildren()
+ {
+ Map = categories => from category in categories
+ select new {category.Id, category.Name, category.ParentId};
+ TransformResults = (database, categories) =>
+ from category in categories
+ let parentCategories = Recurse(category, c => database.Load<Category>(c.ParentId))
+ select new
+ {
+ category.Id,
+ category.Name,
+ Parents =
+ (
+ from parent in parentCategories
+ select new {parent.Id, parent.Name}
+ )
+ };
+ }
+ }
+
+ public class CategoryHeader
+ {
+ public string Id { get; set; }
+ public string Name { get; set; }
+ }
+
+ public class CategoryHeaderWithParents : CategoryHeader
+ {
+ public CategoryHeader[] Parents { get; set; }
+ }
+
+ public class Category
+ {
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string ParentId { get; set; }
+ }
+ }
+}
@@ -205,6 +205,7 @@
<Compile Include="Bugs\QueryingOnEmptyString.cs" />
<Compile Include="Bugs\QueryOptimizerOnStaticIndex.cs" />
<Compile Include="Bugs\RacielrodTest.cs" />
+ <Compile Include="Bugs\RecursiveQueries.cs" />
<Compile Include="Bugs\RoundCrisis.cs" />
<Compile Include="Bugs\SelfReference.cs" />
<Compile Include="Bugs\SerializingAndDeserializingWithRaven.cs" />

0 comments on commit 6c25bbf

Please sign in to comment.