Consulta Linq com múltiplos contém/qualquer para Ravendb
Pergunta
Eu tenho uma classe de documentos que contém uma lista de "tags". Algo como:
class Item {
string Name { get; set; }
List<string> Tags {get; set;}
}
Agora eu gostaria de criar uma consulta para Ravendb que me entrega todos os itens filtrados por uma lista de tags. Ao usar a estrutura da entidade, consegui fazer isso por algo assim:
var query = GetQueryable();
foreach (var tag in tags)
{
query = query.Where(i => i.Tags.Contains(tag));
}
No entanto, isso não parece funcionar com Ravendb, provavelmente porque contém não é suportado. Eu também tentei reescrevê -lo usando qualquer (Where(i => i.Tags.Any(t=>t == tag))
) Mas isso me dá uma exceção estranha:
Unable to cast object of type
'System.Linq.Expressions.PrimitiveParameterExpression`1[System.String]'
to type 'System.Linq.Expressions.MemberExpression
Alguma ótima idéia? Estou fazendo isso completamente errado?
Solução
Contém de fato ainda não é suportado (talvez deva ser, mas isso é outra questão completamente - nós realmente adicionamos suporte a vários operadores quando solicitados)
Quanto a várias consultas contra qualquer, presumo que você esteja tentando fazer dados dinâmicos e deseja alcançar algo como
"X OR Y OR Z"
Isso é complicado, e o provedor LINQ por padrão agregará aqueles múltiplos onde cláusulas com e, então seu exemplo parece
"X AND Y AND Z"
O que obviamente nunca será o caso.
Sua melhor opção para este é descer para a consulta Lucene (pelo menos por enquanto) e fazer algo assim:
var results = s.Advanced.LuceneQuery<Item>()
.Where(string.Format("Tags,:({0})", string.Join(" OR ", tags)));
Faz sentido?
A consulta acima vai parecer algo como
"Tags,:(X OR Y OR Z)"
Nota: "Tags", informa Ravendb que as tags são uma matriz
Ok, [editar]!
A maneira mais fácil de conseguir o que você realmente querer é fazer algo nesse sentido
new IndexDefinition<Item, Item>()
{
Map = docs => from doc in docs
select new
{
Tags = doc.Tags
},
Indexes = {{ x => x.Tags, FieldIndexing.Analyzed }}
}.ToIndexDefinition(store.Conventions));
Então, para consultar seus ANDs, você pode fazer algo assim:
var results = s.Advanced.LuceneQuery<Item, WhateverYouCalledThatIndex>()
.Where(string.Format("Tags:({0})", string.Join(" AND ", tags)));
Agora, coisas a serem conscientes
Tags = doc.Tags
Será serializando toda a matriz inteira em uma bolha gigante, pois são apenas cordas que funcionarão para este exemplo.
Estou olhando para as melhores maneiras de expressar isso, é improvável que vamos criar uma maneira Linq -ish de fazer isso, pois ele realmente não mapeia muito bem - mas é uma resposta que funcionará :)
Eu acho que gostaria de poder pelo menos fazer
Map = docs => from doc in docs
select new
{
Tags = String.Join(" ", doc.Tags)
},
(Isso não funcionará, então não tente), mas é um pouco mais explícito sobre o que você deseja alcançar.