RavenDB - Indexing and querying complex hierarhical data (nested properties) - c#

I have a docuemnt like this:
public class Order
{
public string ClientName { get; set; }
public List<OrderItem> Items { get; set; }
}
public class OrderItem
{
public string ProductCode { get; set; }
public int Quantity { get; set; }
}
And I need to query collection of theese documents like this:
var q = session.Query<Order_Index.Result, Order_Index>()
.Where(o => o.ClientName.StartsWith("Jho") &&
o.Items.Any(i => i.ProductCode == "Book" && i.Quantity >= 10))
.OfType<Order>();
Every example of indexing and querying hierarhical data, that I found, shows only case, when only one property of complex nested object is used in separate query expression. e.g.:
var q = session.Query<Order_Index.Result, Order_Index>()
.Where(o => o.ClientName.StartsWith("Jho") &&
o.ItemProductCodes.Any(c => c == "Book") &&
o.ItemQuantities.Any(qty => qty >= 10))
but none of them consider my situation.
I tried to index ProductCode and Quantity as separate collections and then join them by collection index while querying, but this throws some kind of Linq translation exception. I wonder, is there any opportunity to do this kind of querying in RavenDB?

Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<Order> session = new List<Order>() {
new Order() { ClientName = "Jho", Items = new List<OrderItem>() {
new OrderItem() { ProductCode = "abc", Quantity = 1},
new OrderItem() { ProductCode = "def", Quantity = 4},
new OrderItem() { ProductCode = "Book", Quantity = 5},
new OrderItem() { ProductCode = "jkl", Quantity = 10}
}
},
new Order() { ClientName = "Mary", Items = new List<OrderItem>() {
new OrderItem() { ProductCode = "mno", Quantity = 2},
new OrderItem() { ProductCode = "pqr", Quantity = 3},
new OrderItem() { ProductCode = "stu", Quantity = 4},
new OrderItem() { ProductCode = "vwx", Quantity = 5}
}
},
new Order() { ClientName = "Jho", Items = new List<OrderItem>() {
new OrderItem() { ProductCode = "abc", Quantity = 28},
new OrderItem() { ProductCode = "cdf", Quantity = 7},
new OrderItem() { ProductCode = "Book", Quantity = 26},
new OrderItem() { ProductCode = "jkl", Quantity = 5}
}
}
};
var q = session.Where(o => o.ClientName.StartsWith("Jho") && o.Items.Where(i => i.ProductCode == "Book" && i.Quantity >= 10).Any()).ToList();
}
}
public class Order
{
public string ClientName { get; set; }
public List<OrderItem> Items { get; set; }
}
public class OrderItem
{
public string ProductCode { get; set; }
public int Quantity { get; set; }
}
}

Related

How to use access all rows of type List<T> in C# .NET Framework?

I have a Model called SalesItemModel which has properties such as SaleItemId, SalesID, ProductID, Quantity, and UnitPrice. I pull list from database using SalesItemService.ListSalesItemsDataBySaleID(salesID).
public class SalesItemModel
{
public int SalesItemID { get; set; }
public int SalesID { get; set; }
public int ProductID { get; set; }
public string ProductName { get; set; }
public Nullable<decimal> UnitPrice { get; set; }
public double Quantity { get; set; }
}
Pulling Sales Items:
var salesItem = SalesItemService.ListSalesItemsDataBySaleID(SalesID);
And I want to access quantity and product ID of every row of the returned list to edit data on stock table using those data.
If you just want to access the data in list, you can try the below loop.
foreach(var item in salesItem)
{
item.Quantity = 10;
item.ProductId = 20;
}
var tests = new List<Test>
{
new Test{Name = "Abc", Age = 1},
new Test{Name = "Abc2", Age = 12},
new Test{Name = "Abc3", Age = 13},
new Test{Name = "Abc4", Age = 14},
};
tests = tests.Select(x=>
{
x.Age = 2;
return x;
}).ToList();
Example which you can use.

How to convert SQL subquery to Linq

