Transforming hierarchical data

When our data is structured hierarchically and we want to extract data from e.g. our parent records, we can use Recurse method within our transformer projection function.

Information

Recurse requires us to have a single document or a collection of documents under specified property, but when we have only an Id or array of Ids available, we can mix Recurse with LoadDocument (check Example II).

Example I

Let's assume that we have a following structure:

public class BlogPost
{
	public string Author { get; set; }

	public string Title { get; set; }

	public string Text { get; set; }

	public List<BlogPostComment> Comments { get; set; }
}

public class BlogPostComment
{
	public string Author { get; set; }

	public string Text { get; set; }

	public List<BlogPostComment> Comments { get; set; }
}

All comment authors for each BlogPost can be extracted using Recurse in the following way:

public class BlogPosts_Comments_Authors : AbstractTransformerCreationTask<BlogPost>
{
	public class Result
	{
		public string Title { get; set; }

		public List<string> Authors { get; set; }
	}

	public BlogPosts_Comments_Authors()
	{
		TransformResults =
			posts => from post in posts
					 let comments = Recurse(post, x => x.Comments)
					 select new
						{
							post.Title,
							Authors = comments.Select(x => x.Author)
						};
	}
}

IList<BlogPosts_Comments_Authors.Result> results = session
	.Query<BlogPost>()
	.TransformWith<BlogPosts_Comments_Authors, BlogPosts_Comments_Authors.Result>()
	.ToList();

Example II

Let's assume that we have a following Category where we store Id of a parent record:

public class Category
{
	public string Id { get; set; }

	public string Name { get; set; }

	public string ParentId { get; set; }
}

Now, to extract category name, names of all parent categories, and names of all parent categories for those parents, and so on, we can mix our Recurse method with LoadDocument.

public class Categories_WithParents : AbstractTransformerCreationTask<Category>
{
	public class Result
	{
		public string Name { get; set; }

		public List<string> Parents { get; set; }
	}

	public Categories_WithParents()
	{
		TransformResults =
			categories => from category in categories
						  let parentCategories = Recurse(category, c => LoadDocument<Category>(c.ParentId))
						  select new
							{
								category.Name,
								Parents = parentCategories.Select(parent => parent.Name)
							};
	}
}

IList<Categories_WithParents.Result> results = session
	.Query<Category>()
	.TransformWith<Categories_WithParents, Categories_WithParents.Result>()
	.ToList();