Пример #1
0
        public IEnumerable <RelationById> PerformGetParentRelations(HiveId childId, RelationType relationType = null)
        {
            if (childId.Value.Type != HiveIdValueTypes.Guid)
            {
                return(Enumerable.Empty <RelationById>());
            }

            using (NhProfilerLogging.Start(NhSession, "PerformGetParentRelations",
                                           new OdbcParameter("childId", childId),
                                           new OdbcParameter("relationType", relationType)))
            {
                var value = (Guid)childId.Value;
                // Reference the value here because otherwise it gets lost in NH's query cache

                var query = NhSession.QueryOver <NodeRelation>()
                            .Where(x => x.EndNode.Id == value)
                            .Fetch(x => x.StartNode).Lazy
                            .Fetch(x => x.EndNode).Lazy
                            .Fetch(x => x.NodeRelationTags).Eager
                            .Fetch(x => x.NodeRelationType).Eager;

                if (relationType != null)
                {
                    var relationName  = relationType.RelationName;
                    var nodeRelations = query.JoinQueryOver <NodeRelationType>(x => x.NodeRelationType).Where(x => x.Alias == relationName).Cacheable().List();
                    return(nodeRelations.Select(MapNodeRelation).ToList());
                }

                var relations = query.Cacheable().List();
                return(relations.Select(MapNodeRelation).ToList());
            }
        }
Пример #2
0
 private IEnumerable <NodeRelation> GetDbRelation(string relationName, Guid sourceValue, Guid destValue)
 {
     return(NhSession.QueryOver <NodeRelation>()
            .Where(x => x.StartNode.Id == sourceValue)
            .And(x => x.EndNode.Id == destValue)
            .Fetch(x => x.StartNode).Lazy
            .Fetch(x => x.EndNode).Lazy
            .Fetch(x => x.NodeRelationTags).Eager
            .Fetch(x => x.NodeRelationType).Eager
            .JoinQueryOver(x => x.NodeRelationType)
            .Where(x => x.Alias == relationName)
            .Cacheable()
            .List()
            .Distinct()); // This query generates a cartesian product of the NodeRelationTags and NH 'handily' gives us x * NodeRelations so can't do a Take(1)
 }
Пример #3
0
        public NodeRelationType GetOrCreateNodeRelationType(string alias, AbstractScopedCache repositoryScopedCache)
        {
            return(repositoryScopedCache.GetOrCreateTyped(
                       "NodeRelationType-" + alias,
                       () =>
            {
                var existing = NhSession.QueryOver <NodeRelationType>()
                               .Where(x => x.Alias == alias)
                               .Cacheable()
                               .Take(1)
                               .SingleOrDefault <NodeRelationType>();

                return existing ?? new NodeRelationType()
                {
                    Alias = alias, Id = alias.EncodeAsGuid()
                };
            }));
        }
Пример #4
0
        public IEnumerable <RelationById> PerformGetBranchRelations(HiveId siblingId, RelationType relationType = null)
        {
            if (siblingId.Value.Type != HiveIdValueTypes.Guid)
            {
                return(Enumerable.Empty <RelationById>());
            }

            using (NhProfilerLogging.Start(NhSession, "PerformGetBranchRelations",
                                           new OdbcParameter("siblingId", siblingId),
                                           new OdbcParameter("relationType", relationType)))
            {
                var value = (Guid)siblingId.Value;
                // Reference the value here because otherwise it gets lost in NH's query cache

                var query = NhSession.QueryOver <NodeRelation>()
                            .Fetch(x => x.StartNode).Lazy
                            .Fetch(x => x.EndNode).Lazy
                            .Fetch(x => x.NodeRelationTags).Eager
                            .Fetch(x => x.NodeRelationType).Eager;

                var parentQuery = QueryOver.Of <NodeRelation>().Where(x => x.EndNode.Id == value);

                if (relationType != null)
                {
                    var copyRelationName = relationType.RelationName;

                    var parentQueryWithType = parentQuery
                                              .JoinQueryOver(x => x.NodeRelationType)
                                              .Where(x => x.Alias == copyRelationName);

                    return(query.WithSubquery.WhereProperty(x => x.StartNode.Id).In(parentQueryWithType.Select(x => x.StartNode.Id).Take(1))
                           .CacheRegion("Relations").Cacheable().List().Select(MapNodeRelation));
                }
                else
                {
                    return(query.WithSubquery.WhereProperty(x => x.StartNode.Id).In(parentQuery.Select(x => x.StartNode.Id).Take(1))
                           .CacheRegion("Relations").Cacheable().List().Select(MapNodeRelation));
                }
            }
        }
