/// <summary> /// Adds a <see cref="SearchCondition"/> to the collection for a <see cref="MFDataType.MFDatatypeDate"/> /// or <see cref="MFDataType.MFDatatypeTimestamp"/> property definition. /// This method searches solely by the year and month components in the property value , equivalent to using /// a <see cref="DataFunctionCall" /> set to <see cref="DataFunctionCall.SetDataYearAndMonth" />. /// </summary> /// <param name="searchBuilder">The <see cref="MFSearchBuilder"/> to add the condition to.</param> /// <param name="propertyDef">The ID of the property to search by.</param> /// <param name="year">The four-digit year to search by.</param> /// <param name="month">The 1-based number of the month to search by (1 = January, 12 = December).</param> /// <param name="conditionType">What type of search to execute (defaults to <see cref="MFConditionType.MFConditionTypeEqual"/>).</param> /// <param name="parentChildBehavior">Whether to accept matches to parent/child values as well (defaults to <see cref="MFParentChildBehavior.MFParentChildBehaviorNone"/>).</param> /// <param name="indirectionLevels">The indirection levels (from the search object) to access the property to match.</param> /// <returns>The <paramref name="searchBuilder"/> provided, for chaining.</returns> public static MFSearchBuilder YearAndMonth ( this MFSearchBuilder searchBuilder, int propertyDef, int year, int month, MFConditionType conditionType = MFConditionType.MFConditionTypeEqual, MFParentChildBehavior parentChildBehavior = MFParentChildBehavior.MFParentChildBehaviorNone, PropertyDefOrObjectTypes indirectionLevels = null ) { // Sanity. if (null == searchBuilder) { throw new ArgumentNullException(nameof(searchBuilder)); } if (0 > propertyDef) { throw new ArgumentOutOfRangeException(nameof(propertyDef), "Property Ids must be greater than -1; ensure that your property alias was resolved."); } if (month < 1 || month > 12) { throw new ArgumentOutOfRangeException(nameof(month), "The month number must be between 1 and 12 inclusive."); } if (year < 1000 || year > 9999) { throw new ArgumentOutOfRangeException(nameof(year), "The year must be four digits."); } // What is the type of this property? var dataType = searchBuilder.Vault.PropertyDefOperations.GetPropertyDef(propertyDef).DataType; // What is the data type of the property? DataFunctionCall dataFunctionCall; switch (dataType) { case MFDataType.MFDatatypeTimestamp: case MFDataType.MFDatatypeDate: // Timestamps and dates should be converted to text using a data function call. dataFunctionCall = new DataFunctionCall(); dataFunctionCall.SetDataYearAndMonth(); break; default: throw new ArgumentException($"Property {propertyDef} is not a date or timestamp property.", nameof(propertyDef)); } // Use the property method. return(searchBuilder.Property ( propertyDef, $"{year}-{month:00}", conditionType, parentChildBehavior, indirectionLevels, dataFunctionCall )); }
/// <summary> /// Adds a property value search condition to the collection. /// </summary> /// <param name="searchBuilder">The <see cref="MFSearchBuilder"/> to add the condition to.</param> /// <param name="propertyDef">The property to search by.</param> /// <param name="dataType">The data type of the property definition (not checked).</param> /// <param name="value">The value to search for.</param> /// <param name="conditionType">What type of search to execute.</param> /// <param name="parentChildBehavior">Whether to accept matches to parent/child values as well.</param> /// <param name="dataFunctionCall">An expression for modifying how the results of matches are evaluated.</param> /// <param name="indirectionLevels">The indirection levels (from the search object) to access the property to match.</param> /// <returns></returns> private static MFSearchBuilder AddPropertyValueSearchCondition ( this MFSearchBuilder searchBuilder, int propertyDef, MFDataType dataType, object value, MFConditionType conditionType, MFParentChildBehavior parentChildBehavior, PropertyDefOrObjectTypes indirectionLevels, DataFunctionCall dataFunctionCall ) { // Sanity. if (null == searchBuilder) { throw new ArgumentNullException(nameof(searchBuilder)); } // Create the search condition. var searchCondition = new SearchCondition { ConditionType = conditionType }; // Set up the property value expression. searchCondition.Expression.SetPropertyValueExpression ( propertyDef, parentChildBehavior, dataFunctionCall ); // If we have any indirection levels then use them. if (null != indirectionLevels) { // If any indirection level points at a value list then it will except. // Show a nicer error message here. foreach (PropertyDefOrObjectType indirectionLevel in indirectionLevels) { var objectTypeId = indirectionLevel.ID; if (indirectionLevel.PropertyDef) { // If it's a property def then find the object type. PropertyDef indirectionLevelPropertyDef; try { indirectionLevelPropertyDef = searchBuilder .Vault .PropertyDefOperations .GetPropertyDef(indirectionLevel.ID); } catch { indirectionLevelPropertyDef = null; } // Does it exist? if (null == indirectionLevelPropertyDef) { throw new ArgumentException($"An indirection level references a property definition with ID {indirectionLevel.ID}, but this property definition could not be found.", nameof(indirectionLevel)); } // Is it a list-based one? if (false == indirectionLevelPropertyDef.BasedOnValueList) { throw new ArgumentException($"The indirection level for property {indirectionLevel.ID} does not reference a lookup-style property definition.", nameof(indirectionLevel)); } // Record the object type id. objectTypeId = indirectionLevelPropertyDef.ValueList; } // Is it an object type (fine) or a value list (not fine)? { ObjType indirectionLevelObjectType; try { indirectionLevelObjectType = searchBuilder .Vault .ValueListOperations .GetValueList(objectTypeId); } catch { indirectionLevelObjectType = null; } // Does it exist? if (null == indirectionLevelObjectType) { throw new ArgumentException($"An indirection level references a value list with ID {objectTypeId}, but this value list could not be found.", nameof(indirectionLevel)); } // If it's not a real object type then throw. if (false == indirectionLevelObjectType.RealObjectType) { throw new ArgumentException($"An indirection level references an value list with ID {objectTypeId}, but this list does not refer to an object type (cannot be used with value lists).", nameof(indirectionLevel)); } } } // Set the indirection levels. searchCondition.Expression.IndirectionLevels = indirectionLevels; } // Was the value null? if (null == value) { searchCondition.TypedValue.SetValueToNULL(dataType); } else { searchCondition.TypedValue.SetValue(dataType, value); } // Add the search condition to the collection. searchBuilder.Conditions.Add(-1, searchCondition); // Return the search builder for chaining. return(searchBuilder); }
/// <summary> /// Adds a <see cref="SearchCondition"/> to the collection for a <see cref="MFDataType.MFDatatypeInteger"/>, /// <see cref="MFDataType.MFDatatypeInteger64"/> or <see cref="MFDataType.MFDatatypeFloating"/> property definition. /// </summary> /// <param name="searchBuilder">The <see cref="MFSearchBuilder"/> to add the condition to.</param> /// <param name="propertyDef">The ID of the property to search by.</param> /// <param name="value">The value to search for.</param> /// <param name="conditionType">What type of search to execute (defaults to <see cref="MFConditionType.MFConditionTypeEqual"/>).</param> /// <param name="parentChildBehavior">Whether to accept matches to parent/child values as well (defaults to <see cref="MFParentChildBehavior.MFParentChildBehaviorNone"/>).</param> /// <param name="dataFunctionCall">An expression for modifying how the results of matches are evaluated (defaults to null).</param> /// <param name="indirectionLevels">The indirection levels (from the search object) to access the property to match.</param> /// <returns>The <paramref name="searchBuilder"/> provided, for chaining.</returns> public static MFSearchBuilder Property ( this MFSearchBuilder searchBuilder, int propertyDef, int?value, MFConditionType conditionType = MFConditionType.MFConditionTypeEqual, MFParentChildBehavior parentChildBehavior = MFParentChildBehavior.MFParentChildBehaviorNone, PropertyDefOrObjectTypes indirectionLevels = null, DataFunctionCall dataFunctionCall = null ) { // Sanity. if (null == searchBuilder) { throw new ArgumentNullException(nameof(searchBuilder)); } if (0 > propertyDef) { throw new ArgumentOutOfRangeException(nameof(propertyDef), "Property Ids must be greater than -1; ensure that your property alias was resolved."); } // What is the type of this property? var dataType = searchBuilder.Vault.PropertyDefOperations.GetPropertyDef(propertyDef).DataType; // Do we need to change the data type for the data function call? if (dataFunctionCall != null) { switch (dataFunctionCall.DataFunction) { case MFDataFunction.MFDataFunctionDaysFrom: case MFDataFunction.MFDataFunctionDaysTo: case MFDataFunction.MFDataFunctionYear: { // If it's a timestamp then convert to integer. if (dataType == MFDataType.MFDatatypeDate || dataType == MFDataType.MFDatatypeTimestamp) { dataType = MFDataType.MFDatatypeInteger; } // Ensure value has a value. if (null == value) { throw new ArgumentException($"value cannot be null.", nameof(value)); } // If it's a year then it should be four-digits. if (dataFunctionCall.DataFunction == MFDataFunction.MFDataFunctionYear && (value < 1000 || value > 9999)) { throw new ArgumentException($"The year must be four digits.", nameof(value)); } break; } } } // If it is not the right data type then throw. if (dataType != MFDataType.MFDatatypeInteger && dataType != MFDataType.MFDatatypeInteger64 && dataType != MFDataType.MFDatatypeFloating && dataType != MFDataType.MFDatatypeLookup && dataType != MFDataType.MFDatatypeMultiSelectLookup) { throw new ArgumentException($"Property {propertyDef} is not an integer, long, real, lookup or multi-select lookup property.", nameof(propertyDef)); } // Add the search condition. return(searchBuilder.AddPropertyValueSearchCondition ( propertyDef, dataType, value, conditionType, parentChildBehavior, indirectionLevels, dataFunctionCall )); }
/// <summary> /// Runs a method for each segment in the vault. /// </summary> /// <param name="builder">Search Builder to use for queries.</param> /// <param name="func">Method to be executed for each segment, takes a vault and search conditions and returns count.</param> /// <param name="startSegment">The (zero-based) index of the segment to start at.</param> /// <param name="segmentLimit">The number of total segments to process. See <see cref="DefaultMaximumSegmentIndex"/>.</param> /// <param name="segmentSize">The number of items to include in each segment. See <see cref="DefaultNumberOfItemsInSegment"/>.</param> /// <param name="searchTimeoutInSeconds">The timeout for each search. See <see cref="DefaultSearchTimeoutInSeconds"/>. Zero indicates indefinite timeout.</param> /// <returns>Total count of objects across vault.</returns> /// <remarks>Note that <paramref name="searchTimeoutInSeconds"/> applies to the timeout on each segment search; if multiple segments are needed then the maximum time that this method takes to return will exceed the provided value.</remarks> internal static long ForEachSegment( this MFSearchBuilder builder, Func <Vault, SearchConditions, int> func, int startSegment = 0, int segmentLimit = MFSearchBuilderExtensionMethods.DefaultMaximumSegmentIndex, int segmentSize = MFSearchBuilderExtensionMethods.DefaultNumberOfItemsInSegment, int searchTimeoutInSeconds = MFSearchBuilderExtensionMethods.DefaultSearchTimeoutInSeconds) { // Sanity. if (null == func) { throw new ArgumentNullException(nameof(func)); } if (startSegment < 0) { throw new ArgumentOutOfRangeException(nameof(startSegment), "The start segment must be greater than or equal to zero."); } if (segmentSize <= 0) { throw new ArgumentOutOfRangeException(nameof(segmentSize), "The segment size must be greater than zero."); } if (searchTimeoutInSeconds < 0) { throw new ArgumentOutOfRangeException(nameof(searchTimeoutInSeconds), "The search timeout must be greater than zero, or zero to indicate an indefinite timeout"); } // Set our start values. var segment = startSegment; long resultCount = 0; // The total number of matched items in all segments. // Iterate over segments until we hit the sanity limit, // or until there are no items left to find. while (segment < segmentLimit) { // Make a copy of the builder that we can interact with and not // affect the one passed in. var internalBuilder = new MFSearchBuilder(builder.Vault, builder.Conditions); // Add a condition for the current segment that we want. internalBuilder.ObjectIdSegment(segment, segmentSize); // Execute the provided function. // This must return a count of items that were in the current segment, // but it may also execute other code against the items, depending on // what the calling function needs. var searchResultsCount = func(internalBuilder.Vault, internalBuilder.Conditions); // If we got no items back then we need to check whether a higher segment has items. if (searchResultsCount == 0) { // Recreate the internal builder. internalBuilder = new MFSearchBuilder(builder.Vault, builder.Conditions); // Add a condition to see whether there are any items that have an ID in a higher segment. internalBuilder.MinObjId(segment + 1, segmentSize); // Find any matching items that exist in a higher segment. var resultsTopId = internalBuilder .Vault .ObjectSearchOperations .SearchForObjectsByConditionsEx ( internalBuilder.Conditions, MFSearchFlags.MFSearchFlagDisableRelevancyRanking, SortResults: false, MaxResultCount: 1, SearchTimeoutInSeconds: searchTimeoutInSeconds ); // If there are none then break out of the while loop // as there is no point checking further segments. if (resultsTopId.Count == 0) { break; } } // Increment the total count by the count in this segment. resultCount += searchResultsCount; // Advance to the next segment. segment += 1; } // Return the total number of matched items across all segments. return(resultCount); }
public string FindMultifile(Vault env) { var searchMultiFile = new MFSearchBuilder(env); searchMultiFile.Deleted(false); searchMultiFile.ObjType(MFBuiltInObjectType.MFBuiltInObjectTypeDocument); searchMultiFile.Conditions.AddPropertyCondition(22, MFConditionType.MFConditionTypeEqual, MFDataType.MFDatatypeBoolean, false); searchMultiFile.Class(this.Configuration.CurrentConfiguration.ProposalClass); var searchMFResults = searchMultiFile.FindEx(); foreach (var searchResult in searchMFResults) { // If it is an MFD with one file then convert back. if (searchResult.Info.FilesCount == 1) { // Set to SFD. searchResult.SaveProperty( (int)MFBuiltInPropertyDef.MFBuiltInPropertyDefSingleFileObject, MFDataType.MFDatatypeBoolean, true); // Audit trail. // Done. return(""); } try { //Get multifile name var MFTitle = searchResult.Title; // Get a collection of documents for the document collection. var createdItems = new Lookups(); // Get a copy of the current object's properties. var propertiesCopy = this.GetNewObjectPropertyValues(searchResult.Properties); // For each file, create a new object. foreach (var file in searchResult.Info.Files.Cast <ObjectFile>()) { // Add Component Type based on Title if (file.Title.Contains("NPA")) { propertiesCopy.SetProperty(this.Configuration.CurrentConfiguration.ComponentTypeProperty, MFDataType.MFDatatypeLookup, 2); } else if (file.Title.Contains("EOI")) { propertiesCopy.SetProperty(this.Configuration.CurrentConfiguration.ComponentTypeProperty, MFDataType.MFDatatypeLookup, 3); } else if (file.Title.Contains("Price")) { propertiesCopy.SetProperty(this.Configuration.CurrentConfiguration.ComponentTypeProperty, MFDataType.MFDatatypeLookup, 4); } else if (file.Title.Contains("RFT")) { propertiesCopy.SetProperty(this.Configuration.CurrentConfiguration.ComponentTypeProperty, MFDataType.MFDatatypeLookup, 1); } else if (file.Title.Contains("RFP")) { propertiesCopy.SetProperty(this.Configuration.CurrentConfiguration.ComponentTypeProperty, MFDataType.MFDatatypeLookup, 1); } // Download the file. var sourceObjectFiles = this.GetNewObjectSourceFiles(env, file); propertiesCopy.SetProperty(0, MFDataType.MFDatatypeText, file.Title); // Create the new object. var createdObjectId = env.ObjectOperations .CreateNewObjectExQuick( (int)MFBuiltInObjectType.MFBuiltInObjectTypeDocument, propertiesCopy.Clone(), sourceObjectFiles, SFD: true); createdItems.Add(-1, new Lookup { ObjectType = (int)MFBuiltInObjectType.MFBuiltInObjectTypeDocument, Item = createdObjectId, // TODO: Version??? }); } // Add the created documents to a property for the collection. { var collectionMembersPropertyValue = new PropertyValue { PropertyDef = (int)MFBuiltInPropertyDef.MFBuiltInPropertyDefCollectionMemberDocuments }; collectionMembersPropertyValue.Value.SetValueToMultiSelectLookup(createdItems); propertiesCopy.Add(-1, collectionMembersPropertyValue); } propertiesCopy.SetProperty(0, MFDataType.MFDatatypeText, MFTitle); propertiesCopy.RemoveProperty(this.Configuration.CurrentConfiguration.ComponentTypeProperty); propertiesCopy.SetProperty(100, MFDataType.MFDatatypeLookup, this.Configuration.CurrentConfiguration.ProposalCollectionClass); // Create the document collection. var documentCollectionObjVer = new ObjVer { Type = (int)MFBuiltInObjectType.MFBuiltInObjectTypeDocumentCollection, ID = env.ObjectOperations.CreateNewObjectExQuick( (int)MFBuiltInObjectType.MFBuiltInObjectTypeDocumentCollection, propertiesCopy) }; // Can we reference the collection? if (this.Configuration.CurrentConfiguration.DocumentCollectionReference.IsResolved) { // Find items which referenced this (old) document and instead reference the collection. foreach (var objVerEx in env .ObjectOperations .GetRelationships(searchResult.ObjVer, MFRelationshipsMode.MFRelationshipsModeToThisObject) .Cast <ObjectVersion>() .Select(ov => new ObjVerEx(env, ov))) { var checkout = objVerEx.StartRequireCheckedOut(); objVerEx.AddLookup( this.Configuration.CurrentConfiguration.DocumentCollectionReference.ID, documentCollectionObjVer, exactVersion: false); objVerEx.SaveProperties(); objVerEx.EndRequireCheckedOut(checkout); } } //Remove orignal File { searchResult.Delete(); } } catch (Exception e) { SysUtils.ReportErrorToEventLog(e); // Throw. throw; } } return(""); }