2010-11-17 18 views
17

Tengo una clase de documento que contiene una lista de "etiquetas". Algo así como:Consulta de Linq con múltiples Contains/Any para RavenDB

class Item { 
    string Name { get; set; } 
    List<string> Tags {get; set;} 
} 

Ahora me gustaría crear una consulta para RavenDB que me da todos los elementos filtrados por una lista de etiquetas. Al utilizar Entity Framework logré hacerlo por algo como esto:

var query = GetQueryable(); 
foreach (var tag in tags) 
{ 
    query = query.Where(i => i.Tags.Contains(tag)); 
} 

Sin embargo, esto no parece trabajar con RavenDB, muy probablemente porque no se admite Contiene .. También he intentado volver a escribir el uso de cualquiera, (Where(i => i.Tags.Any(t=>t == tag))), pero eso me da una extraña excepción:

Unable to cast object of type 
'System.Linq.Expressions.PrimitiveParameterExpression`1[System.String]' 
to type 'System.Linq.Expressions.MemberExpression 

Cualquier grandes ideas? ¿Estoy haciendo esto completamente mal?

+0

¿Qué es el tipo de devolución 'GetQueryable()'? y qué estás haciendo en 'query = query.Where (i => i.Tags.Contains (tag));'? ¿Cuál es el tipo de consulta? –

+0

GetQueryable() devuelve un IQueryable . Sin embargo, tengo acceso a toda DocumentSession, así que eso fue solo un ejemplo. – CodingInsomnia

Respuesta

18

Contiene es de hecho aún no está soportada (Tal vez que debería ser, pero eso es harina de otro costal - sólo realmente añadir soporte para varios operadores cuando su pedido)

En cuanto a múltiples consultas en cualquier, supongo que' estamos tratando de hacer los datos dinámicos y que quiere lograr algo así como

"X OR Y OR Z" 

Eso es un asunto difícil, y el proveedor de LINQ por defecto se agregarán los múltiples cláusulas WHERE con y, por lo que su ejemplo se parece

"X AND Y AND Z" 

Que obviamente nunca será el caso.

Su mejor opción para esto es que bajar a la consulta de Lucene (al menos por ahora) y hacer algo como esto:

var results = s.Advanced.LuceneQuery<Item>() 
        .Where(string.Format("Tags,:({0})", string.Join(" OR ", tags))); 

sentido?

La consulta anterior se verá algo como

"Tags,:(X OR Y OR Z)" 

Nota: "Tags", informa RavenDB que las etiquetas es una matriz

bien, [editar]!

La forma más fácil de obtener lo que realmente desea es hacer algo en este 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)); 

continuación para consultar sus ands, se puede hacer algo como esto:

   var results = s.Advanced.LuceneQuery<Item, WhateverYouCalledThatIndex>() 
        .Where(string.Format("Tags:({0})", string.Join(" AND ", tags))); 

Ahora, hay que tener en cuenta

 Tags = doc.Tags 

Wi ll serializar todo el conjunto en un blob gigante, ya que son solo cadenas las que funcionarán para este ejemplo.

Busco a mejores formas de expresar esto, es poco probable que vamos a llegar a una forma de LINQ-ish de hacer esto, ya que en realidad no mapa aprecia muy bien - pero es una respuesta que va a trabajar :)

creo que será bastante como para ser capaz de hacer al menos

Map = docs => from doc in docs 
            select new 
            { 
             Tags = String.Join(" ", doc.Tags) 
            }, 

(Esto no funcionará de modo que no lo intente), pero es un poco más explícito sobre lo que quieres lograr

+0

¡Cerrar, pero no cigarro! ;-) En primer lugar, la versión que uso no parece funcionar con la sintaxis "Tags ,:". Sin embargo, si creo una clase Tag con una sola propiedad llamada Name (y uso "Tags, Name:"), su solución funciona. Pero no hace exactamente lo que necesito. Me gustaría que devuelva solo los artículos que tienen TODAS las etiquetas en la lista de etiquetas (no ninguna de las etiquetas). – CodingInsomnia

+0

Bien, ESO es más difícil, ¿cómo harías eso como una operación basada en conjunto en una base de datos relacional? Creo que en este momento es posible que tenga que crear un índice para fusionar esas etiquetas en un solo campo y especificar el analizador que desee, y usar una consulta Lucene para decir "Todos estos términos, por favor" –

+0

Sí, no es muy bonito en una base de datos relacional tampoco, pero el código que escribí en la pregunta realmente funciona con Entity Framework. Sin embargo, no es muy bonita, y apenas muy eficiente (que fue parte de la razón por la que pensé que tal vez Raven sería capaz de hacer un mejor trabajo). – CodingInsomnia

Cuestiones relacionadas