Linq-запрос с несколькими Contains / Any для RavenDB - PullRequest
17 голосов
/ 17 ноября 2010

У меня есть класс документа, который содержит список «тегов». Что-то вроде:

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

Теперь я хотел бы создать запрос для RavenDB, который передает мне все элементы, отфильтрованные по списку тегов. При использовании Entity Framework мне удалось сделать это примерно так:

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

Однако, похоже, это не работает с RavenDB, скорее всего потому, что Contains не поддерживается. Я также попытался переписать его, используя Any (Where(i => i.Tags.Any(t=>t == tag))), но это вызывает у меня странное исключение:

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

Есть отличные идеи? Я делаю это совершенно неправильно?

1 Ответ

17 голосов
/ 17 ноября 2010

Contains действительно еще не поддерживается (возможно, так и должно быть, но это совсем другое дело - мы действительно добавляем поддержку для различных операторов только по запросу)

Что касается нескольких запросов к Any, я полагаю, что вывы пытаетесь создать динамические данные, и вы хотите получить что-то вроде

"X OR Y OR Z"

Это сложно, и поставщик LINQ по умолчанию объединит эти несколько предложений WHERE с AND, поэтому ваш пример выглядит как

"X AND Y AND Z"

Что, очевидно, никогда не будет иметь место.

Лучший вариант для этого - перейти к запросу Lucene (по крайней мере, на данный момент) и сделать что-то вроде этого:

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

Имеет смысл?

Приведенный выше запрос будет выглядеть примерно так:

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

Примечание: «Теги» информируют RavenDB, что Теги - это массив

Хорошо, [править]!

Самый простой способ получить то, что вы на самом деле хотите , - это сделать что-то вроде этого

                new IndexDefinition<Item, Item>()
                {
                    Map = docs => from doc in docs
                                  select new
                                  {
                                      Tags = doc.Tags
                                  },
                    Indexes = {{ x => x.Tags, FieldIndexing.Analyzed }}
                }.ToIndexDefinition(store.Conventions));

Тогдазапросить ваши и вы можете сделать сЧто-то вроде этого:

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

Теперь, что нужно знать

       Tags = doc.Tags

Сериализует весь массив в один гигантский BLOB-объект, так как в этом примере будут работать только строки.

Я смотрю на более эффективные способы выражения этого, маловероятно, что мы придумаем способ сделать это в LINQ, так как он не очень хорошо отображает - но это это ответ, который будет работать:)

Я думаю, я бы очень хотел иметь возможность по крайней мере сделать

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

(Это не сработает, так чтопопробуйте), но это немного более ясно о том, чего вы хотите достичь.

...