I have two tables, Employees and Transactions.
First table: dbo.Employees
Second table: dbo.Transactions
I need to convert the below query to a lambda expression (Linq):
SELECT Employees.EmployeeId, Trans.LastTrans
FROM dbo.Employees
INNER JOIN
(SELECT EmployeeId, MAX(TrasactionDate) LastTrans
FROM dbo.Transactions
GROUP BY EmployeeId) Trans ON Trans.EmployeeId = Employees.EmployeeId
I want the result employeeId and LastTrans as shown below:
See following. The query does not need two Select in linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Context db = new Context()
{
Employees = new List<Employees>() {
new Employees() { EmployeeId = 1, Name = "Hossam"},
new Employees() { EmployeeId = 2, Name = "Peter"},
new Employees() { EmployeeId = 3, Name = "George"}
},
Transactions = new List<Transactions>() {
new Transactions() { TranactionId = 1, amount = 200, TransactionDate = DateTime.Parse("2020-01-02"), EmployeeId = 1},
new Transactions() { TranactionId = 2, amount = 300, TransactionDate = DateTime.Parse("2020-02-02"), EmployeeId = 1},
new Transactions() { TranactionId = 3, amount = 1000, TransactionDate = DateTime.Parse("2020-02-25"), EmployeeId = 1},
new Transactions() { TranactionId = 4, amount = 5000, TransactionDate = DateTime.Parse("2020-04-01"), EmployeeId = 2},
new Transactions() { TranactionId = 5, amount = 7000, TransactionDate = DateTime.Parse("2020-07-16"), EmployeeId = 2},
new Transactions() { TranactionId = 6, amount = 8000, TransactionDate = DateTime.Parse("2020-08-06"), EmployeeId = 3},
new Transactions() { TranactionId = 6, amount = 600, TransactionDate = DateTime.Parse("2020-09-04"), EmployeeId = 3}
}
};
var results = (from trans in db.Transactions
join emp in db.Employees on trans.EmployeeId equals emp.EmployeeId
select new { EmployeeId = trans.EmployeeId, LastTrans = trans.TransactionDate }
).GroupBy(x => x.EmployeeId)
.Select(x => x.OrderByDescending(y => y.LastTrans))
.Select(x => x.First())
.OrderBy(x => x.EmployeeId)
.ToList();
}
}
public class Context
{
public List<Employees> Employees { get; set; }
public List<Transactions> Transactions { get; set; }
}
public class Employees
{
public int EmployeeId { get; set; }
public string Name { get; set; }
}
public class Transactions
{
public int TranactionId { get; set; }
public decimal amount { get; set; }
public DateTime TransactionDate { get; set; }
public int EmployeeId { get; set; }
}
}
This query is enough to get your result and you do not need additional join to Employees
var grouped =
from trans in db.Transactions
group trans by trans.EmployeeId into g
select new
{
EmployeeId = g.Key,
LastTrans = g.Max(x => x.TrasactionDate)
};

How to make nested list from one base list

