using System.Collections.Concurrent; using System.Runtime.InteropServices; using ZB.MOM.WW.CBDD.Bson; using ZB.MOM.WW.CBDD.Core.Storage; namespace ZB.MOM.WW.CBDD.Core.Collections; public partial class DocumentCollection where T : class { /// /// Scans the entire collection using a raw BSON predicate. /// This avoids deserializing documents that don't match the criteria. /// /// Function to evaluate raw BSON data /// Matching documents internal IEnumerable Scan(Func predicate) { if (predicate == null) throw new ArgumentNullException(nameof(predicate)); var transaction = _transactionHolder.GetCurrentTransactionOrStart(); var txnId = transaction.TransactionId; var pageCount = _storage.PageCount; var buffer = new byte[_storage.PageSize]; var pageResults = new List(); for (uint pageId = 0; pageId < pageCount; pageId++) { pageResults.Clear(); ScanPage(pageId, txnId, buffer, predicate, pageResults); foreach (var doc in pageResults) { yield return doc; } } } /// /// Scans the collection in parallel using multiple threads. /// Useful for large collections on multi-core machines. /// /// Function to evaluate raw BSON data /// Number of threads to use (default: -1 = ProcessorCount) internal IEnumerable ParallelScan(Func predicate, int degreeOfParallelism = -1) { if (predicate == null) throw new ArgumentNullException(nameof(predicate)); var transaction = _transactionHolder.GetCurrentTransactionOrStart(); var txnId = transaction.TransactionId; var pageCount = (int)_storage.PageCount; if (degreeOfParallelism <= 0) degreeOfParallelism = Environment.ProcessorCount; return Partitioner.Create(0, pageCount) .AsParallel() .WithDegreeOfParallelism(degreeOfParallelism) .SelectMany(range => { var localBuffer = new byte[_storage.PageSize]; var localResults = new List(); for (int i = range.Item1; i < range.Item2; i++) { ScanPage((uint)i, txnId, localBuffer, predicate, localResults); } return localResults; }); } private void ScanPage(uint pageId, ulong txnId, byte[] buffer, Func predicate, List results) { _storage.ReadPage(pageId, txnId, buffer); var header = SlottedPageHeader.ReadFrom(buffer); if (header.PageType != PageType.Data) return; var slots = MemoryMarshal.Cast( buffer.AsSpan(SlottedPageHeader.Size, header.SlotCount * SlotEntry.Size)); for (int i = 0; i < header.SlotCount; i++) { var slot = slots[i]; if (slot.Flags.HasFlag(SlotFlags.Deleted)) continue; var data = buffer.AsSpan(slot.Offset, slot.Length); var reader = new BsonSpanReader(data, _storage.GetKeyReverseMap()); if (predicate(reader)) { var doc = FindByLocation(new DocumentLocation(pageId, (ushort)i)); if (doc != null) results.Add(doc); } } } }