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()); } }
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) }
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() }; })); }
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)); } } }
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>(); }
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); } }
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); }