Why do I get this error in a RavenDB index query: The field 'CustomerId' is not indexed - indexing

This is my index code:
public class InvoiceSummaryView
{
public DateTime DueDate { get; set; }
public string CompanyAddress { get; set; }
public string DebtorName { get; set; }
public float Amount { get; set; }
public bool IsPaid { get; set; }
public string CustomerId { get; set; }
}
public class InvoiceSummaryIndex : AbstractIndexCreationTask<CustomerInvoice>
{
public InvoiceSummaryIndex()
{
Map = invoices => from invoice in invoices
select new { DueDate = invoice.DueDate, DebtorId = invoice.DebtorId, Amount = invoice.Amount };
TransformResults = (database, results) =>
from invoice in results
let debtor = database.Load<Debtor>(invoice.DebtorId)
let company = database.Load<Company>(debtor.CompanyId)
select new {
DueDate = invoice.DueDate,
CompanyAddress = Company.Address.ToString(),
DebtorName = debtor.Contact.First + " " + debtor.Contact.Last,
Amount = invoice.Amount,
IsPaid = invoice.IsPaid,
CustomerId = Company.CustomerId
};
}
}
And this is my query:
var query = from viewItem in session.Query<InvoiceSummaryView>("InvoiceSummaryIndex")
where viewItem.CustomerId == id
orderby viewItem.DueDate
select viewItem;
the error is:
"Error": "System.ArgumentException: The field 'CustomerId' is not indexed,
cannot query on fields that are not indexed at ...

Look at your index:
select new { DueDate = invoice.DueDate, DebtorId = invoice.DebtorId, Amount = invoice.Amount };
The fields that you have indexed are DueDate, DebtorId and Amount, that is it.
If you'll it it like this:
select new { DueDate = invoice.DueDate, DebtorId = invoice.DebtorId, Amount = invoice.Amount, invoice.CustomerId };
It will work

Related

how to use in clause in Linq Query and pass it dynamically from code

I am converting my project to EF Core in my old project I have a query running.
IDictionary<int, IGrouping<int, UserPurchaseItemAddonWithAmount>> addons =
context.Fetch<UserPurchaseItemAddonWithAmount>($"Select UPIA.*, EA.Amount From UserPurchaseItemAddons UPIA Inner Join ExtraAddons EA on UPIA.AddonID = EA.AddonID Where UPIA.UserPurchaseItemID in ({string.Join(',', userPurchaseItems.Select(S => S.UserPurchaseItemID))})")
.GroupBy(G => G.UserPurchaseItemID).ToDictionary(D => D.Key);
I need to convert this query in to Linq query what I am doing is below
IDictionary<int, IGrouping<int, UserPurchaseItemAddonWithAmount>> addons =
(from f in context.UserPurchaseItemAddons
join s in context.ExtraAddons
on f.AddonId equals s.AddonId
select new
{
Amount = s.Amount,
UserPurchaseItemAddonID = f.UserPurchaseItemAddonId,
UserPurchaseItemID = f.UserPurchaseItemId,
BranchItemVariantID = f.BranchItemVariantId,
AddonID = f.AddonId,
UserID = f.UserId,
IsDeleted = f.IsDeleted,
ModifiedOn = f.ModifiedOn,
ModifiedBy = f.ModifiedBy,
Reason = f.Reason,
}).GroupBy(G => G.UserPurchaseItemID).ToDictionary(D => D.Key);
This query is causing a compiler error related to casting to IGrouping<int, UserPurchaseItemAddonWithAmount> to an anonymous type. The other thing is that how can I apply in clause in where condition in above query, just like the first query .
class
public class UserPurchaseItemAddonWithAmount
{
public decimal Amount { get; set; }
public int UserPurchaseItemAddonID { get; set; }
public int UserPurchaseItemID { get; set; }
public int BranchItemVariantID { get; set; }
public int AddonID { get; set; }
public int UserID { get; set; }
public bool IsDeleted { get; set; }
public DateTime? ModifiedOn { get; set; }
public int? ModifiedBy { get; set; }
public string? Reason { get; set; }
}
Try the following query. Main mistake that you have returned anonymous class.
var purchaseItemIds = userPurchaseItems.Select(S => S.UserPurchaseItemID);
IDictionary<int, IGrouping<int, UserPurchaseItemAddonWithAmount>> addons =
(from f in context.UserPurchaseItemAddons
join s in context.ExtraAddons on f.AddonId equals s.AddonId
where purchaseItemIds.Contains(f.UserPurchaseItemID)
select new UserPurchaseItemAddonWithAmount
{
Amount = s.Amount,
UserPurchaseItemAddonID = f.UserPurchaseItemAddonId,
UserPurchaseItemID = f.UserPurchaseItemId,
BranchItemVariantID = f.BranchItemVariantId,
AddonID = f.AddonId,
UserID = f.UserId,
IsDeleted = f.IsDeleted,
ModifiedOn = f.ModifiedOn,
ModifiedBy = f.ModifiedBy,
Reason = f.Reason,
})
.AsEnumerable()
.GroupBy(G => G.UserPurchaseItemID)
.ToDictionary(D => D.Key);