I would like to make nested list from this one list
public class Product
{
string Id;
string ProductName;
decimal Price;
string Supplier;
int Quantity;
string VersionId;
string TypeId;
}
public class ProductTypeDto
{
public string TypeId { get; set; }
public string ProductName { get; set; }
public string Price { get; set; }
public List<ProductVersionDto> ProductVersions { get; set; }
}
public class ProductVersionDto
{
public string VersionId { get; set; }
public string ProductName { get; set; }
public string Supplier { get; set; }
public int Quantity { get; set; }
}
I would like to know how I can create a list of ProductTypeDto using linq c# .
I need to get all products with the same TypeId , and ProductVersions field should contain products with same version Id (and of course same TypeId).
I don't want to use foreach loops, and I dont want to loop over products twice to make this nested list.. I think there would be a better way using Linq, would be great if you can help..
Edit: I add here what I have done so far, but it is not the way I want this to be fixed.
List<ProductTypeDto> products = this._dbContext
.Products
.Where(product => product.TypeId == query.TypeId)
.Select(product => new ProductTypeDto()
{
TypeId = product.TypeId,
ProductName = product.ProductName,
Price = product.Price,
ProductVersions = product.Products.Where(p => p.TypeId == product.TypeId)
.Select(p => new ProductVersionDto()
{
VersionId = p.VersionId,
ProductName = p.ProductName,
Supplier = p.Supplier,
Quantity = p.Quantity
}).ToList()
})
.ProjectTo<ProductTypeDto>(this._config)
.ToListAsync(cancellationToken);
This is the result I want to get:
var product1 = new Product() { Id = 1, ProductName = "foo", Price = 20, Supplier = "test1", Quantity = 3, VersionId = "1", TypeId = "1" };
var product2 = new Product() { Id = 2, ProductName = "foo1", Price = 60, Supplier = "test2", Quantity = 9, VersionId = "1", TypeId = "1" };
var product3 = new Product() { Id = 3, ProductName = "foo2", Price = 30, Supplier = "test3", Quantity = 5, VersionId = "2", TypeId = "1" };
var product4 = new Product() { Id = 4, ProductName = "foo3", Price = 10, Supplier = "test4", Quantity = 4, VersionId = "1", TypeId = "2" };
var product5 = new Product() { Id = 5, ProductName = "foo4", Price = 50, Supplier = "test5", Quantity = 8, VersionId = "1", TypeId = "3" };
List<ProductVersionDto> p1 = {
new ProductVersionDto { ProductName = "foo", Quantity= 3, Supplier ="test1"}
new ProductVersionDto { ProductName = "foo1", Quantity= 9, Supplier ="test2"}
};
List<ProductVersionDto> p2 = {
new ProductVersionDto { ProductName = "foo3", Quantity= 4, Supplier ="test4"}
};
List<ProductVersionDto> p3 = {
new ProductVersionDto { ProductName = "foo4", Quantity= 8, Supplier ="test5"}
};
List<ProductTypeDto> products = {
new ProductTypeDto{ ProductName = "foo", Price =20, ProductVersions = p1}
new ProductTypeDto{ ProductName = "foo1", Price =60, ProductVersions = p1}
new ProductTypeDto{ ProductName = "foo3", Price =10, ProductVersions = p2}
new ProductTypeDto{ ProductName = "foo4", Price =50, ProductVersions = p3}
}
Try this:
var dtos =
(
from p in products
group new ProductVersionDto()
{
VersionId = p.VersionId,
ProductName = p.ProductName,
Supplier = p.Supplier,
Quantity = p.Quantity
} by new { p.TypeId, p.ProductName, p.Price } into g
select new ProductTypeDto()
{
TypeId = g.Key.TypeId,
ProductName = g.Key.TypeId,
Price = g.Key.Price,
ProductVersions = g.ToList(),
}
).ToList();
It appears that you meant Price to be a decimal in ProductTypeDto, BTW.

Convert flat db row data to nested typed objects linq

I'm getting the results of a sql outer join as flat results in an IEnumerable, and would like to convert them to nested typed objects in linq. From something like this:
[{id: 1, industryId: 1}, {id:1, industryId: 2}, {id:2, industryId: 1} etc..]
to something like this:
list of Company [{id: 1, list of Industry{industryId: 1, 2}, {id: 2, list of Industry{industryId: 1}}]
I'm currently trying a solution with GroupBy:
Companies = flatDbRows
.GroupBy(
row => row.CompanyId,
(key, value) => new CompanyModel
{
CompanyId = value.First().CompanyId,
CompanyName = value.First().CompanyName,
Industries = value
.GroupBy(
row => new { row.IndustryId, row.Industry },
(k, v) => new IndustryModel() { IndustryId = k.IndustryId, Name = k.Industry }
)
.Where(x => x.IndustryId != 0)
.ToList(),
}).ToList();
}
but it doesn't feel great, especially with all the value.First() I'm using to get the values that only belong to each grouped company. Is there something more appropriate? Group join sounded more like what I wanted, but I'm having trouble understanding how to apply it to a single list. I'm open to using query syntax instead of the lambdas if that's easier.
I'm trying to go from this model (where company-related info will be duplicated for each outer joined industry result):
public class CompanyFlatDbRowsModel
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public int IndustryId{ get; set; }
public string Industry { get; set; }
}
to this:
public class CompanyModel
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public IEnumerable<IndustryModel> Industries { get; set; }
}
// FULL edit after providing your models
public class TestClass
{
public class CompanyModel
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public List<IndustryModel> Industires { get; set; }
}
public class IndustryModel
{
public int IndustryId { get; set; }
public string IndustryName { get; set; }
}
public class CompanyFlatDbRowsModel
{
public CompanyFlatDbRowsModel()
{
}
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public int IndustryId { get; set; }
public string Industry { get; set; }
}
[Fact]
public void Test()
{
var data = new List<CompanyFlatDbRowsModel>
{
new CompanyFlatDbRowsModel
{
CompanyId = 1,
CompanyName = "Company 1",
IndustryId = 1,
Industry = "Industry 1"
},
new CompanyFlatDbRowsModel
{
CompanyId = 1,
CompanyName = "Company 1",
IndustryId = 2,
Industry = "Industry 2"
},
new CompanyFlatDbRowsModel
{
CompanyId = 2,
CompanyName = "Company 2",
IndustryId = 3,
Industry = "Industry 3"
},
new CompanyFlatDbRowsModel
{
CompanyId = 2,
CompanyName = "Company 2",
IndustryId = 4,
Industry = "Industry 4"
},
};
var result = data.GroupBy(x => x.CompanyId)
.Select(x => new CompanyModel()
{
CompanyId = x.Key,
CompanyName = x.First().CompanyName,
Industires = x.Select(y=> new IndustryModel
{
IndustryName = y.Industry,
IndustryId = y.IndustryId
}).ToList()
}).ToList();
foreach (var item in result)
{
var text = $"Company id : {item.CompanyId}, industries : {string.Join(',',item.Industires.Select(x=>$"(name: {x.IndustryName}, id: {x.IndustryId})"))}";
Debug.WriteLine(text);
}
}
}
output:
Company id : 1, industries : (name: Industry 1, id: 1),(name: Industry 2, id: 2)
Company id : 2, industries : (name: Industry 3, id: 3),(name: Industry 4, id: 4)
edit:
alternatively you can do as below, however the "first" thing still occurs somewhere, I have tried also the GroupJoin but it doesn't really help in that case.
var otherResult = data.Select(x =>
new CompanyModel
{
CompanyId = x.CompanyId,
CompanyName = x.CompanyName,
Industires = data
.Where(y => y.CompanyId == x.CompanyId)
.Select(y => new IndustryModel
{
IndustryId = y.IndustryId,
IndustryName = y.Industry
}).ToList()
})
.GroupBy(y => y.CompanyId)
.Select(x => x.First())
.ToList();
edit:
one more approach without using "first"
var anotherResult = data.GroupBy(x => x.CompanyId)
.Select(x =>
{
var companyModel = new CompanyModel()
{
CompanyId = x.Key
};
companyModel.Industires = x.Select(y =>
{
companyModel.CompanyName = y.CompanyName; // assignign here occurs multiple times however with the same value
return new IndustryModel
{
IndustryId = y.IndustryId,
IndustryName = y.Industry
};
}).ToList();
return companyModel;
}).ToList();

