Используйте LINQ в C # для поиска записей MondoDB, когда значения в поле списка соответствуют значению критерия из списка

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

Запись Mongo в коллекции («Предмет»):

{
    "_id": ...,
    "StringList": [
        "string1",
        "string2",
        "string3"
    ],
    ...
}

Критерий поиска:

var criteria = new List<string> { "string2", "string4" };

Мой код:

var foundItems = iMongoDataProvider.FindAll<Item>()
                           .Where(x =>x.StringList.ContainsAny(criteria)).ToList();

На основании вышеизложенного должна быть возвращена запись Mongo, поскольку одно из значений StringList совпадает с одним из значений в критериях поиска. Ничего не возвращается, хотя я могу вручную просмотреть коллекцию и найти соответствующую запись. Что я делаю неправильно? Может ли кто-нибудь привести пример, который сделает то, что мне нужно? Спасибо!


person Mark Linebarger    schedule 05.01.2017    source источник


Ответы (3)


вы пробовали что-то вроде:

using System;
using System.Collections.Generic;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using System.Linq;
using System.Linq.Expressions;

var foundItems = _collection.FindAll(x=> criteria.Any(cc=> xx.StringList.Contains(cc))).ToList();

Где _collection - это IMongoCollection<TEntity> _collection

person federico scamuzzi    schedule 05.01.2017
comment
объявлена ​​переменная xx? - person Hogan; 05.01.2017
comment
Я не понимаю этого ответа. Как вчера спросил @Hogan, я не вижу объявления для _1 _... должно ли это быть x? Если так, то это не сработало. Пожалуйста, объясни. - person Mark Linebarger; 07.01.2017

Вам нужен ElemMatch Filter (https://docs.mongodb.com/v3.2/reference/operator/query/elemMatch/):

var foundItems = collection.Find(Builders<Item>.Filter.ElemMatch(
                     x => x.StringList,
                     s=>criteria.Contains(s)));

где collection - это ваш IMongoCollection<Item> Я вижу, что вы используете FindAll, это означает, что ваш драйвер MongoDb имеет версию 1.x (подробнее об этом см. здесь: FindAll в MongoDB .NET Driver 2.0) Я бы посоветовал обновить ваш драйвер, потому что эта версия не является актуальной. Или есть какая-то важная причина этого не делать?

Это фильтр запроса на сервере. Конечно, вы можете получить свои данные как IEnumerable и отфильтровать их локально:

var foundItems = collection.Find(x=>true)
                   .ToEnumerable()
                   .Where(x => x.StringList.Intersect(criteria).Any());

Если ваши данные не такие большие, и у вас все в порядке с фильтрацией на клиенте, это тоже хороший способ.

И если вы уже делаете FindAll, это означает, что вы получаете все данные, вы можете запросить их с помощью intersect:

var foundItems = iMongoDataProvider.FindAll<Item>()
                           .Where(x => x.StringList.Intersect(criteria).Any());
person Maksim Simkin    schedule 05.01.2017
comment
Пытался использовать ваше первое предложение, но получил Cannot resolve symbol 'Builders'; получил то же сообщение для ToEnumerable по вашему второму предложению. Не уверен, что мне не хватает. - person Mark Linebarger; 07.01.2017
comment
@MarkLinebarger Вам не хватает новой версии драйвера, я обновил свой ответ. Извините, все, что я написал, было для 2.x MongoDb c # drvier - person Maksim Simkin; 07.01.2017
comment
У нас версия 1.11.0.92. Мы не проводили никаких исследований различий при обновлении до 2.x (и любых проблем, которые это может вызвать), поэтому я не могу выполнить обновление без консультации с командой. Я знаю, что FindAll используется во многих местах нашего решения; если это резко изменилось, не стоит менять, чтобы решить эту единственную проблему. Я знаю, что мой код работает, если я найду все на основе одного из критериев записей; то есть: var foundItems = iMongoDataProvider.FindAll<Item>().Where(x => x.StringList.Contains(criteria[0])).ToList(); - person Mark Linebarger; 07.01.2017
comment
@MarkLinebarger Я обновил свой ответ, поскольку я понял, что FindAlljust дает вам все элементы, поэтому это не mongodb, но вопрос Linq и запрос пересечения должны давать допустимые результаты. - person Maksim Simkin; 07.01.2017

Вам нужно знать, имеет ли пересечение двух списков какие-либо значения:

 .Where(x =>x.StringList.Intersect(criteria).Any())

Я не уверен, в чем проблема с вашим кодом, но вот рабочий код

void Main()
{
    List<string> []StringList = new List<string>[] {
             new List<string> {    "string1", "string2", "string3" },
             new List<string> {    "string11", "string12", "string13" },
             new List<string> {    "string21", "string22", "string4" }
    };

    var criteria = new List<string> { "string2", "string4" };

    var foundItems = StringList
                      .Where(x => x.Intersect(criteria).Any()).ToList();

    foundItems.Dump();
}

Я тестировал это с помощью LinqPad (который я рекомендую всем, кто работает с Linq, и это бесплатно).

person Hogan    schedule 05.01.2017
comment
Это, на первый взгляд, казалось моим лучшим вариантом, но при запуске я получаю эту ошибку: Невозможно определить информацию сериализации для выражения: Enumerable.Intersect ‹String› (x.StringList, System.Collections.Generic.List `1 [System.String]). Я что-то упускаю. - person Mark Linebarger; 07.01.2017
comment
@MarkLinebarger не уверен, мне нужно было увидеть весь исходный код, но он должен быть таким же, как .Where(x => criteria.Intersect(x.StringList).Any()), дает ли это вам ту же ошибку? - person Hogan; 07.01.2017
comment
Я получаю ту же общую ошибку ... вот полная ошибка для второй попытки: 'System.NotSupportedException не было обработано кодом пользователя HResult = -2146233067 Сообщение = Невозможно определить информацию сериализации для выражения: Enumerable.Intersect ‹String› (System.Collections.Generic.List`1 [System.String], x.StringList). Источник = MongoDB.Driver ' - person Mark Linebarger; 07.01.2017
comment
@MarkLinebarger - см. Мою правку - чем ваш код отличается от этого рабочего примера? - person Hogan; 07.01.2017
comment
Возможно, моя проблема заключается в том, чтобы сделать это с помощью MondoDB. Как отмечалось в моем исходном вопросе, я просматриваю коллекцию в Mongo, которая включает поле (StringList), которое представляет собой массив строк. В коде (выше) я пытаюсь FindAll элементов, где любое значение, найденное в массиве строк, соответствует любому значению, которое можно найти в списке ‹string› ( критерии). Я не знаю, допускают ли MongoDB.Drivers этот тип выражения Linq. Есть идеи по этому поводу? - person Mark Linebarger; 07.01.2017
comment
@MarkLinebarger - да, Максим Симкин хорошо объяснил это в своем ответе, используйте это. - person Hogan; 07.01.2017
comment
Его предложение требует обновления наших драйверов MongoDB; Я буду это проверять. Спасибо за вашу помощь. - person Mark Linebarger; 07.01.2017