public MochaRelationship this[RelationshipKey key] { get { if (_itemsByKey.ContainsKey(key)) { return(_itemsByKey[key]); } return(null); } }
/// <summary> /// Try to load a relationship. /// </summary> /// <returns>True if the value was loaded, or if the value was originally requested. False if it was never originally loaded.</returns> public virtual bool TryGetRelationship(long entityId, long relTypeId, Direction direction, out IReadOnlyCollection <long> resultValues) { long relTypeAndDir = direction == Direction.Forward ? relTypeId : -relTypeId; RelationshipKey key = new RelationshipKey(entityId, relTypeAndDir); List <long> values; if (!_unsecuredGraphData.Relationships.TryGetValue(key, out values)) { resultValues = Empty; bool wasRequested = WasRelationshipRequested(entityId, relTypeId, direction); return(wasRequested); } resultValues = values; return(true); }
/// <summary> /// Scan for document token fields. /// </summary> private void LoadDocumentCaches(IProcessingContext context) { _binaryCache = new List <BinaryDataEntry>( ); _documentCache = new List <DocumentDataEntry>( ); HashSet <string> tokensDone = new HashSet <string>( ); // GUID for fileDataHash field. long fileDataHash = new EntityRef("fileDataHash").Id; long isOfType = WellKnownAliases.CurrentTenant.IsOfType; // Determine types that are 'binary'. Others are 'document'. ISet <long> fileTypes = PerTenantEntityTypeCache.Instance.GetDescendantsAndSelf(new EntityRef("fileType").Id); ISet <long> binaryTypes = PerTenantEntityTypeCache.Instance.GetDescendantsAndSelf(new EntityRef("imageFileType").Id); ISet <long> documentTypes = new HashSet <long>(fileTypes.Except(binaryTypes)); // Callbacks to load data Func <string, byte[]> loadDocumentData = token => FileRepositoryUtils.LoadFileData(Factory.DocumentFileRepository, token, context); Func <string, byte[]> loadBinaryData = token => FileRepositoryUtils.LoadFileData(Factory.BinaryFileRepository, token, context); foreach (KeyValuePair <FieldKey, FieldValue> field in _bulkResult.FieldValues) { if (field.Key.FieldId != fileDataHash) { continue; } // Get token string token = field.Value?.RawValue as string; if (string.IsNullOrEmpty(token)) { continue; } if (tokensDone.Contains(token)) { continue; } tokensDone.Add(token); // Get entity type long entityId = field.Key.EntityId; RelationshipKey typeRelKey = new RelationshipKey(entityId, isOfType); List <long> types; _bulkResult.Relationships.TryGetValue(typeRelKey, out types); long?singleType = types?.FirstOrDefault( ); if (singleType == null) { continue; } long typeId = singleType.Value; // Determine type bool isBinary = binaryTypes.Contains(typeId); bool isDoc = documentTypes.Contains(typeId); // Create entry if (isBinary) { var binaryDataEntry = new BinaryDataEntry { DataHash = token, LoadDataCallback = loadBinaryData }; _binaryCache.Add(binaryDataEntry); } else if (isDoc) { var docDataEntry = new DocumentDataEntry { DataHash = token, LoadDataCallback = loadDocumentData }; _documentCache.Add(docDataEntry); } } }
/// <summary> /// Load relationships. /// </summary> /// <param name="context"></param> /// <returns></returns> public IEnumerable <RelationshipEntry> GetRelationships(IProcessingContext context) { if (_relationshipCache != null) { return(_relationshipCache); } // Create relationship entries _relationshipCache = new List <RelationshipEntry>( ); foreach (var pair in _bulkResult.Relationships) { long originId = pair.Key.EntityId; long relTypeId = pair.Key.TypeId; Direction direction = pair.Key.Direction; List <long> destinations = pair.Value; // Security check source if (!_canRead(originId)) { continue; } // Determine the clone action RelationshipInfo relInfo; if (!_bulkResult.BulkSqlQuery.Relationships.TryGetValue(pair.Key.TypeIdAndDirection, out relInfo)) { continue; // assert false } var effectiveCloneAction = direction == Direction.Forward ? relInfo.CloneAction : relInfo.ReverseCloneAction; // If the cloneAction is a drop, then the export action is ordinarily also a drop // However, if the cloneAction is the other direction is a clone entities, then it must actually be a single component, and this is // the scenario where export&clone behave differently. Clone drops because it must to maintain cardinality, but the reference // still makes sense for an export. See #28790 if (effectiveCloneAction == CloneActionEnum_Enumeration.Drop) { var oppCloneAction = direction == Direction.Forward ? relInfo.ReverseCloneAction : relInfo.CloneAction; if (oppCloneAction == CloneActionEnum_Enumeration.CloneEntities) { effectiveCloneAction = CloneActionEnum_Enumeration.CloneReferences; } } bool isDrop = effectiveCloneAction == CloneActionEnum_Enumeration.Drop; // Prepare to handle duplicate entries bool wouldDiscardIfDuplicate = WouldDiscardIfDuplicate(relInfo, direction); // Resolve IDs Guid originUid; Guid relTypeUid; if (!_idToUpgradeId.TryGetValue(originId, out originUid)) { continue; } if (!_idToUpgradeId.TryGetValue(relTypeId, out relTypeUid)) { continue; } // Add each entry foreach (long destId in destinations) { // If clone action is 'drop' then only include the relationship if the target is also present in the dataset if (isDrop) { EntityValue entityValue; if (!_bulkResult.AllEntities.TryGetValue(destId, out entityValue)) { continue; // assert false } if (IsReferenceOnly(entityValue)) { continue; // external node when cloneAction is drop } } // If relationship is present in both directions, then drop the reverse if (wouldDiscardIfDuplicate) { RelationshipKey reverseKey = new RelationshipKey(destId, -pair.Key.TypeIdAndDirection); List <long> reverseIds; if (_bulkResult.Relationships.TryGetValue(reverseKey, out reverseIds)) { if (reverseIds.Contains(originId)) { continue; } } } Guid destUid; if (!_idToUpgradeId.TryGetValue(destId, out destUid)) { continue; } RelationshipEntry entry; if (direction == Direction.Forward) { entry = new RelationshipEntry(relTypeUid, originUid, destUid); } else { entry = new RelationshipEntry(relTypeUid, destUid, originUid); } _relationshipCache.Add(entry); } } return(_relationshipCache); }
/*========================================================================================================================== | CONSTRUCTOR \-------------------------------------------------------------------------------------------------------------------------*/ /// <summary> /// Given a <see cref="PropertyInfo"/> instance, exposes a set of properties associated with known <see cref="Attribute"/> /// instances. /// </summary> /// <param name="property">The <see cref="PropertyInfo"/> instance to check for <see cref="Attribute"/> values.</param> /// <param name="attributePrefix">The prefix to apply to the attributes.</param> public PropertyConfiguration(PropertyInfo property, string? attributePrefix = "") { /*------------------------------------------------------------------------------------------------------------------------ | Validate parameters \-----------------------------------------------------------------------------------------------------------------------*/ Contract.Requires(property, nameof(property)); /*------------------------------------------------------------------------------------------------------------------------ | Set backing property \-----------------------------------------------------------------------------------------------------------------------*/ Property = property; /*------------------------------------------------------------------------------------------------------------------------ | Set default values \-----------------------------------------------------------------------------------------------------------------------*/ AttributeKey = attributePrefix + property.Name; AttributePrefix = attributePrefix; DefaultValue = null; InheritValue = false; RelationshipKey = AttributeKey; RelationshipType = RelationshipType.Any; CrawlRelationships = Relationships.None; MetadataKey = null; DisableMapping = false; AttributeFilters = new(); FlattenChildren = false; /*------------------------------------------------------------------------------------------------------------------------ | Attributes: Retrieve basic attributes \-----------------------------------------------------------------------------------------------------------------------*/ GetAttributeValue<DefaultValueAttribute>(property, a => DefaultValue = a.Value); GetAttributeValue<InheritAttribute>(property, a => InheritValue = true); GetAttributeValue<AttributeKeyAttribute>(property, a => AttributeKey = attributePrefix + a.Value); GetAttributeValue<MapToParentAttribute>(property, a => MapToParent = true); GetAttributeValue<MapToParentAttribute>(property, a => AttributePrefix += (a.AttributePrefix?? property.Name)); GetAttributeValue<FollowAttribute>(property, a => CrawlRelationships = a.Relationships); GetAttributeValue<FlattenAttribute>(property, a => FlattenChildren = true); GetAttributeValue<MetadataAttribute>(property, a => MetadataKey = a.Key); GetAttributeValue<DisableMappingAttribute>(property, a => DisableMapping = true); /*------------------------------------------------------------------------------------------------------------------------ | Attributes: Determine relationship key and type \-----------------------------------------------------------------------------------------------------------------------*/ GetAttributeValue<RelationshipAttribute>( property, a => { RelationshipKey = a.Key ?? RelationshipKey; RelationshipType = a.Type; } ); if ( RelationshipType is RelationshipType.Any && RelationshipKey.Equals("Children", StringComparison.InvariantCultureIgnoreCase) ) { RelationshipType = RelationshipType.Children; } /*------------------------------------------------------------------------------------------------------------------------ | Attributes: Set attribute filters \-----------------------------------------------------------------------------------------------------------------------*/ var filterByAttribute = property.GetCustomAttributes<FilterByAttributeAttribute>(true); if (filterByAttribute is not null && filterByAttribute.Any()) { foreach (var filter in filterByAttribute) { AttributeFilters.Add(filter.Key, filter.Value); } } }