Item Buckets and maintaining Parent Relationships

Item Buckets are a fantastic idea, they allow the Content Editor to store a very large number of items into a folder (called a Bucket) and allows the Content Editor to search for items contained within that bucket – all from the CMS. However, there are a number of things you need to bear in mind when using Item Buckets. Say for example you have a Blog Post with Comments underneath the Post as child items. You could use the following Sitecore Search to pull out all of the Comments that are relating to the Post.

This can be achieved by performing the following search query:

public List<Item> GetComments(Item commentsFolder)
 {
 ISearchIndex searchIndex = ContentSearchManager.GetIndex(Utilities.IndexUtilities.GetIndex());
 List<Item> returnItems = new List<Item>();
 using (IProviderSearchContext context = searchIndex.CreateSearchContext())
 {
 SearchResults<SearchResultItem> searchResults = context.GetQueryable<SearchResultItem>().Filter(x => x.Parent == commentsFolder.ID).GetResults();

 foreach (var searchHit in searchResults.Hits)
 {
// Do something with the comment
 }
 }

 return returnItems;
 }

This example takes the Post’s “Comments Folder” as a parameter and returns all the child items of that Comments Folder.

Now, you wish to turn the Comments folder into an Item Bucket. This is a great idea as there could be thousands of comments per post. What you will instantly notice though is that once you make the Comments folder an Item Bucket, Sitecore will reorganise the Items within a Bucket and place them in folders in accordance with the Bucketing Strategy – this can be evidenced by observing that the path to each Content Item in the Bucket changes. This also means that you cannot use the above code to perform the search as the parent of a Comment will no longer be the Comments Folder.

This is not necessarily an issue, as on the template you specify as an Item Bucket, you can specify the “Lock Child Relationship” feature in the item template:

 

Lock Child Relationship

This will ensure that any children within a Bucket will not inherit the path imposed on them by the Bucketing Strategy (therefore the above code will work for that scenario).

However, what if you want a combination of both? What if you want to maintain a Bucketing Strategy AND maintain parent-child relationshop? Well, one strategy we can use is adding a Computed Field into the index and into to your entity called CommentFolderId.

You can create a class that knows hows to work out the parent folder of a comment and store this in the index. With this class, it utilises the extension method GetParentBucketItemOrParent() from the Sitecore.Buckets.dll so ensure you have a reference to this.


namespace SitecoreJim.Business.ComputedFields
{
 public class CommentsFolderIdComputedField : IComputedIndexField
 {
 public object ComputeFieldValue(IIndexable indexable)
 {
 Assert.ArgumentNotNull(indexable, "indexable");
 SC.ContentSearch.SitecoreIndexableItem scIndexable =
 indexable as SC.ContentSearch.SitecoreIndexableItem;

 SC.Data.Items.Item item = (SC.Data.Items.Item)scIndexable;

 if (item == null)
 {
 Log.Log.Warn(
 this + " : unsupported SitecoreIndexableItem type : " + scIndexable.GetType());
 return false;
 }

 // Check to see whether the Item is the item we wish to store the parent folder in the index
 if ( (item.TemplateID != new ID(Constants.WritingCommentTemplateString))
 && (item.TemplateID != new ID(Constants.PollCommentTemplateString)))
 return false;

 string stringToIndex = string.Empty;

 Item parentItem = item.GetParentBucketItemOrParent();
 stringToIndex = parentItem.ID.ToString().Replace("-", "").Replace("{", "").Replace("}", "");

 return stringToIndex;
 }

 public string FieldName { get; set; }
 public string ReturnType { get; set; }
 }
}

95% of that code was copied off someone’s blog post (apologies – I can’t remember who it is to credit them!!). The important bit of this is a call to:

GetParentBucketItemOrParent()

This will ascertain what the “real” parent item is of an item that is in a bucket.

Now update Sitecore.ContentSearch.Lucene.DefaultIndexConfiguration.config file so that the index can make use of this. Firstly, add the following field definition into the <fields> section:


<field fieldname="commentsfolderid" storateType="yes" indexType="tokenized">SitecoreJim.Business.ComputedFields.CommentsFolderIdComputedField,SitecoreJim.Business</field>

Then, in the section <fieldNames hint=”raw:AddFieldByFieldName”> you need to add the following so that Sitecore knows what the field type is:

<field fieldName="commentsfolderid" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.GUID" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
<analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
</field>

Once you rebuild the indexes, the new computed field will become part of the index.

If you haven’t already, you need to create an entity that inherits from SearchResultItem, add the property into the class for CommentsFolderId and decorate the property with the informational identify which field it relates to in the index:


public class WritingComment : SearchResultItem
{
[IndexField("commentsfolderid")]
public Guid CommentsFolderId { get; set; }

public Item InnerItem { get; set; }
}

All that needs to occur now is to slightly modify the search query to make use of this computed field. So what we are now saying get me all the comments that relate to a given Comments folder (which is unique to a post).


using (IProviderSearchContext context = searchIndex.CreateSearchContext())
{
SearchResults<WritingComment> searchResults = context.GetQueryable<WritingComment>()
.Filter(x => x.CommentsFolderId == commentsFolder.ID).GetResults();

foreach (var searchHit in searchResults.Hits)
{
// Do something with the comment</pre>
}
}

There we go, now you can turn the Comments Folder into an Item Bucket whilst still maintaining the parent-child relationship.

Hope this helps 🙂

Advertisements

2 thoughts on “Item Buckets and maintaining Parent Relationships

  1. Nice post and good to know a way round this. Although you could argue that having child items in a bucket goes against the idea of a bucket itself, the example you’ve provided is a relevant case for having child items.

    Only small thing – it wasn’t clear in the blog where to put the first line of config for the computed field. I believe it should go in raw:AddComputedIndexField .

    The second line of config as you’ve mentioned goes in raw:AddFieldByFieldName

    Nice work 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s