Как использовать linq, чтобы сделать WHERE для объекта коллекции (используя источник данных Netflix) - PullRequest
2 голосов
/ 18 сентября 2010

Я использую LinqPad для изучения Linq путем запроса источника данных NetFlix OData.

(Кстати, я знаю, что у них такой же вопрос уже на SO ... не помог мне).

Вот запрос, который я получил, который потрясающий.

from x in Titles
//where x.Rating=="PG"
where x.Instant.Available==true
where x.AverageRating>=4.0
//where x.Rating.StartsWith("TV")
//where x.Genres.First (g => g.Name.Contains("Family") ) //(from y in Genres where y.Name.Contains("Family") select y)
//where x.Genres.First (g => g.Name=="")
//orderby x.Name
orderby x.AverageRating descending
//select x
//)
select new {x.Name, x.Rating, x.AverageRating, x.ShortSynopsis}

(простите за все комментарии ... это свидетельство того факта, что я экспериментирую и что я изменю запрос для различных нужд).

Есть две вещи, которые я не могу понять.

Во-первых. Допустим, я хочу вернуть только первые 10 результатов.

Второе (и самое главное). Я хочу фильтровать по частичной строке жанра. Каждый заголовок содержит коллекцию жанров. Я хочу показать только Жанры, где Имя содержит определенную строку (например, «Семейство»). Еще лучше фильтровать, используя заголовки, где genre.name.contains "firstFilter" И "secondFilter".

По сути, я хочу фильтровать по жанрам и не могу понять, как это сделать, так как Title содержит собственную коллекцию жанров, и я не могу понять, как вернуть только заголовки из одного или нескольких жанров коллекции .

Спасибо за вашу помощь!

ps ... похоже, что источник Netflix OData не поддерживает ни одного оператора.

Сет

Ответы [ 3 ]

4 голосов
/ 18 сентября 2010

Чтобы вернуть первые 10 результатов, поместите код выше в круглые скобки и поставьте .Take (10) в конце

var foo = ( from x in Titles... ).Take(10);

В настоящее время нет способа использовать синтаксис запроса в C #.

Что касается жанрового фильтра, как указывает klabranche, oData не поддерживает многие из тех же конструкций Linq, которые вы можете использовать локально с обычным IEnumerable или IQueryable.

Решение Klabranche не поддерживает содержит.Тем не менее, он совершает 2 поездки на сервер, чтобы получить результаты.(см. мой комментарий к его ответу о том, почему это кажется необходимым)

Вот альтернатива, которая делает один прием на сервер для получения данных, а затем обрабатывает эти данные локально.Поскольку некоторые запросы выполняются локально, вы можете использовать предложения string.Contains, "или" и другие преимущества.

Недостатком этого подхода является то, что он извлекает по сети больше данных, чем необходимо для ответа на запрос,С другой стороны, это легко понять, и это работает.

Когда я комбинирую «Семья» и «Дети», он возвращает 21 результат.

var oDataQuery = from x in Titles
                 where x.AverageRating >= 4
                    && x.Instant.Available==true       
                 orderby x.AverageRating descending
                 select new {x.Name, x.Rating, x.AverageRating, x.ShortSynopsis, x.Genres};

var localQuery = from o in oDataQuery.ToList()
                 where o.Genres.Any(g => g.Name.Contains("Family"))
                    && o.Genres.Any(g => g.Name.Contains("Children"))
                 select new {o.Name, o.Rating, o.AverageRating, o.ShortSynopsis };

localQuery.Dump();
1 голос
/ 18 сентября 2010

Для получения первых 10:

(from x in Titles
where x.Instant.Available==true 
where x.AverageRating>=4.0 
orderby x.AverageRating descending 
select new {x.Name, x.Rating, x.AverageRating, x.ShortSynopsis}
).Take(10)

Для фильтрации по одному жанру (не то, что вы хотите ...):

from g in Genres
from t in g.Titles 
where g.Name == "Horror" 
where t.Instant.Available==true
where t.AverageRating >=4.0
orderby t.AverageRating descending 
select new {t.Name, t.Rating, t.AverageRating, t.ShortSynopsis}

Однако, вы хотели иметь несколько жанров НО OData не поддерживает Выбрать много запросов , поэтому содержит сбои или пытается ИЛИ название жанра.

Ниже не удается , потому что содержит много ...

var q1 = from g in Genres
from t in g.Titles 
where g.Name.Contains("Horror")
where t.Instant.Available==true
where t.AverageRating >=4.0
orderby t.AverageRating descending 
select new {t.Name, t.Rating, t.AverageRating, t.ShortSynopsis};

Для фильтрации по нескольким жанрам я обнаружил, что вы можете использовать Concat или Union запрос (в LinqPad обязательно измените на C # операторы, а не выражения):

var q1 = from g in Genres
from t in g.Titles 
where g.Name=="Horror"
where t.Instant.Available==true
where t.AverageRating >=4.0
orderby t.AverageRating descending 
select new {t.Name, t.Rating, t.AverageRating, t.ShortSynopsis};

var q2 = from g in Genres
from t in g.Titles 
where g.Name=="HBO"
where t.Instant.Available==true
where t.AverageRating >=4.0
orderby t.AverageRating descending 
select new {t.Name, t.Rating, t.AverageRating, t.ShortSynopsis};

var concat = q1.ToList().Concat(q2);
//var union = q1.Union(q2);

Объединение двух запросов приведет к удалению дубликатов, но это то, что вам нужно. Если я правильно вас понял, вам нужны фильмы, которые есть только в обоих жанрах?

В этом случае вы захотите использовать Concat, который будет возвращать все записи.

Теперь вам просто нужно найти записи, которые есть в запросе более одного раза, и у вас есть результаты:

var results = from c in concat
group c by c.Name into grp
where grp.Count() > 1
select grp;
1 голос
/ 18 сентября 2010

OData и Netflix API поддерживают методы Take () и Contains ():

from t in Titles
where t.Name.Contains("Matrix")
select t

(from t in Titles
where t.Name.Contains("Matrix")
select t).Take(10)
...