internal BlBinaryExpression ApplySoftDeleteFilterIfApplicable(bool includeDeleted, BlBinaryExpression filter, BlsPawn pawn) { string containerName = Graph.GetStorageContainerNameForPawn(pawn); BlGraphContainer container = Graph.CompiledCollections.FirstOrDefault(c => c.StorageContainerName == containerName); BlContainerProp softDeleteProp = container?.Properties.FirstOrDefault(prop => prop.IsSoftDeleteProp); if (softDeleteProp == null) { return(filter); } var softDeleteClause = new BlBinaryExpression { PropName = softDeleteProp.Name, Operator = BlOperator.Eq, Value = includeDeleted }; if (filter == null) { return(softDeleteClause); } var newRoot = new BlBinaryExpression { Left = filter, Operator = BlOperator.And, Right = softDeleteClause }; return(newRoot); }
/// <summary> /// Use this method to find pawns based on their properties. If you do not provide any /// filter predicates, the method will return all pawns of the specified type. /// </summary> /// <param name="includeSoftDeleted">Also retrieve soft deleted pawns if set to true; false by default</param> /// <param name="filter">Boolean expression specifying the filtering conditions</param> /// <param name="sortProperty">Property of the pawn to use for sorting</param> /// <param name="sortDir">Ascending or Descending</param> /// <param name="batchSize">Number of records to return in the <see cref="BLS.StorageCursor"/> class></param> /// <typeparam name="TPawn">Type of the pawn</typeparam> /// <returns>Storage Cursor containing the resulting collection of pawns</returns> /// <exception cref="NotImplementedException"></exception> public StorageCursor <TPawn> Find <TPawn>( Expression <Func <TPawn, bool> > filter = null, Expression <Func <TPawn, IComparable> > sortProperty = null, Sort sortDir = Sort.Asc, bool includeSoftDeleted = false, int batchSize = 200) where TPawn : BlsPawn, new() { string container = Graph.GetStorageContainerNameForPawn(new TPawn()); BlBinaryExpression filterExpression = ResolveFilterExpression(filter); filterExpression = ApplySoftDeleteFilterIfApplicable(includeSoftDeleted, filterExpression, new TPawn()); string sortProp = ResolveSortExpression(sortProperty); var cursor = StorageProvider.FindInContainer <TPawn>(container, filterExpression, sortProp, sortDir.ToString(), batchSize); var additions = ToAddBuffer .Where(p => p.GetType() == typeof(TPawn)) .Cast <TPawn>(); var updates = ToUpdate .Where(p => p.GetType() == typeof(TPawn)) .Select(p => (TPawn)p); if (filter == null) { cursor.AttachInMemoryPawns(additions.ToList()).AttachInMemoryPawns(updates.ToList()); } return(cursor); }
/// <summary> /// Call the method to get number of related pawns. The result will include both in-storage and in-memory pawns /// </summary> /// <param name="includeSotDeleted">If set to true, the method ignores soft-delete flag if one is available on the /// pawn model. Set to false by default</param> /// <returns>Number of related pawns</returns> /// <exception cref="NotImplementedException"></exception> public int GetCount(bool includeSotDeleted = false) { BlBinaryExpression filter = SourcePawn.SystemRef .ApplySoftDeleteFilterIfApplicable(includeSotDeleted, null, new T()); string containerName = SourcePawn.SystemRef.Graph.GetStorageContainerNameForPawn(new T()); return(SourcePawn.SystemRef.StorageProvider.GetContainerCount(containerName, filter)); }
// recursive method to resolve AND/OR binary filter expressions private BlBinaryExpression ResolveBinaryFilterExpression(BinaryExpression expression, BlBinaryExpression newExpression) { newExpression.Left = new BlBinaryExpression(); newExpression.Right = new BlBinaryExpression(); switch (expression.NodeType) { case ExpressionType.AndAlso: { newExpression.Operator = BlOperator.And; break; } case ExpressionType.OrElse: { newExpression.Operator = BlOperator.Or; break; } } if (expression.Left.GetType().Name == MethodBinaryExpression) { newExpression.Left = ResolveComparisonFilterExpression(expression.Left as BinaryExpression); } else { newExpression.Left = ResolveBinaryFilterExpression(expression.Left as BinaryExpression, newExpression.Left); } if (expression.Right.GetType().Name == MethodBinaryExpression) { newExpression.Right = ResolveComparisonFilterExpression(expression.Right as BinaryExpression); } else { newExpression.Right = ResolveBinaryFilterExpression(expression.Right as BinaryExpression, newExpression.Right); } return(newExpression); }
/// <summary> /// Call the method to search for pawns based on the search term, optionally specifying /// any additional filters and sort /// </summary> /// <param name="searchTerm">The term to search</param> /// <param name="filter">Boolean expression specifying any additional filter conditions</param> /// <param name="includeSoftDeleted">Also retrieve soft deleted pawns if set to true; false by default</param> /// <param name="sortProperty">Property of the pawn to use for sorting</param> /// <param name="sortDir">Ascending or Descending</param> /// <param name="batchSize">Number of records to return in the <see cref="BLS.StorageCursor"/> class</param> /// <param name="searchProperties">List of property expressions to apply the search on. Only properties /// marked with the <see cref="BLS.Functional.FullTextSearchable"/> attribute are allowed in the list of</param> /// <typeparam name="TPawn">Type of the pawn</typeparam> /// <returns>Storage Cursor containing the resulting collection of pawns</returns> /// <exception cref="NotImplementedException"></exception> public StorageCursor <TPawn> Search <TPawn>( string searchTerm, Expression <Func <TPawn, string[]> > searchProperties, Expression <Func <TPawn, bool> > filter = null, Expression <Func <TPawn, IComparable> > sortProperty = null, Sort sortDir = Sort.Asc, bool includeSoftDeleted = false, int batchSize = 200) where TPawn : BlsPawn, new() { var container = Graph.GetStorageContainerNameForPawn(new TPawn()); BlBinaryExpression filterExpression = ResolveFilterExpression(filter); filterExpression = ApplySoftDeleteFilterIfApplicable(includeSoftDeleted, filterExpression, new TPawn()); string sortProp = ResolveSortExpression(sortProperty); return(StorageProvider.SearchInContainer <TPawn>( container, ResolveSearchProperties(searchProperties), searchTerm, filterExpression, sortProp, sortDir.ToString(), batchSize)); }
/// <summary> /// Use this method to retrieve related objects of the certain <typeparam name="T"></typeparam> type. The method /// returns a cursor which contains pawns in storage as well as pawns currently sitting in the BLS memory /// </summary> /// <param name="filter">Optional filter to apply to the result set; the filter will be applied to both the im-memory /// and in-storage data</param> /// <param name="sortDir"></param> /// <param name="includeSoftDeleted">If set to true, the method ignores soft-delete flag if one is available on the /// pawn model. Set to false by default</param> /// <param name="batchSize">Controls the size of the batch of each incremental retrieval of pawns from storage. Defaults to 200 objects</param> /// <param name="sortProperty"></param> /// <returns>Cursor containing the result set</returns> /// <exception cref="InvalidOperationException"></exception> public StorageCursor <T> Find( Expression <Func <T, bool> > filter = null, Expression <Func <T, IComparable> > sortProperty = null, Sort sortDir = Sort.Asc, bool includeSoftDeleted = false, int batchSize = 200) { Connection[] connections = SourcePawn.SystemRef.ToConnect .Where(c => c.From == SourcePawn) .ToArray(); BlGraphContainer container = SourcePawn.SystemRef.Graph.CompiledCollections .FirstOrDefault(c => c.BlContainerName == typeof(T).Name); if (container == null) { throw new InvalidOperationException("collection not found"); } string id = SourcePawn.GetId(); string relationName = SourcePawn.SystemRef.Graph.GetStorageRelationName(this); string containerName = SourcePawn.SystemRef.Graph.GetStorageContainerNameForPawn(SourcePawn); if (filter == null) { var filterExpression = SourcePawn.SystemRef .ApplySoftDeleteFilterIfApplicable(includeSoftDeleted, null, new T()); List <T> connectedPawns = connections.Select(c => (T)c.To).ToList(); // if there is no id, the pawn has not been saved yet so there only return in-memory relations if (string.IsNullOrEmpty(id)) { return(new StorageCursor <T>().AttachInMemoryPawns(connectedPawns).InjectBls(SourcePawn.SystemRef)); } // otherwise, also check the storage string sort = SourcePawn.SystemRef.ResolveSortExpression(sortProperty); var cursorFromStorage = SourcePawn.SystemRef.StorageProvider.GetByRelation <T>(id, relationName, containerName, filterExpression, sort, sortDir, batchSize); return(cursorFromStorage.AttachInMemoryPawns(connectedPawns).InjectBls(SourcePawn.SystemRef)); } // the rest of the code assumes the filter is not null if (string.IsNullOrEmpty(id)) { List <T> foundRelations = connections.Select(c => (T)c.To).ToList(); List <T> connectedPawns = foundRelations.Where(filter.Compile()).ToList(); return(new StorageCursor <T>().AttachInMemoryPawns(connectedPawns).InjectBls(SourcePawn.SystemRef)); } else { List <T> foundRelations = connections.Select(c => (T)c.To).ToList(); List <T> connectedPawns = foundRelations.Where(filter.Compile()).ToList(); BlBinaryExpression storageFilterExpression = SourcePawn.SystemRef.ResolveFilterExpression(filter); storageFilterExpression = SourcePawn.SystemRef .ApplySoftDeleteFilterIfApplicable(includeSoftDeleted, storageFilterExpression, new T()); string sort = SourcePawn.SystemRef.ResolveSortExpression(sortProperty); StorageCursor <T> cursorFromStorage = SourcePawn.SystemRef.StorageProvider.GetByRelation <T>(id, relationName, containerName, storageFilterExpression, sort, sortDir, batchSize); return(cursorFromStorage.AttachInMemoryPawns(connectedPawns).InjectBls(SourcePawn.SystemRef)); } }
// method to resolve final comparison (==, !=, >, etc.) expressions in the filter expression private BlBinaryExpression ResolveComparisonFilterExpression(BinaryExpression expression) { if (expression.Left.GetType().Name != PropertyExpressionType) { throw new IncorrectFilterArgumentStructureError( $"Left operand of the filter expression has to be a property accessor . You provided {expression.Left.GetType().Name}"); } BlBinaryExpression resultExpression = new BlBinaryExpression(); switch (expression.NodeType) { case ExpressionType.Equal: { resultExpression.Operator = BlOperator.Eq; break; } case ExpressionType.NotEqual: { resultExpression.Operator = BlOperator.NotEq; break; } case ExpressionType.GreaterThan: { resultExpression.Operator = BlOperator.Grt; break; } case ExpressionType.GreaterThanOrEqual: { resultExpression.Operator = BlOperator.GrtOrEq; break; } case ExpressionType.LessThan: { resultExpression.Operator = BlOperator.Ls; break; } case ExpressionType.LessThanOrEqual: { resultExpression.Operator = BlOperator.LsOrEq; break; } default: { throw new NotSupportedException("incorrect binary operator"); } } if (expression.Left is MemberExpression propAccessor) { resultExpression.PropName = propAccessor.Member.Name; } resultExpression.Value = Expression.Lambda(expression.Right).Compile().DynamicInvoke(); resultExpression.IsLeaf = true; return(resultExpression); }