Пример #5
0
        public void AddAttributeValueFuturesToSession(Guid[] versionIds, NodeVersion outerVersionSelect)
        {
            Attribute            attribAlias    = null;
            Attribute            aliasForString = null;
            AttributeStringValue stringsLoader  = null;
            var strings = NhSession.QueryOver <Attribute>(() => attribAlias)
                          .Left.JoinAlias(() => attribAlias.AttributeStringValues, () => stringsLoader)
                          .Where(() => attribAlias.NodeVersion.Id.IsIn(versionIds))
                          .Future <Attribute>();

            Attribute            aliasForLongString = null;
            AttributeStringValue longStringsLoader  = null;
            var longStrings = NhSession.QueryOver <Attribute>(() => attribAlias)
                              .Left.JoinAlias(() => attribAlias.AttributeLongStringValues, () => longStringsLoader)
                              .Where(() => attribAlias.NodeVersion.Id.IsIn(versionIds))
                              .Future <Attribute>();

            Attribute             aliasForInteger = null;
            AttributeIntegerValue integerLoader   = null;
            var integers = NhSession.QueryOver <Attribute>(() => attribAlias)
                           .Left.JoinAlias(() => attribAlias.AttributeIntegerValues, () => integerLoader)
                           .Where(() => attribAlias.NodeVersion.Id.IsIn(versionIds))
                           .Future <Attribute>();

            Attribute             aliasForDecimal = null;
            AttributeDecimalValue decimalLoader   = null;
            var decimals = NhSession.QueryOver <Attribute>(() => attribAlias)
                           .Left.JoinAlias(() => attribAlias.AttributeDecimalValues, () => decimalLoader)
                           .Where(() => attribAlias.NodeVersion.Id.IsIn(versionIds))
                           .Future <Attribute>();

            Attribute          aliasForDate = null;
            AttributeDateValue dateLoader   = null;
            var dates = NhSession.QueryOver <Attribute>(() => attribAlias)
                        .Left.JoinAlias(() => attribAlias.AttributeDateValues, () => dateLoader)
                        .Where(() => attribAlias.NodeVersion.Id.IsIn(versionIds))
                        .Future <Attribute>();
        }