LoadDocument by query in RavenDB index

I have the following structure:
public class Order
{
public string Id { get; set; }
public string CustomerId { get; set; }
public decimal Amount { get; set; }
public DateTimeOffset CreatedDate { get; set; }
}
public class Customer
{
public string Id { get; set; }
public string Name { get; set; }
}
I want to export all customers (RavenDB Stream) with order turnover and last ordered date.
I do already have an index (Customers_ByTurnover) which outputs this data (map=Orders, reduce by CustomerId). Although this does only list customers which have already ordered something.
I need an index for all Customers and load these details into each row.
Here is the code I want to write (the Query method is pseudo and doesn't really exist):
public class Customers_ByOrders : AbstractIndexCreationTask<Customer, Customers_ByOrders.Result>
{
public class Result
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Turnover { get; set; }
public DateTimeOffset? LastOrderedDate { get; set; }
}
public Customers_ByOrders()
{
Map = items => items.Select(item => new Result()
{
Id = item.Id,
Name = item.Name,
Turnover = Query<Order>().Where(x => x.CustomerId == item.Id).Sum(x => x.Amount),
LastOrderedDate = Query<Order>().Where(x => x.CustomerId == item.Id).Select(x => x.CreatedDate).OrderByDescending(x => x).FirstOrDefault()
});
}
}
How can I solve this issue?
You cannot create a query inside an index, to get the desired info you will have to create a map-reduce index on the Orders collection, group by on CustomerId and in the reduce function apply Sum() on Amount field and order the LastOrderedDate. To get the Name name field you will have to use LoadDocument extension.
public class Customers_ByOrders : AbstractIndexCreationTask<Orders, Customers_ByOrders.Result>
{
public class Result
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Turnover { get; set; }
public DateTimeOffset? LastOrderedDate { get; set; }
}
public Customers_ByOrders()
{
Map = orders => from o in orders
select new Result
{
Id = o.CustomerId,
Turnover = o.Amount,
LastOrderedDate = o.CreatedDate
};
Reduce = results => from result in results
group result by result.Id
into g
select new Result
{
Id = g.Key,
Turnover = g.Sum(x => x.Turnover),
LastOrderedDate = g.OrderByDescending(x => x.LastOrderedDate).Select(x => x.LastOrderedDate).FirstOrDefault()
};
}
}

How to create a search index by related document field in RavenDb?

