public override Dictionary <OGM, CollectionItemList> Load(IEnumerable <OGM> parents, Core.EntityCollectionBase target)
        {
            if (parents.Count() == 0)
            {
                return(new Dictionary <OGM, CollectionItemList>());
            }

            Dictionary <object, OGM> parentDict = new Dictionary <object, OGM>();

            foreach (OGM parent in parents)
            {
                object?key = parent.GetKey();
                if (key is null)
                {
                    continue;
                }

                if (!parentDict.ContainsKey(key))
                {
                    parentDict.Add(key, parent);
                }
            }

            string matchClause = string.Empty;

            if (target.Direction == DirectionEnum.In)
            {
                matchClause = "MATCH ({0})-[rel:{2}]->({3})";
            }
            else if (target.Direction == DirectionEnum.Out)
            {
                matchClause = "MATCH ({0})<-[rel:{2}]-({3})";
            }

            string whereClause  = " WHERE node.{1} in ({{keys}}) ";
            string returnClause = " RETURN node.{1} as ParentKey, out.{4} as ItemKey ";

            if (target.Relationship.IsTimeDependent)
            {
                returnClause = $" RETURN node.{{1}} as ParentKey, out.{{4}} as ItemKey, rel.{target.Relationship.StartDate} as StartDate, rel.{target.Relationship.EndDate} as EndDate";
            }

            Entity targetEntity = target.ForeignEntity;

            string[] nodeNames = target.Parent.GetEntity().GetDbNames("node");
            string[] outNames  = targetEntity.GetDbNames("out");

            if (nodeNames.Length > 1 && outNames.Length > 1)
            {
                throw new InvalidOperationException("Both ends are virtual entities, this is too expensive to query...");
            }

            List <string> fullMatch = new List <string>();

            for (int nodeIndex = 0; nodeIndex < nodeNames.Length; nodeIndex++)
            {
                for (int outIndex = 0; outIndex < outNames.Length; outIndex++)
                {
                    string match = string.Format(string.Concat(matchClause, whereClause, returnClause),
                                                 nodeNames[nodeIndex],
                                                 target.ParentEntity.Key.Name,
                                                 target.Relationship.Neo4JRelationshipType,
                                                 outNames[outIndex],
                                                 targetEntity.Key.Name);

                    fullMatch.Add(match);
                }
            }

            Dictionary <string, object?> parameters = new Dictionary <string, object?>();

            parameters.Add("keys", parents.Select(item => item.GetKey()).ToArray());

            if (parents.Any(parent => parent.GetEntity() != target.Parent.GetEntity()))
            {
                throw new InvalidOperationException("This code should only load collections of the same concrete parent class.");
            }

            var result = Transaction.RunningTransaction.Run(string.Join(" UNION ", fullMatch), parameters);
            List <RelationshipIndex> indexCache = new List <RelationshipIndex>();

            foreach (var record in result)
            {
                DateTime?startDate = null;
                DateTime?endDate   = null;

                if (target.Relationship.IsTimeDependent)
                {
                    startDate = (record.Values["StartDate"] != null) ? Conversion <long, DateTime> .Convert((long)record.Values["StartDate"].As <long>()) : (DateTime?)null;

                    endDate = (record.Values["EndDate"] != null) ? Conversion <long, DateTime> .Convert((long)record.Values["EndDate"].As <long>()) : (DateTime?)null;
                }

                indexCache.Add(new RelationshipIndex(record.Values["ParentKey"].As <object>(), record.Values["ItemKey"].As <object>(), startDate, endDate));
            }

            Dictionary <object, List <RawNode> > itemsCache = Load(targetEntity, indexCache.Select(r => r.TargetEntityKey));

            List <CollectionItem> items = new List <CollectionItem>();

            foreach (var index in indexCache)
            {
                OGM parent = parentDict[index.SourceEntityKey];
                foreach (var item in itemsCache[index.TargetEntityKey])
                {
                    OGM?child = ReadNode(parent, targetEntity, item);
                    items.Add(target.NewCollectionItem(parent, child, index.StartDate, index.EndDate));
                }
            }

            return(CollectionItemList.Get(items));
        }
        public override Dictionary <OGM, CollectionItemList> Load(IEnumerable <OGM> parents, Core.EntityCollectionBase target)
        {
            if (parents.Count() == 0)
            {
                return(new Dictionary <OGM, CollectionItemList>());
            }

            HashSet <OGM> parentHashset = new HashSet <OGM>(parents);

            string matchClause = string.Empty;

            if (target.Direction == DirectionEnum.In)
            {
                matchClause = "MATCH ({0})-[rel:{2}]->({3})";
            }
            else if (target.Direction == DirectionEnum.Out)
            {
                matchClause = "MATCH ({0})<-[rel:{2}]-({3})";
            }

            string whereClause  = " WHERE node.{1} in ($keys) ";
            string returnClause = " RETURN node as Parent, out as Item ";

            if (target.Relationship.IsTimeDependent)
            {
                returnClause = $" RETURN node as Parent, out as Item, rel.{target.Relationship.StartDate} as StartDate, rel.{target.Relationship.EndDate} as EndDate";
            }

            Entity targetEntity = target.ForeignEntity;

            string[] nodeNames = target.Parent.GetEntity().GetDbNames("node");
            string[] outNames  = targetEntity.GetDbNames("out");

            if (nodeNames.Length > 1 && outNames.Length > 1)
            {
                throw new InvalidOperationException("Both ends are virtual entities, this is too expensive to query...");
            }

            List <string> fullMatch = new List <string>();

            for (int nodeIndex = 0; nodeIndex < nodeNames.Length; nodeIndex++)
            {
                for (int outIndex = 0; outIndex < outNames.Length; outIndex++)
                {
                    string match = string.Format(string.Concat(matchClause, whereClause, returnClause),
                                                 nodeNames[nodeIndex],
                                                 target.ParentEntity.Key.Name,
                                                 target.Relationship.Neo4JRelationshipType,
                                                 outNames[outIndex]);

                    fullMatch.Add(match);
                }
            }

            Dictionary <string, object?> parameters = new Dictionary <string, object?>();

            parameters.Add("keys", parents.Select(item => item.GetKey()).ToArray());

            if (parents.Any(parent => parent.GetEntity() != target.Parent.GetEntity()))
            {
                throw new InvalidOperationException("This code should only load collections of the same concrete parent class.");
            }

            string cypher = string.Join(" UNION ", fullMatch);
            var    result = Transaction.RunningTransaction.Run(cypher, parameters);
            List <CollectionItem> items = new List <CollectionItem>();

            foreach (var record in result)
            {
                DateTime?startDate = null;
                DateTime?endDate   = null;

                if (target.Relationship.IsTimeDependent)
                {
                    startDate = (record.Values["StartDate"] is not null) ? Conversion <long, DateTime> .Convert((long)record.Values["StartDate"].As <long>()) : (DateTime?)null;

                    endDate = (record.Values["EndDate"] is not null) ? Conversion <long, DateTime> .Convert((long)record.Values["EndDate"].As <long>()) : (DateTime?)null;
                }
                OGM?parent = target.Parent.GetEntity().Map(record.Values["Parent"].As <RawNode>(), NodeMapping.AsWritableEntity);
                OGM?item   = targetEntity.Map(record.Values["Item"].As <RawNode>(), NodeMapping.AsWritableEntity);

                if (parent is null || item is null)
                {
                    throw new NotSupportedException("The cypher query expected to have a parent node and a child node.");
                }

                if (parentHashset.Contains(parent))
                {
                    items.Add(target.NewCollectionItem(parent, item, startDate, endDate));
                }
            }

            return(CollectionItemList.Get(items));
        }