问题
I'm struggling with the behaviour of grouping using RavenDB and LuceneQuery.
I was always under the impression that IEnumerable was only evaluated when calling ToArray(), etc.
The following query was split into two parts for the sake of clarity.
I would not expect the query to be evaluated until after ToArray() is called on totalledBalanceList, my expectation being that the grouping is done on the server across all of the data. However, the actual result depends on the number of items stipulated in .Take(). Without the Take(1024), the results come back for the default 128 items.
I need to be able to perform the grouping across the entire dataset.
using (var session = MvcApplication.RavenSession)
{
var computedBalanceList =
from c in session.Advanced.LuceneQuery<Journal, Ledger_ByDateAndDebitIdAndCreditIdAndValues>()
.Where(parameters)
.OrderBy(c => c.DateAsString).Take(1024)
select new LedgerBalanceDto
{
Account = account,
Name = queryName,
Debits = c.DebitId == account.Id
? c.DebitValue
: 0,
Credits = (c.CreditId == account.Id)
? c.CreditValue
: 0,
Currency = (c.DebitId == account.Id) ? c.DebitCurrency : c.CreditCurrency,
CurrencySymbol = (c.DebitId == account.Id) ? c.DebitCurrencySymbol : c.CreditCurrencySymbol,
};
var totalledBalanceList =
from balance in computedBalanceList
group new {balance.Debits, balance.Credits} by new {balance.Currency, balance.CurrencySymbol}
into grouping
select new LedgerBalanceDto
{
Account = account,
Currency = grouping.Key.Currency,
CurrencySymbol = grouping.Key.CurrencySymbol,
Debits = grouping.Sum(c => c.Debits),
Credits = grouping.Sum(c => c.Credits),
Name = queryName
};
return totalledBalanceList;
And the index:
public class Ledger_ByDateAndDebitIdAndCreditIdAndValues:AbstractIndexCreationTask<Journal>
{
public Ledger_ByDateAndDebitIdAndCreditIdAndValues()
{
Map = journals => from c in journals
select new {c.Date,c.DateAsString, c.DebitId, c.CreditId,c.DebitValue,c.CreditValue};
Index(x=>x.Date,FieldIndexing.Analyzed);
Index(x=>x.DateAsString,FieldIndexing.Analyzed);
Index(x=>x.DebitId,FieldIndexing.Analyzed);
Index(x=>x.CreditId,FieldIndexing.Analyzed);
Index(x=>x.DebitValue,FieldIndexing.Analyzed);
Index(x=>x.CreditValue,FieldIndexing.Analyzed);
Sort(x=>x.DateAsString,SortOptions.String);
}
}
I've also rewritten the query so that the grouping happens "outside" the filter, but I get exactly the same results, namely the result depends on the Take().
var totalledBalanceList = from balance in
from c in query
.Where(parameters)
.OrderBy(c => c.DateAsString)
select new LedgerBalanceDto
{
Account = account,
Name = queryName,
Debits = c.DebitId == account.Id
? c.DebitValue
: 0,
Credits = (c.CreditId == account.Id)
? c.CreditValue
: 0,
Currency = (c.DebitId == account.Id) ? c.DebitCurrency : c.CreditCurrency,
CurrencySymbol = (c.DebitId == account.Id) ? c.DebitCurrencySymbol : c.CreditCurrencySymbol,
}
group new {balance.Debits, balance.Credits} by new {balance.Currency, balance.CurrencySymbol}
into grouping
select new LedgerBalanceDto
{
Account = account,
Currency = grouping.Key.Currency,
CurrencySymbol = grouping.Key.CurrencySymbol,
Debits = grouping.Sum(c => c.Debits),
Credits = grouping.Sum(c => c.Credits),
Name = queryName
};
return totalledBalanceList;
Any thoughts on this would be much appreciated.
Part of the Journal class:
public class Journal
{
public string Id { get; set; }
public string DebitId{get;set;}
public string CreditId{get;set;}
public decimal? ExchangeRate { get; set; }
public decimal CreditValue {get;set;}
public decimal DebitValue {get;set;}
public string DebitCurrency {get;set;}
public string CreditCurrency {get;set;}
public decimal Nett
{
get { return _nett; }
set
{
_nett = value;
CreditValue = Math.Round(Nett, 2);
DebitValue = Math.Round(Nett * (ExchangeRate ?? 1), 2);
}
}
etc ...
}
Example data IEnumerable<Journal
>:
Id DebitId CreditId Nett ExchangeRate DbCurr CrCurr DbVal CrVal
1 Expense1 Bank 100 2.03 BRL USD 203.00 100.00
2 Expense2 Bank 50 null USD USD 50.00 50.00
3 Bank Client1 300 null USD USD 300.00 300.00
4 Stock Bank 300 null USD USD 300.00 300.00
For example, when I query for the Bank, I want to be able to sum the DbVal and CrVal and calculate the balance, but in order to do so, I have to zero either one or the other (as per the query).
回答1:
You are mixing LINQ and Lucene query here, and that means they will be evaluated on the client.
You need to move a lot of this into an index and query that using session.Query, not session.Advanced.LuceneQuery.
来源:https://stackoverflow.com/questions/11963777/when-is-a-groupby-query-evaluated-in-ravendb