I think my scenario is a common scenario in any application. I will exemplify using an common used domain: Products and Orders with Items (of products).
What I am trying to do is search orders by the product name, considering that the order has a list of itens and each item has a related productId.
I did some searches and read the Raven documentation, but I was unable to find the answer to my problem.
Please consider the code below:
public class Product
{
public string Id { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
}
public class Order
{
public string Id { get; set; }
public string OrderNumber { get; set; }
public decimal Total { get; set; }
public string Customer { get; set; }
public Item[] Items { get; set; }
}
public class Item
{
public string ProductId { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
public class Order_ByProductName : AbstractMultiMapIndexCreationTask<Order_ByProductName.Result>
{
public class Result
{
public string ProductId { get; set; }
public string ProductName { get; set; }
public string[] OrdersIds { get; set; }
public string[] OrdersNumbers { get; set; }
}
public Order_ByProductName()
{
AddMap<Product>(products => from product in products
select new
{
ProductId = product.Id,
ProductName = product.ProductName,
OrderId = default(string),
OrderNumber = default(string)
});
AddMap<Order>(orders => from order in orders
group order by order.Items.Select(c => c.ProductId)
into g
select new
{
ProductId = g.Key,
ProductName = default(string),
OrdersIds = g.Select(c => c.Id),
OrdersNumbers = g.Select(c => c.OrderNumber)
});
Reduce = results => from result in results
group result by result.ProductId
into g
select new
{
ProductId = g.Key,
ProductName = g.Select(r => r.ProductName).Where(t => t != null).First(),
OrdersIds = g.Where(r => r.OrdersIds != null).SelectMany(r => r.OrdersIds),
OrdersNumbers = g.Where(r => r.OrdersNumbers != null).SelectMany(r => r.OrdersNumbers)
};
Sort("ProductName", SortOptions.String);
Index(x => x.ProductName, FieldIndexing.Analyzed);
}
}
class Program
{
static void Main(string[] args)
{
var documentStore = new DocumentStore
{
Url = "http://localhost:8080",
DefaultDatabase = "MyDatabase"
};
documentStore.Initialize();
new Order_ByProductName().Execute(documentStore);
using (var session = documentStore.OpenSession())
{
var product1 = new Product() { Price = 100, ProductName = "Phone" };
var product2 = new Product() { Price = 1000, ProductName = "Laptop" };
var product3 = new Product() { Price = 200, ProductName = "Windows Phone" };
session.Store(product1);
session.Store(product2);
session.Store(product3);
session.SaveChanges();
}
using (var session = documentStore.OpenSession())
{
var products = session.Query<Product>().ToList();
var order1 = new Order();
order1.Customer = "Jhon Doe";
order1.OrderNumber = "001";
order1.Items = new Item[] {
new Item { ProductId = products[0].Id, Price = products[0].Price, Quantity = 1 },
new Item { ProductId = products[1].Id, Price = products[1].Price, Quantity = 1 }
};
order1.Total = order1.Items.Sum(c => (c.Quantity * c.Price));
var order2 = new Order();
order2.Customer = "Joan Doe";
order2.OrderNumber = "002";
order2.Items = new Item[] {
new Item { ProductId = products[2].Id, Price = products[2].Price, Quantity = 1 }
};
order2.Total = order2.Items.Sum(c => (c.Quantity * c.Price));
session.Store(order1);
session.Store(order2);
session.SaveChanges();
}
using (var session = documentStore.OpenSession())
{
var results = session
.Query<Order_ByProductName.Result, Order_ByProductName>()
.Where(x => x.ProductName == "Phone")
.ToList();
foreach (var item in results)
{
Console.WriteLine($"{item.ProductName}\t{string.Join(", ", item.OrdersNumbers)}");
}
Console.ReadKey();
}
}
}
I found a related issue here How to index related documents in reverse direction in Ravendb, but this scenario is a bit different. The code above runs if you comment the index creation, but thorws an exception otherwise: "Could not understand query: Variable initializer must be a select query expression".
I need somthing like below SQL but in RavenDb:
select pro.ProductName, ord.OrderNumber from orders ord
inner join items itm on itm.OrderId = ord.Id
inner join products pro on pro.Id = itm.ProductId
where pro.ProductName like '%Phone%';
How to create a search by ProductName and return a list or Orders in RavenDb?
Im using version 3.5(.1)
Consider the main problem as search "entities by related entity property".
Thanks in advance!
Consider storing the product name directly in the order items. That will have two advantages - the order items won't be affected by later product changes, and indexing will be cheaper.
The following code demonstrates both approaches:
public class Product
{
public string Id { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
}
public class Order
{
public string Id { get; set; }
public string OrderNumber { get; set; }
public decimal Total { get; set; }
public string Customer { get; set; }
public Item[] Items { get; set; }
}
public class Item
{
public string ProductId { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
public class OrderView
{
public string OrderId { get; set; }
public string OrderNumber { get; set; }
public string[] ProductIds { get; set; }
public string[] ProductNames { get; set; }
}
public class Order_ByItemName : AbstractIndexCreationTask<Order, OrderView>
{
public Order_ByItemName()
{
Map = orders => from order in orders
select new
{
OrderId = order.Id,
OrderNumber = order.OrderNumber,
ProductIds = order.Items.Select(x => x.ProductId).ToArray(),
ProductNames = order.Items.Select(x => x.ProductName).ToArray(),
};
Index(x => x.ProductNames, FieldIndexing.Analyzed);
StoreAllFields(FieldStorage.Yes);
}
}
public class Order_ByProductName : AbstractIndexCreationTask<Order, OrderView>
{
public Order_ByProductName()
{
Map = orders => from order in orders
let products = LoadDocument<Product>(order.Items.Select(x => x.ProductId))
select new
{
OrderId = order.Id,
OrderNumber = order.OrderNumber,
ProductIds = products.Select(x => x.Id).ToArray(),
ProductNames = products.Select(x => x.ProductName).ToArray(),
};
Index(x => x.ProductNames, FieldIndexing.Analyzed);
StoreAllFields(FieldStorage.Yes);
}
}
class Program
{
static void Main(string[] args)
{
var documentStore = new DocumentStore
{
Url = "http://localhost:8080",
DefaultDatabase = "MyDatabase"
};
documentStore.Initialize();
new Order_ByProductName().Execute(documentStore);
new Order_ByItemName().Execute(documentStore);
using (var session = documentStore.OpenSession())
{
var product1 = new Product() { Id = "products/1", Price = 100, ProductName = "Phone" };
var product2 = new Product() { Id = "products/2", Price = 1000, ProductName = "Laptop" };
var product3 = new Product() { Id = "products/3", Price = 200, ProductName = "Windows Phone" };
session.Store(product1);
session.Store(product2);
session.Store(product3);
session.SaveChanges();
}
using (var session = documentStore.OpenSession())
{
var products = session.Query<Product>().ToList();
var order1 = new Order();
order1.Id = "orders/1";
order1.Customer = "Jhon Doe";
order1.OrderNumber = "001";
order1.Items = new Item[] {
new Item { ProductId = products[0].Id, ProductName = products[0].ProductName, Price = products[0].Price, Quantity = 1 },
new Item { ProductId = products[1].Id, ProductName = products[1].ProductName, Price = products[1].Price, Quantity = 1 }
};
order1.Total = order1.Items.Sum(c => (c.Quantity * c.Price));
var order2 = new Order();
order1.Id = "orders/1";
order2.Customer = "Joan Doe";
order2.OrderNumber = "002";
order2.Items = new Item[] {
new Item { ProductId = products[2].Id, ProductName = products[2].ProductName, Price = products[2].Price, Quantity = 1 }
};
order2.Total = order2.Items.Sum(c => (c.Quantity * c.Price));
session.Store(order1);
session.Store(order2);
session.SaveChanges();
}
Thread.Sleep(5000); // wait for indexing
using (var session = documentStore.OpenSession())
{
var itemResults = session
.Query<OrderView, Order_ByItemName>()
.Search(x => x.ProductNames, "Phone")
.ProjectFromIndexFieldsInto<OrderView>()
.ToList();
var results = session
.Query<OrderView, Order_ByProductName>()
.Search(x => x.ProductNames, "Phone")
.ProjectFromIndexFieldsInto<OrderView>()
.ToList();
Console.WriteLine("Order_ByItemName");
foreach (var order in itemResults)
{
Console.WriteLine($"OrderNumber: {order.OrderNumber}");
for (int i = 0; i < order.ProductIds.Length; i++)
{
Console.WriteLine($"Item: {order.ProductIds[i]} - {order.ProductNames[i]}");
}
}
Console.WriteLine("Order_ByProductName");
foreach (var order in results)
{
Console.WriteLine($"OrderNumber: {order.OrderNumber}");
for (int i = 0; i < order.ProductIds.Length; i++)
{
Console.WriteLine($"Item: {order.ProductIds[i]} - {order.ProductNames[i]}");
}
}
Console.ReadKey();
}
}
}

SELECT all with children

I have the following classes:
public class Order
{
public Order()
{
OrderItems = new List<OrderItem>();
}
public int Id { get; set; }
public string OrderName { get; set; }
public int OrderStatusId { get; set; }
public OrderStatus Status { get; set; }
public List<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public int Id { get; set; }
public int OrderId { get; set; }
public string ProductName { get; set; }
}
public class OrderStatus
{
public int Id { get; set; }
public string Description { get; set; }
}
For returning a single order I use this method (any comment on it would also be helpful):
Order GetOrder()
{
using (var con = GetConnection())
{
try
{
con.Open();
string query =
#"SELECT * FROM [Order] JOIN OrderStatus ON [Order].OrderStatusId = OrderStatus.Id WHERE [Order].Id =1;
SELECT * FROM OrderItem Where OrderId = 1";
using (var multi = con.QueryMultiple(query))
{
Order order = multi.Read<Order, OrderStatus, Order>
((ord, ordStat) => { ord.Status = ordStat; return ord; })
.FirstOrDefault();
order.OrderItems = multi.Read<OrderItem>().ToList();
return order;
}
}
catch (Exception ex)
{
return null;
}
}
}
I know I can select all of my orders Id values into a collection on a separate query and run the above method in a foreach loop using the collection values as a parameter but I wonder if dapper has a more elegant solution for returning all of my orders instead of just one.
It sounds like you have a list or array of the ids; dapper supports parameter expansion, allowing use with in, for example:
int[] ids = ...
string query =
#"SELECT * FROM [Order]
JOIN OrderStatus ON [Order].OrderStatusId = OrderStatus.Id
WHERE [Order].Id in #ids;
SELECT * FROM OrderItem Where OrderId in #ids";
using (var multi = con.QueryMultiple(query, new { ids }))
{
var orders = multi.Read<Order, OrderStatus, Order>
((ord, ordStat) => { ord.Status = ordStat; return ord; })
.AsList();
var orderItems = multi.Read<OrderItem>().ToLookup(x => x.OrderId);
foreach(var order in orders) // pick out each order's items
order.OrderItems = orderItems[order.Id].ToList();
}
Any use?

Raven Db Querying

I am using Map Reduce index for query. And I want to select filter by licenceId/licenceIds which may be one or many separated by comma entered in text box
e.g.
L1 (select having LicenseId only L1)
L1,L2 (select having LicenseId only L1 OR L2)
L2,L3,L5 (select having LicenseId only L3 OR L3 OR L5)
Here is result document:
public class GrossSalesByRevenueClass
{
public string LicenseId { get; set; }
public string RevClass { get; set; }
public decimal GrossSales { get; set; }
public decimal NetSales { get; set; }
public int Quantity { get; set; }
public bool NonSales { get; set; }
public DateTime Day { get; set; }
public string DayName { get; set; }
public int Month { get; set; }
public int Quarter { get; set; }
public int Year { get; set; }
}
Index is:
public class IdxGrossSalesByRevenueClassByDay : AbstractIndexCreationTask<Ticket, GrossSalesByRevenueClass>
{
public IdxGrossSalesByRevenueClassByDay()
{
Map = docs => from doc in docs
from c in doc.Coversfrom t in c.TicketItems
select new
{
LicenseId = doc.LicenseId,
RevClass = t.RevenueClass,
GrossSales = t.TicketItemGross,
NetSales = t.NetPrice,
Quantity = t.Quantity,
NonSales = t.IsNonSales,
Day = doc.TicketDate.Date,
DayName = doc.TicketDate.ToString("ddd")
};
Reduce = result => from r in result
group r by new { r.NonSales, r.RevClass, r.Day, r.LicenseId } into g
select new
{
LicenseId = g.Key.LicenseId,
RevClass = g.Key.RevClass,
GrossSales = g.Sum(x => x.GrossSales),
NetSales = g.Sum(x => x.NetSales),
Quantity = g.Sum(x => x.Quantity),
NonSales = g.Key.NonSales,
Day = g.Key.Day,
DayName = g.Select(x => x.DayName).FirstOrDefault(),
};
}
}
And I am querying like following:
GrossSalesByRevenueClassCollection = session.Query<GrossSalesByRevenueClass, IdxGrossSalesByRevenueClassByDay>()
.TransformWith<GrossSalesByRevenueClassTransformer, GrossSalesByRevenueClass>()
.Where(x => x.Day >= d1 && x.Day <= d2 && (?????))
In place of (?????) there should be License id list (L1 OR L2)
I tried Contains() in where but not working for me please tell me how do I query an index
for such a requirement.
something like this
var licenses = userLicenses.Split(",");
GrossSalesByRevenueClassCollection = session.Query<GrossSalesByRevenueClass, IdxGrossSalesByRevenueClassByDay>()
.Where(x => x.Day >= d1 && x.Day <= d2 && x.LicenseId.In(licenses))