public override void ConfigureMappings()
        {
            #region LinearHiveIndexOperation -> IndexOperation

            this.CreateMap <LinearHiveIndexOperation, IndexOperation>(true)
            .CreateUsing(x => new IndexOperation())
            .MapMemberFrom(x => x.Item, x => new IndexItem
            {
                Fields       = x.Fields,
                Id           = x.Id.Value,
                ItemCategory = x.ItemCategory
            })
            .MapMemberFrom(x => x.Operation, x => x.OperationType);

            #endregion


            #region SearchResult -> Relation

            Func <string, int> getOrdinal = (s) =>
            {
                //need to safe parse the ordinal
                int ordinal;
                return(int.TryParse(s, out ordinal) ? ordinal : 0);
            };
            this.CreateMap <SearchResult, IRelationById>()
            .CreateUsing(x => new RelationById(
                             new HiveId(x.Fields[FixedRelationIndexFields.SourceId]),
                             new HiveId(x.Fields[FixedRelationIndexFields.DestinationId]),
                             new RelationType(x.Fields[FixedRelationIndexFields.RelationType]),
                             getOrdinal(x.Fields[FixedIndexedFields.Ordinal])))
            .AfterMap((s, t) =>
            {
                //need to setup the metadata
                foreach (var m in s.Fields.Where(x => x.Key.StartsWith(FixedRelationIndexFields.MetadatumPrefix)))
                {
                    t.MetaData.Add(new RelationMetaDatum(m.Key.Split('.')[1], m.Value));
                }
            });

            #endregion


            #region SearchResult -> Revision<TypedEntity>

            this.CreateMap <SearchResult, Revision <TypedEntity> >(true)
            .CreateUsing(x => new Revision <TypedEntity>())
            .ForMember(x => x.MetaData, x => x.MapUsing(new SearchResultToRevisionData(_helper)))
            .MapMemberFrom(x => x.Item, Map <SearchResult, TypedEntity>)
            .AfterMap((s, t) =>
            {
            });

            #endregion

            #region SearchResult -> TypedEntity

            this.CreateMap(new SearchResultToTypedEntity(_helper, this), true)
            .CreateUsing(x => new TypedEntity())
            .ForMember(x => x.Id, opt => opt.MapFrom(y => HiveId.Parse(y.Fields[FixedIndexedFields.EntityId])))
            .AfterMap((s, t) =>
            {
                ExamineHelper.SetEntityDatesFromSearchResult(t, s);
            });

            #endregion

            #region SearchResult -> EntitySchema

            this.CreateMap <SearchResult, EntitySchema>()
            .CreateUsing(x => new EntitySchema())
            .ForMember(x => x.Id, opt => opt.MapFrom(y => HiveId.Parse(y.Id)))
            .ForMember(x => x.Alias, opt => opt.MapFrom(y => y.Fields.GetValueAsString("Alias")))
            .ForMember(x => x.Name, opt => opt.MapFrom(y => new LocalizedString(y.Fields.GetValueAsString("Name"))))
            .ForMember(x => x.SchemaType, opt => opt.MapFrom(y => y.Fields.GetValueAsString("SchemaType")))
            .MapMemberFrom(x => x.XmlConfiguration,
                           y => y.Fields.GetValueAsString("XmlConfiguration").IsNullOrWhiteSpace()
                                        ? new XDocument()
                                        : XDocument.Parse(y.Fields.GetValueAsString("XmlConfiguration")))
            .AfterMap((s, t) =>
            {
                var groups = _helper.GetMappedGroupsForSchema(t.Id);
                foreach (var g in groups)
                {
                    t.AttributeGroups.Add(g.Item1);
                }

                //find all attribute definitions with this schema id
                var attDefs = _helper.GetAttributeDefinitionsForSchema(t.Id);
                //declare a local cache for found attribute types, see notes below.
                var attributeTypeCache = new List <AttributeType>();

                foreach (var a in attDefs)
                {
                    //ok, we've already looked up the groups and its very IMPORTANT that the same group object is applied
                    //to the AttributeDefinition otherwise problems will occur.
                    //So instead of re-coding the mapping operation for SearchResult -> AttributeDefinition, we'll re-use our
                    //current Map, but we'll ensure that it does not go re-lookup the AttributeGroup. We can do this by removing
                    //the FixedIndexFieldsGroupId item from the fields so it won't think it has a gruop, then we will add the group back.
                    //NOTE: this procedure can be avoided when the ScopedCache is turned on in the ExamineHelper, however
                    // i don't feel that relying on that mechanism is robust. Once we implement an ExamineDataContext to do
                    // the lookup caching instead, then this could be removed.
                    var groupId = a.Fields.ContainsKey(FixedIndexedFields.GroupId) ? a.Fields[FixedIndexedFields.GroupId] : null;
                    a.Fields.Remove(FixedIndexedFields.GroupId);

                    //similar to the above, it is very IMPORTANT that the same AttributeType object is applied to each
                    //of the AttributeDefinitions if they reference the same alias/id. In order to acheive this we will
                    //remove the FixedIndexedFields.AttributeTypeId from the fields so the mapping operation thinks that it
                    //doesn't have one assigned and won't go look it up. We will store the AttributeTypeId locally and look
                    //it up manually and create a local cache to re-assign to the AttributeDefinitions.
                    //NOTE: this procedure can be avoided when the ScopedCache is turned on in the ExamineHelper, however
                    // i don't feel that relying on that mechanism is robust. Once we implement an ExamineDataContext to do
                    // the lookup caching instead, then this could be removed.
                    var attributeTypeId = a.Fields.ContainsKey(FixedIndexedFields.AttributeTypeId) ? a.Fields[FixedIndexedFields.AttributeTypeId] : null;
                    a.Fields.Remove(FixedIndexedFields.AttributeTypeId);

                    //now do the mapping
                    var mappedAttributeDefinition = Map <SearchResult, AttributeDefinition>(a);

                    //now see if we can find the already found group by id
                    if (groupId != null)
                    {
                        var group = t.AttributeGroups.SingleOrDefault(x => x.Id.Value.ToString() == groupId);
                        mappedAttributeDefinition.AttributeGroup = group;
                    }

                    //now see if we can find an attribute type from our cache or from the helper
                    if (attributeTypeId != null)
                    {
                        var attType = attributeTypeCache.SingleOrDefault(x => x.Id.Value.ToString() == attributeTypeId);
                        if (attType == null)
                        {
                            //its not in our cache so look it up and add to cache
                            attType = _helper.PerformGet <AttributeType>(true, LuceneIndexer.IndexNodeIdFieldName, new HiveId(attributeTypeId))
                                      .SingleOrDefault();
                            if (attType != null)
                            {
                                attributeTypeCache.Add(attType);
                            }
                        }
                        mappedAttributeDefinition.AttributeType = attType;
                    }

                    //add the attribute definition
                    t.AttributeDefinitions.Add(mappedAttributeDefinition);
                }

                ExamineHelper.SetEntityDatesFromSearchResult(t, s);
            });

            #endregion

            #region SearchResult -> AttributeDefinition

            this.CreateMap <SearchResult, AttributeDefinition>()
            .CreateUsing(x => new AttributeDefinition())
            .ForMember(x => x.Id, opt => opt.MapFrom(y => HiveId.Parse(y.Id)))
            .ForMember(x => x.Alias, opt => opt.MapFrom(y => y.Fields.GetValueAsString("Alias")))
            .ForMember(x => x.Name, opt => opt.MapFrom(y => new LocalizedString(y.Fields.GetValueAsString("Name"))))
            .ForMember(x => x.Description, opt => opt.MapFrom(y => new LocalizedString(y.Fields.GetValueAsString("Description"))))
            .ForMember(x => x.RenderTypeProviderConfigOverride, opt => opt.MapFrom(y => y.Fields.GetValueAsString("RenderTypeProviderConfigOverride")))
            .AfterMap((s, t) =>
            {
                //need to do Ordinal safely
                int ordinal;
                if (int.TryParse(s.Fields.GetValueAsString(FixedIndexedFields.Ordinal), out ordinal))
                {
                    t.Ordinal = ordinal;
                }

                //lookup the attribute def & group from hive for the attribute def
                if (s.Fields.ContainsKey(FixedIndexedFields.GroupId))
                {
                    var group        = _helper.PerformGet <AttributeGroup>(true, LuceneIndexer.IndexNodeIdFieldName, new HiveId(s.Fields[FixedIndexedFields.GroupId]));
                    t.AttributeGroup = group.SingleOrDefault();
                }
                if (s.Fields.ContainsKey(FixedIndexedFields.AttributeTypeId))
                {
                    var attType     = _helper.PerformGet <AttributeType>(true, LuceneIndexer.IndexNodeIdFieldName, new HiveId(s.Fields[FixedIndexedFields.AttributeTypeId]));
                    t.AttributeType = attType.SingleOrDefault();
                }

                ExamineHelper.SetEntityDatesFromSearchResult(t, s);
            });

            #endregion

            #region SearchResult -> AttributeGroup

            this.CreateMap <SearchResult, AttributeGroup>()
            .CreateUsing(x => new AttributeGroup())
            .ForMember(x => x.Id, opt => opt.MapFrom(y => HiveId.Parse(y.Id)))
            .ForMember(x => x.Alias, opt => opt.MapFrom(y => y.Fields.GetValueAsString("Alias")))
            .ForMember(x => x.Name, opt => opt.MapFrom(y => new LocalizedString(y.Fields.GetValueAsString("Name"))))
            .AfterMap((s, t) =>
            {
                //need to do Ordinal safely
                int ordinal;
                if (int.TryParse(s.Fields.GetValueAsString(FixedIndexedFields.Ordinal), out ordinal))
                {
                    t.Ordinal = ordinal;
                }

                ExamineHelper.SetEntityDatesFromSearchResult(t, s);
            });

            #endregion

            #region SearchResult -> AttributeType

            this.CreateMap <SearchResult, AttributeType>()
            .CreateUsing(x => new AttributeType())
            .ForMember(x => x.Id, opt => opt.MapFrom(y => HiveId.Parse(y.Id)))
            .ForMember(x => x.Alias, opt => opt.MapFrom(y => y.Fields.GetValueAsString("Alias")))
            .ForMember(x => x.Name, opt => opt.MapFrom(y => new LocalizedString(y.Fields.GetValueAsString("Name"))))
            .ForMember(x => x.Description, opt => opt.MapFrom(y => new LocalizedString(y.Fields.GetValueAsString("Description"))))
            .ForMember(x => x.RenderTypeProvider, opt => opt.MapFrom(y => y.Fields.GetValueAsString("RenderTypeProvider")))
            .ForMember(x => x.RenderTypeProviderConfig, opt => opt.MapFrom(y => y.Fields.GetValueAsString("RenderTypeProviderConfig")))
            .AfterMap((s, t) =>
            {
                //need to do Ordinal safely
                int ordinal;
                if (int.TryParse(s.Fields.GetValueAsString(FixedIndexedFields.Ordinal), out ordinal))
                {
                    t.Ordinal = ordinal;
                }

                //create the serialization type based on the FQN stored in the index
                var serializationType = Type.GetType(s.Fields[FixedIndexedFields.SerializationType]);
                if (serializationType == null)
                {
                    //this shouldn't happen but in case something has changed in the index, then we'll default to string
                    t.SerializationType = new StringSerializationType();
                }
                else
                {
                    t.SerializationType = (IAttributeSerializationDefinition)Activator.CreateInstance(serializationType);
                }

                ExamineHelper.SetEntityDatesFromSearchResult(t, s);
            });

            #endregion

            #region EntitySchema -> NestedHiveIndexOperation

            //create a map that supports inheritance as we don't want to create a map for all EntitySchemas
            this.CreateMap <EntitySchema, NestedHiveIndexOperation>(true)
            .CreateUsing(x => new NestedHiveIndexOperation())
            .ForMember(x => x.Entity, opt => opt.MapFrom(y => y))
            .ForMember(x => x.OperationType, opt => opt.MapFrom(y => IndexOperationType.Add))
            .ForMember(x => x.Id, opt => opt.MapFrom(y => new Lazy <string>(() => y.Id.Value.ToString())))    //need to lazy load as it might not be set
            .ForMember(x => x.ItemCategory, opt => opt.MapFrom(y => typeof(EntitySchema).Name))
            .ForMember(x => x.Fields, opt => opt.MapUsing <EntitySchemaToIndexFields>())
            .AfterMap((s, t) =>
            {
                //Create sub operations for each of its children (both attribute definitions and groups)
                foreach (var op in s.AttributeDefinitions.Select(Map <AttributeDefinition, NestedHiveIndexOperation>)
                         .Concat(s.AttributeGroups.Select(Map <AttributeGroup, NestedHiveIndexOperation>)))
                {
                    //NOTE: we need to add this schema id to the fields otherwise we would just add this in the mapping operation for AttributeDefinition if it exposed the schema it belonged to
                    op.Fields.Add(FixedIndexedFields.SchemaId, new Lazy <ItemField>(() => new ItemField(s.Id.Value.ToString())));        //need to add it as lazy since the id might not exist yet
                    t.SubIndexOperations.Add(op);
                }

                //get the relations
                s.MapRelations(t, this);
            });
            #endregion

            #region TypedEntity -> NestedHiveIndexOperation

            this.CreateMap(new TypedEntityToIndexOperation(this), true);

            #endregion

            #region Relation -> NestedHiveIndexOperation

            this.CreateMap <IReadonlyRelation <IRelatableEntity, IRelatableEntity>, NestedHiveIndexOperation>(true)
            .CreateUsing(x => new NestedHiveIndexOperation())
            .ForMember(x => x.Entity, opt => opt.MapFrom(y => y))
            .ForMember(x => x.OperationType, opt => opt.MapFrom(y => IndexOperationType.Add))
            //need to lazy load as ids might not be set yet, this is also a 'composite' id of Source,Dest,Type
            .ForMember(x => x.Id, opt => opt.MapFrom(y => new Lazy <string>(y.GetCompositeId)))
            .ForMember(x => x.ItemCategory, opt => opt.MapFrom(y => "Relation"))
            .ForMember(x => x.Fields, opt => opt.MapUsing <RelationToIndexFields>());

            #endregion

            #region Revision<TypedEntity> -> NestedHiveIndexOperation

            this.CreateMap <Revision <TypedEntity>, NestedHiveIndexOperation>(true)
            .CreateUsing(x => new NestedHiveIndexOperation())
            .AfterMap((s, t) =>
            {
                //first, map the underlying TypedEntity
                var op = Map <TypedEntity, NestedHiveIndexOperation>(s.Item);

                //map all of the properties... we don't have to explicitly declare a map for this, the engine will automatically just create one
                Map(op, t);

                //ensure the rest of the revision data
                _helper.EnsureRevisionDataForIndexOperation(s, t);
            });

            #endregion

            #region AttributeType -> NestedHiveIndexOperation

            //create a map that supports inheritance as we don't want to create a map for all EntitySchemas... this would also be impossible
            this.CreateMap <AttributeType, NestedHiveIndexOperation>(true)
            .CreateUsing(x => new NestedHiveIndexOperation())
            .ForMember(x => x.Entity, opt => opt.MapFrom(y => y))
            .ForMember(x => x.OperationType, opt => opt.MapFrom(y => IndexOperationType.Add))
            .ForMember(x => x.Id, opt => opt.MapFrom(y => new Lazy <string>(() => y.Id.Value.ToString())))    //need to lazy load as it might not be set
            .ForMember(x => x.ItemCategory, opt => opt.MapFrom(y => typeof(AttributeType).Name))
            .ForMember(x => x.Fields, opt => opt.MapUsing(new AttributeTypeToIndexFields(_helper)));

            #endregion

            #region AttributeGroup -> NestedHiveIndexOperation

            //create a map that supports inheritance as we don't want to create a map for all EntitySchemas... this would also be impossible
            this.CreateMap <AttributeGroup, NestedHiveIndexOperation>(true)
            .CreateUsing(x => new NestedHiveIndexOperation())
            .ForMember(x => x.Entity, opt => opt.MapFrom(y => y))
            .ForMember(x => x.OperationType, opt => opt.MapFrom(y => IndexOperationType.Add))
            .ForMember(x => x.Id, opt => opt.MapFrom(y => new Lazy <string>(() => y.Id.Value.ToString())))    //need to lazy load as it might not be set
            .ForMember(x => x.ItemCategory, opt => opt.MapFrom(y => typeof(AttributeGroup).Name))
            .ForMember(x => x.Fields, opt => opt.MapUsing <AttributeGroupToIndexFields>());

            #endregion

            #region AttributeDefinition -> NestedHiveIndexOperation

            //create a map that supports inheritance as we don't want to create a map for all EntitySchemas... this would also be impossible
            this.CreateMap <AttributeDefinition, NestedHiveIndexOperation>(true)
            .CreateUsing(x => new NestedHiveIndexOperation())
            .ForMember(x => x.Entity, opt => opt.MapFrom(y => y))
            .ForMember(x => x.OperationType, opt => opt.MapFrom(y => IndexOperationType.Add))
            .ForMember(x => x.Id, opt => opt.MapFrom(y => new Lazy <string>(() => y.Id.Value.ToString())))    //need to lazy load as it might not be set
            .ForMember(x => x.ItemCategory, opt => opt.MapFrom(y => typeof(AttributeDefinition).Name))
            .ForMember(x => x.Fields, opt => opt.MapUsing <AttributeDefinitionToIndexFields>())
            .AfterMap((s, t) =>
            {
                //Add sub operation for it's AttributeType
                t.SubIndexOperations.Add(Map <AttributeType, NestedHiveIndexOperation>(s.AttributeType));
            });

            #endregion
        }