how to get count Of Product according Store

I have three Table : Product and StoreDetail , Store
Store Table holds storeName .
I want get count of product according storeName , to do this I use belowe code :
var stocksQuery = storeDetails.GroupBy(row => new { row.StoreId, row.ProductId }).AsQueryable();
List<StockStatusViewModel> result = new List<StockStatusViewModel>();
foreach (var item in stocksQuery)
{
result.Add(new StockStatusViewModel
{
Quantity = item.Sum(row => row.Quantity),
ProductCombinationId = item.Key.ProductAttributeCombinationId,
StoreId = item.Key.StoreId,
// here I need productName and StoreName
});
}
but I need to storeName and ProductName , how can I get these ?
here Is my classes:
public class StoreDetail
{
public Product Product{ get; set; }
public Guid ProductId { get; set; }
}
public class Product{
public ICollection<StoreDetail> StoreDetails { get; set; }
}
Can you try some thing like this, instead of group by StoredId and ProductId, I will group by Store and Product
var stocksQuery = storeDetails.GroupBy(row => new { row.Store, row.Product }).AsQueryable();
List<StockStatusViewModel> result = new List<StockStatusViewModel>();
foreach (var item in stocksQuery)
{
result.Add(new StockStatusViewModel
{
Quantity = item.Sum(row => row.Quantity),
ProductCombinationId = item.Key.ProductAttributeCombinationId,
StoreId = item.Key.StoreId,
StoreName = item.Key.Store.StoreName,
ProductName = item.Key.Product.ProductName
});
}
For better performance, I think we just select what we need so we can change the code to
var stocksQuery = storeDetails.GroupBy(row => new { row.StoreId, row.Store.StoreName, row.ProductId, row.Product.ProductName }).AsQueryable();
List<StockStatusViewModel> result = new List<StockStatusViewModel>();
foreach (var item in stocksQuery)
{
result.Add(new StockStatusViewModel
{
Quantity = item.Sum(row => row.Quantity),
ProductCombinationId = item.Key.ProductAttributeCombinationId,
StoreId = item.Key.StoreId,
StoreName = item.Key.StoreName,
ProductName = item.Key.ProductName
});
}

Categories

Resources