Пример #6
0
        public void AddRelation(IReadonlyRelation <IRelatableEntity, IRelatableEntity> item, AbstractScopedCache repositoryScopedCache)
        {
            var sessionIdAsString = GetSessionId().ToString("n");

            using (DisposableTimer.TraceDuration <NhSessionHelper>("In AddRelation for session " + sessionIdAsString, "End AddRelation for session " + sessionIdAsString))
            {
                // Get the source and destination items from the Nh session
                var sourceNode = NhSession.Get <Node>((Guid)item.SourceId.Value);
                var destNode   = NhSession.Get <Node>((Guid)item.DestinationId.Value);

                // Check the Nh session is already aware of the items
                if (sourceNode == null || destNode == null)
                {
                    string extraMessage = string.Empty;
                    if (sourceNode == null)
                    {
                        extraMessage = "Source {0} cannot be found.\n".InvariantFormat(item.SourceId.Value);
                    }
                    if (destNode == null)
                    {
                        extraMessage += "Destination {0} cannot be found.".InvariantFormat(item.DestinationId.Value);
                    }
                    throw new InvalidOperationException(
                              "Before adding a relation between source {0} and destination {1}, you must call AddOrUpdate with those items or they must already exist in the datastore.\n{2}"
                              .InvariantFormat(item.SourceId, item.DestinationId, extraMessage));
                }

                // Try to load an existing relation of the same type between the two
                var relationType = GetOrCreateNodeRelationType(item.Type.RelationName, repositoryScopedCache);

                // Grab the existing relation (if exists) using the compound key of start node / end node / relation type
                var cacheKey = GenerateCacheKeyForRelation(item, relationType);

                NodeRelation relationToReturn = repositoryScopedCache.GetOrCreateTyped(
                    cacheKey,
                    () =>
                {
                    return(NhSession
                           .QueryOver <NodeRelation>()
                           .Where(x => x.StartNode == sourceNode && x.EndNode == destNode && x.NodeRelationType == relationType)
                           .Cacheable()
                           .SingleOrDefault());
                });

                // Avoid a duplicate by checking if one already exists
                if (relationToReturn != null)
                {
                    // Make sure existing relation has ordinal
                    relationToReturn.Ordinal = item.Ordinal;
                }
                else
                {
                    // Create a new relation
                    relationToReturn = new NodeRelation {
                        StartNode = sourceNode, EndNode = destNode, NodeRelationType = relationType, Ordinal = item.Ordinal
                    };
                    relationToReturn = NhSession.Merge(relationToReturn) as NodeRelation;
                }

                // Ensure metadata correct on existing or new entity
                CreateAndAddRelationTags(item, relationToReturn);
            }
        }
