using Xunit; using ZB.MOM.WW.CBDD.Core.Collections; using ZB.MOM.WW.CBDD.Bson; using System.Linq; using System.Collections.Generic; using ZB.MOM.WW.CBDD.Core.Indexing; using ZB.MOM.WW.CBDD.Core.Storage; using System; using System.IO; using ZB.MOM.WW.CBDD.Shared; namespace ZB.MOM.WW.CBDD.Tests { public class AdvancedQueryTests : IDisposable { private readonly string _dbPath; private readonly Shared.TestDbContext _db; public AdvancedQueryTests() { _dbPath = Path.Combine(Path.GetTempPath(), $"cbdd_advanced_{Guid.NewGuid()}.db"); _db = new Shared.TestDbContext(_dbPath); // Seed Data _db.TestDocuments.Insert(new TestDocument { Category = "A", Amount = 10, Name = "Item1" }); _db.TestDocuments.Insert(new TestDocument { Category = "A", Amount = 20, Name = "Item2" }); _db.TestDocuments.Insert(new TestDocument { Category = "B", Amount = 30, Name = "Item3" }); _db.TestDocuments.Insert(new TestDocument { Category = "B", Amount = 40, Name = "Item4" }); _db.TestDocuments.Insert(new TestDocument { Category = "C", Amount = 50, Name = "Item5" }); _db.SaveChanges(); } public void Dispose() { _db.Dispose(); if (File.Exists(_dbPath)) File.Delete(_dbPath); } [Fact] public void GroupBy_Simple_Key_Works() { var groups = _db.TestDocuments.AsQueryable() .GroupBy(x => x.Category) .ToList(); groups.Count.ShouldBe(3); var groupA = groups.First(g => g.Key == "A"); groupA.Count().ShouldBe(2); groupA.ShouldContain(x => x.Amount == 10); groupA.ShouldContain(x => x.Amount == 20); var groupB = groups.First(g => g.Key == "B"); groupB.Count().ShouldBe(2); var groupC = groups.First(g => g.Key == "C"); groupC.Count().ShouldBe(1); } [Fact] public void GroupBy_With_Aggregation_Select() { var results = _db.TestDocuments.AsQueryable() .GroupBy(x => x.Category) .Select(g => new { Category = g.Key, Total = g.Sum(x => x.Amount) }) .OrderBy(x => x.Category) .ToList(); results.Count.ShouldBe(3); results[0].Category.ShouldBe("A"); results[0].Total.ShouldBe(30); // 10 + 20 results[1].Category.ShouldBe("B"); results[1].Total.ShouldBe(70); // 30 + 40 results[2].Category.ShouldBe("C"); results[2].Total.ShouldBe(50); // 50 } [Fact] public void Aggregations_Direct_Works() { var query = _db.TestDocuments.AsQueryable(); query.Count().ShouldBe(5); query.Sum(x => x.Amount).ShouldBe(150); query.Average(x => x.Amount).ShouldBe(30.0); query.Min(x => x.Amount).ShouldBe(10); query.Max(x => x.Amount).ShouldBe(50); } [Fact] public void Aggregations_With_Predicate_Works() { var query = _db.TestDocuments.AsQueryable().Where(x => x.Category == "A"); query.Count().ShouldBe(2); query.Sum(x => x.Amount).ShouldBe(30); } [Fact] public void Join_Works_InMemory() { // Create a second collection for joining _db.OrderDocuments.Insert(new OrderDocument { ItemName = "Item1", Quantity = 5 }); _db.OrderDocuments.Insert(new OrderDocument { ItemName = "Item3", Quantity = 2 }); _db.SaveChanges(); var query = _db.TestDocuments.AsQueryable() .Join(_db.OrderDocuments.AsQueryable(), doc => doc.Name, order => order.ItemName, (doc, order) => new { doc.Name, doc.Category, order.Quantity }) .OrderBy(x => x.Name) .ToList(); query.Count.ShouldBe(2); query[0].Name.ShouldBe("Item1"); query[0].Category.ShouldBe("A"); query[0].Quantity.ShouldBe(5); query[1].Name.ShouldBe("Item3"); query[1].Category.ShouldBe("B"); query[1].Quantity.ShouldBe(2); } [Fact] public void Select_Project_Nested_Object() { var doc = new ComplexDocument { Id = ObjectId.NewObjectId(), Title = "Order1", ShippingAddress = new Address { City = new City { Name = "New York" }, Street = "5th Ave" }, Items = new List { new OrderItem { Name = "Laptop", Price = 1000 }, new OrderItem { Name = "Mouse", Price = 50 } } }; _db.ComplexDocuments.Insert(doc); _db.SaveChanges(); var query = _db.ComplexDocuments.AsQueryable() .Select(x => x.ShippingAddress) .ToList(); query.Count().ShouldBe(1); query[0].City.Name.ShouldBe("New York"); query[0].Street.ShouldBe("5th Ave"); } [Fact] public void Select_Project_Nested_Field() { var doc = new ComplexDocument { Id = ObjectId.NewObjectId(), Title = "Order1", ShippingAddress = new Address { City = new City { Name = "New York" }, Street = "5th Ave" } }; _db.ComplexDocuments.Insert(doc); _db.SaveChanges(); var cities = _db.ComplexDocuments.AsQueryable() .Select(x => x.ShippingAddress.City.Name) .ToList(); cities.Count().ShouldBe(1); cities[0].ShouldBe("New York"); } [Fact] public void Select_Anonymous_Complex() { ZB.MOM.WW.CBDD.Shared.TestDbContext_TestDbContext_Mappers.ZB_MOM_WW_CBDD_Shared_CityMapper cityMapper = new ZB.MOM.WW.CBDD.Shared.TestDbContext_TestDbContext_Mappers.ZB_MOM_WW_CBDD_Shared_CityMapper(); var doc = new ComplexDocument { Id = ObjectId.NewObjectId(), Title = "Order1", ShippingAddress = new Address { City = new City { Name = "New York" }, Street = "5th Ave" } }; _db.ComplexDocuments.Insert(doc); _db.SaveChanges(); var result = _db.ComplexDocuments.AsQueryable() .Select(x => new { x.Title, x.ShippingAddress.City }) .ToList(); result.Count().ShouldBe(1); result[0].Title.ShouldBe("Order1"); result[0].City.Name.ShouldBe("New York"); } [Fact] public void Select_Project_Nested_Array_Of_Objects() { var doc = new ComplexDocument { Id = ObjectId.NewObjectId(), Title = "Order with Items", ShippingAddress = new Address { City = new City { Name = "Los Angeles" }, Street = "Hollywood Blvd" }, Items = new List { new OrderItem { Name = "Laptop", Price = 1500 }, new OrderItem { Name = "Mouse", Price = 25 }, new OrderItem { Name = "Keyboard", Price = 75 } } }; _db.ComplexDocuments.Insert(doc); _db.SaveChanges(); // Retrieve the full document and verify Items array var retrieved = _db.ComplexDocuments.FindAll().First(); retrieved.Title.ShouldBe("Order with Items"); retrieved.ShippingAddress.City.Name.ShouldBe("Los Angeles"); retrieved.ShippingAddress.Street.ShouldBe("Hollywood Blvd"); // Verify array of nested objects retrieved.Items.Count.ShouldBe(3); retrieved.Items[0].Name.ShouldBe("Laptop"); retrieved.Items[0].Price.ShouldBe(1500); retrieved.Items[1].Name.ShouldBe("Mouse"); retrieved.Items[1].Price.ShouldBe(25); retrieved.Items[2].Name.ShouldBe("Keyboard"); retrieved.Items[2].Price.ShouldBe(75); } } }