Beispiel #2
0
        protected override void PerformMap(SearchResult source, TypedEntity target, MappingExecutionScope scope)
        {
            base.PerformMap(source, target, scope);

            //lookup the document type from examine

            var entitySchema = _helper.PerformGet <EntitySchema>(true, LuceneIndexer.IndexNodeIdFieldName, new HiveId(source.Fields[FixedIndexedFields.SchemaId])).ToArray();

            if (!entitySchema.Any())
            {
                throw new DataException("Could not find an item in the index with id " + source.Fields[FixedIndexedFields.SchemaId]);
            }
            target.EntitySchema = entitySchema.SingleOrDefault();

            var ancestorSchemaIds =
                _helper.PeformGetParentRelations(target.EntitySchema.Id, FixedRelationTypes.DefaultRelationType).
                SelectRecursive(
                    x => _helper.PeformGetParentRelations(x.SourceId, FixedRelationTypes.DefaultRelationType)).
                ToArray();

            if (ancestorSchemaIds.Any())
            {
                var ancestorSchemas = _helper.PerformGet <EntitySchema>(true, LuceneIndexer.IndexNodeIdFieldName, ancestorSchemaIds.Select(x => x.SourceId).ToArray()).ToArray();
                target.EntitySchema = new CompositeEntitySchema(target.EntitySchema, ancestorSchemas);
            }

            // We'll check this later if an attribute definition doesn't exist on the current schema so we can check parents
            var compositeSchema = target.EntitySchema as CompositeEntitySchema;

            //now we need to build up the attributes, get all attribute aliases and go from there
            foreach (var f in source.Fields.Where(x =>
                                                  x.Key.StartsWith(FixedAttributeIndexFields.AttributePrefix) &&
                                                  x.Key.EndsWith(FixedAttributeIndexFields.AttributeAlias)))
            {
                //get the alias for the attribute
                var alias = f.Value;

                //now we can use this alias to find the rest of the attributes values
                //var nameKey = FixedAttributeIndexFields.AttributePrefix + alias + "." + FixedAttributeIndexFields.AttributeName;
                var valueKey = FixedAttributeIndexFields.AttributePrefix + alias;
                var idKey    = FixedAttributeIndexFields.AttributePrefix + alias + "." + FixedAttributeIndexFields.AttributeId;

                //find the associated definition in the schema and set it
                var def = target.EntitySchema.AttributeDefinitions.SingleOrDefault(x => x.Alias == alias);

                // Check if the definition is "inherited" because it exists on a parent schema
                if (def == null)
                {
                    if (compositeSchema != null)
                    {
                        def = compositeSchema.AllAttributeDefinitions.SingleOrDefault(x => x.Alias == alias);
                    }
                }

                if (def != null)
                {
                    //get all values for the current value (as some attributes can store multiple named values, not just one)
                    var values = source.Fields
                                 .Where(k => k.Key.StartsWith(valueKey)
                                        //&& !k.Key.EndsWith(FixedAttributeIndexFields.AttributeName)
                                        && !k.Key.EndsWith(FixedAttributeIndexFields.AttributeAlias) &&
                                        !k.Key.EndsWith(FixedAttributeIndexFields.AttributeId));

                    var attribute = new TypedAttribute(def)
                    {
                        Id = HiveId.Parse(source.Fields[idKey])
                    };

                    foreach (var v in values)
                    {
                        //get the value name, it could be blank if this attribute is only storing one value
                        var valueName = v.Key.Substring(valueKey.Length, v.Key.Length - valueKey.Length);
                        if (valueName.IsNullOrWhiteSpace())
                        {
                            //if its a null value name, then set the dynamic value
                            attribute.DynamicValue = GetRealValueFromField(def.AttributeType.SerializationType, v.Value);
                        }
                        else
                        {
                            //if its a named value, then set it by name
                            attribute.Values.Add(valueName.TrimStart('.'), GetRealValueFromField(def.AttributeType.SerializationType, v.Value));
                        }
                    }

                    target.Attributes.SetValueOrAdd(attribute);
                }
            }
        }
 protected override IEnumerable <T> PerformGet <T>(bool allOrNothing, params HiveId[] ids)
 {
     return(Helper.PerformGet <T>(allOrNothing, LuceneIndexer.IndexNodeIdFieldName, ids));
 }