Пример #7
0
        public IQueryOver <NodeVersion, NodeVersionStatusHistory> GenerateVersionedQuery(out NodeVersion outerVersionSelectAlias, Guid[] nodeIds = null, RevisionStatusType revisionStatus = null, bool limitToLatestRevision = true)
        {
            // We want the NodeVersion table, joined to the NodeVersionStatusHistory table,
            // ordered by the status history date descending, but we only want the latest one

            Node        node             = null;
            NodeVersion subSelectVersion = null;
            NodeVersionStatusHistory subSelectTopStatus = null;
            NodeVersion           outerVersionSelect    = null;
            NodeVersionStatusType subSelectStatusType   = null;

            // First define the subselection of the top 1 version items when joined and sorted by version status history in date-descending order
            // We also add a clause to say "where the outer selected version's node id equals the subselected node id" since it's selecting the top 1
            // so we want it to be the 1 latest-date item relevant for each row of the outer select
            var subSelectTopStatusByDate = QueryOver.Of(() => subSelectTopStatus)
                                           //.Where(() => subSelectTopStatus.NodeVersion.Id == outerVersionSelect.Id);
                                           .JoinQueryOver(() => subSelectTopStatus.NodeVersion, () => subSelectVersion)
                                           .JoinQueryOver(() => subSelectVersion.Node, () => node)
                                           .Where(() => subSelectTopStatus.NodeVersion.Id == subSelectVersion.Id)
                                           .And(() => outerVersionSelect.Node.Id == node.Id);

            int takeCount = limitToLatestRevision ? 1 : 999;

            // Now we need to add a filter for the revision status type, if one was supplied
            QueryOver <NodeVersionStatusHistory> subSelectTopStatusByDateWithFilter = null;
            QueryOver <NodeVersionStatusHistory> excludeNegatingStatusses           = null;

            if (revisionStatus != null)
            {
                var statusAlias = revisionStatus.Alias;

                if (revisionStatus.NegatedByTypes.Any())
                {
                    NodeVersionStatusHistory negateHistory = null;
                    NodeVersionStatusType    negateType    = null;
                    NodeVersion negateVersion   = null;
                    var         negatingAliases = revisionStatus.NegatedByTypes.Select(x => x.Alias).ToArray();

                    //var first = negatingAliases.First();
                    excludeNegatingStatusses = QueryOver.Of(() => negateHistory)
                                               .JoinAlias(() => negateHistory.NodeVersionStatusType, () => negateType)
                                               .JoinAlias(() => negateHistory.NodeVersion, () => negateVersion)
                                               .Where(() => negateType.Alias.IsIn(negatingAliases))
                                               .And(() => outerVersionSelect.Node == negateVersion.Node)
                                               .Select(Projections.SqlFunction("coalesce", NHibernateUtil.DateTime, Projections.Max <NodeVersionStatusHistory>(x => x.Date), new ConstantProjection(new DateTime(1981, 8, 1))));

                    //excludeNegatingStatusses = QueryOver.Of(() => negateHistory)
                    //    .JoinAlias(() => negateHistory.NodeVersionStatusType, () => negateType)
                    //    .JoinAlias(() => negateHistory.NodeVersion, () => negateVersion)
                    //    .JoinAlias(() => negateVersion.Node, () => node)
                    //    .Where(() => negateType.Alias.IsIn(negatingAliases))
                    //    .And(() => negateHistory.Date > subSelectTopStatus.Date)
                    //    //.And(() => subSelectTopStatus.NodeVersion.Id == negateVersion.Id)
                    //    .And(() => outerVersionSelect.Node.Id == node.Id)
                    //    .Select(x => x.Id)
                    //    .Take(1);

                    //excludeNegatingStatusses = QueryOver.Of(() => negateHistory)
                    //    .JoinAlias(() => negateHistory.NodeVersionStatusType, () => negateType)
                    //    .JoinAlias(() => negateHistory.NodeVersion, () => negateVersion)
                    //    .JoinAlias(() => negateVersion.Node, () => node)
                    //    .Where(() => negateType.Alias.IsIn(negatingAliases))
                    //    .And(() => outerVersionSelect.Node.Id == node.Id)
                    //    .And(() => negateHistory.Date > subSelectTopStatus.Date)
                    //    .OrderBy(x => x.Date).Desc
                    //    .Select(x => x.Id)
                    //    .Take(1);
                }

                var subSelectBuilder = subSelectTopStatusByDate
                                       .And(() => subSelectStatusType.Alias == statusAlias)
                                       .JoinQueryOver(x => subSelectTopStatus.NodeVersionStatusType, () => subSelectStatusType)
                                       .OrderBy(() => subSelectTopStatus.Date).Desc;

                if (excludeNegatingStatusses != null)
                {
                    // Yeah, I know, horrible to check Sql dialect when generating query, but Nh's dialect support doesn't allow a provider-based
                    // way of telling whether the db engine supports / requires "all" to be prefix before a subquery when doing an operation.
                    // e.g. SqlCe requires "blah > all (select max(blah) from blah)", SqlServer doesn't mind, SQLite doesn't support it
                    if (RequiresAllForGtSubquery())
                    {
                        subSelectBuilder = subSelectBuilder.WithSubquery.WhereProperty(() => subSelectTopStatus.Date).GtAll(excludeNegatingStatusses);
                    }
                    else
                    {
                        subSelectBuilder = subSelectBuilder.WithSubquery.WhereProperty(() => subSelectTopStatus.Date).Gt(excludeNegatingStatusses);
                    }

                    //subSelectBuilder = subSelectBuilder.WithSubquery.WhereNotExists(excludeNegatingStatusses);
                    //subSelectBuilder = subSelectBuilder.WithSubquery.WhereProperty(() => subSelectTopStatus.Id).NotIn(excludeNegatingStatusses);
                }

                subSelectTopStatusByDateWithFilter = subSelectBuilder.Select(x => subSelectTopStatus.NodeVersion.Id).Take(takeCount);
                // We have to include a Take here for compatibility with SqlServerCe
            }
            else
            {
                subSelectTopStatusByDateWithFilter = subSelectTopStatusByDate
                                                     .OrderBy(() => subSelectTopStatus.Date).Desc
                                                     .Select(x => subSelectTopStatus.NodeVersion.Id).Take(takeCount);
                // We have to include a Take here for compatibility with SqlServerCe
            }

            NodeVersionStatusHistory outerHistoryForSort = null;
            IQueryOver <NodeVersion, NodeVersionStatusHistory> outerQuery = NhSession.QueryOver <NodeVersion>(
                () => outerVersionSelect)
                                                                            //.Fetch(x => x.AttributeSchemaDefinition).Eager
                                                                            .Fetch(x => x.Attributes).Eager
                                                                            // We load these eagerly rather than in a Future to avoid a separate query
                                                                            .Fetch(x => x.Node).Eager
                                                                            // There's a 1-m mapping between Node-NodeVersion so safe to load this with a join too rather than with a future
                                                                            .Inner.JoinQueryOver(x => outerHistoryForSort.NodeVersionStatusType, () => subSelectStatusType)
                                                                            .Inner.JoinQueryOver(x => outerVersionSelect.NodeVersionStatuses, () => outerHistoryForSort);

            NodeVersion innerVersion = null;
            NodeVersionStatusHistory innerHistory = null;
            NodeVersionStatusType    innerType    = null;
            var buildInnerHistorySubQuery         = QueryOver.Of <NodeVersionStatusHistory>(() => innerHistory)
                                                    .JoinQueryOver(() => innerHistory.NodeVersion, () => innerVersion)
                                                    .JoinQueryOver(() => innerHistory.NodeVersionStatusType, () => innerType)
                                                    .Where(() => innerVersion.Node == outerVersionSelect.Node);

            if (revisionStatus != null)
            {
                var statusAlias = revisionStatus.Alias;
                buildInnerHistorySubQuery = buildInnerHistorySubQuery
                                            .And(() => innerType.Alias == statusAlias);

                NodeVersionStatusType reselectType = null;
                outerQuery = outerQuery
                             .And(() => subSelectStatusType.Alias == statusAlias);

                // Yeah, I know, horrible to check Sql dialect when generating query, but Nh's dialect support doesn't allow a provider-based
                // way of telling whether the db engine supports / requires "all" to be prefix before a subquery when doing an operation.
                // e.g. SqlCe requires "blah > all (select max(blah) from blah)", SqlServer doesn't mind, SQLite doesn't support it
                if (excludeNegatingStatusses != null)
                {
                    if (RequiresAllForGtSubquery())
                    {
                        outerQuery = outerQuery.WithSubquery.WhereProperty(() => outerHistoryForSort.Date).GtAll(excludeNegatingStatusses);
                    }
                    else
                    {
                        outerQuery = outerQuery.WithSubquery.WhereProperty(() => outerHistoryForSort.Date).Gt(excludeNegatingStatusses);
                    }
                }
            }

            var subQueryInnerHistory = buildInnerHistorySubQuery.Select(x => x.Id)
                                       .OrderBy(() => innerHistory.Date).Desc
                                       .Take(takeCount);

            outerQuery = outerQuery
                         .OrderBy(() => outerHistoryForSort.Date).Asc
                         .WithSubquery.WhereProperty(() => outerHistoryForSort.Id).In(subQueryInnerHistory);

            //// Based on perf testing of SqlCe queries, it shaves a lot off execution time if we specify the status type in the
            //// outer query in addition to the subquery
            //if (revisionStatus != null)
            //{
            //    var statusAlias = revisionStatus.Alias;
            //    outerQuery = outerQuery
            //        .And(x => subSelectStatusType.Alias == statusAlias);
            //}

            //outerQuery = outerQuery
            //    .OrderBy(() => outerHistoryForSort.Date).Asc
            //    .WithSubquery.WhereProperty(() => outerVersionSelect.Id).In(subSelectTopStatusByDateWithFilter.Clone());

            if (nodeIds != null && nodeIds.Any())
            {
                outerQuery = outerQuery.And(() => outerVersionSelect.Node.Id.IsIn(nodeIds));
            }

            outerVersionSelectAlias = outerVersionSelect;
            return(outerQuery);
        }