private IEnumerable GetRelatives(IEnumerable <IIdentifiable> source, RelativesExpression relativesExpression, Type itemType, DateTime asOf)
        {
            var results = Activator
                          .CreateInstance(typeof(List <>)
                                          .MakeGenericType(itemType))
                          as IList;

            var ids = source.Select(item => item.Id);

            if (!ids.Any())
            {
                return(results);
            }

            var workItemLinks = WorkItemStore.QueryLinks(GetQueryStringForRelativesOf(ids, relativesExpression.AncestorType, relativesExpression.DescendentType, relativesExpression.Direction, asOf));

            var map = new Dictionary <int, List <int> >();

            workItemLinks.Where(link => link.SourceId == 0).ToList().ForEach(link => map.Add(link.TargetId, new List <int>()));
            foreach (var ancestorId in map.Keys)
            {
                map[ancestorId].AddRange(GetTransitiveLinks(ancestorId, workItemLinks).Select(child => child.TargetId));
            }

            ids = ids.Union(workItemLinks.Select(link => link.SourceId)).Union(workItemLinks.Select(link => link.TargetId)).Where(id => id != 0);
            var workItems = HydrateAndFilterItems(ids, relativesExpression.AncestorType, relativesExpression.DescendentType, asOf);

            foreach (var ancestorId in map.Keys)
            {
                var id = ancestorId;
                // Get the ancestor from the list of hydrated work items
                var ancestorWorkItem = workItems.Single(item => item.Id == ancestorId);

                // Filter the work items to just those IDs that are in the map for this ancestor.
                // NOTE: Please note that the strategy is to start with the hydrated work items and filter to
                // those in the map, and NOT to lookup every work item in the map. The latter strategy would
                // break because the map also contains the IDs of Task Groups that have been filtered out.
                // This way we start with work items that we already know exist.
                var descendentWorkItems = workItems.Where(wi => map[id].Contains(wi.Id));

                var ancestor    = WorkItemMapper.Create(relativesExpression.AncestorType, new[] { ancestorWorkItem }).Cast <object>().Single();
                var descendents = WorkItemMapper.Create(relativesExpression.DescendentType, descendentWorkItems);

                var properTypedDescendents = GenerateListOfType(descendents, itemType);
                var result = Activator.CreateInstance(itemType, ancestor, properTypedDescendents);
                results.Add(result);
            }

            return(results);
        }