Как запросить коллекцию MongoDB с помощью драйвера C #, используя значения словаря в качестве параметров фильтра

#c# #mongodb #linq #aggregation-framework

#c# #mongodb #linq #агрегация-фреймворк

Вопрос:

У меня есть список событий

Где событие

 public class Event
    {
        public int Id { get; set; }
        public int Seq { get; set; }

        public Event(int id, int seq)
        {
            Id = id;
            Seq = seq;
        }
    }
  

Я хочу запросить этот список и получать только события, допустим, со значением идентификатора 1 и 2.

Затем получайте только записи события с идентификатором 1, у которых Seq равен или больше 3

И получайте только записи события с идентификатором 2, у которых Seq равен или больше 4

В примере LINQ я создал словарь «eventsToRetrieve» из int, int со значениями, которые я хочу получить, как описано выше

 [1,3]
[2,4]
  

Пример кода с C # / LINQ будет выглядеть следующим образом

  var allEvents = new List<Event>()
            {
                new Event(1, 1),
                new Event(1, 2),
                new Event(1, 3),
                new Event(1, 4),
                new Event(1, 5),
                new Event(2, 1),
                new Event(2, 2),
                new Event(2, 3),
                new Event(2, 4),
                new Event(2, 5),
                new Event(2, 6),
                new Event(3, 1),
                new Event(3, 2),
                new Event(3, 3),
            };

            var eventsToRetrieve = new Dictionary<int, int>()
            {
                { 1 ,3},
                { 2 ,4}
            };


            var eventsIWant = allEvents
           .Where(ev => eventsToRetrieve.Keys.Contains(ev.Id) 
               amp;amp; ev.Seq >= eventsToRetrieve.Single(er => er.Key == ev.Id).Value 
           ).ToList();
  

Этот LINQ работает нормально, и я получаю ожидаемые результаты.
Результатом «eventsIWant» являются только события со значениями

 (1,3)
(1,4)
(1,5)
(2,4)
(2,5)
(2,6)
  

Теперь я хочу применить что-то подобное к коллекции MongoDB, которая выглядит как приведенный выше список. У меня есть eventsCollection с составным ключом (Id), который представляет собой уникальную комбинацию пар (EntityId, SequenceNumber).

Класс событий

 
    public class Event
    {
        /// <summary>
        /// The composite key for this Event
        /// </summary>
        [BsonId]
        public EventId Id { get; internal set; }
    }

    public class EventId
    {
        public Guid EntityId { get; private set; }
        public long SequenceNumber { get; private set; }
    }

  

Я пробовал что-то вроде

 
var eventsIWant = await _eventsCollection.FindAsync( event => eventsToRetrieve.Keys.Contains(event.Id.EntityId)
amp;amp; event.Id.SequenceNumber >= eventsToRetrieve[event.Id.EntityId])

  

Но я получаю

 System.ArgumentException: Unsupported filter MONGODB C#...
  

для

 events.Id.SequenceNumber >= sitesWithLatestSequence[events.Id.EntityId])

  

часть.

Я также попробовал что-то подобное с платформой агрегации C # / MongoDB, но не смог заставить что-то работать

Есть ли способ сделать что-то подобное тому, что сделал LINQ, но в качестве фильтра или агрегирования mongo MongoDB?

Извините, если название не очень точное, но не смог придумать что-то лучше. Открыт для предложений.

Комментарии:

1. Должен ли запрос быть динамическим или всегда есть 2 пары чисел для фильтрации? В случае, если это всегда 2 пары, вы можете создать простой фильтр без использования словаря. В случае, если количество пар является динамическим, вам, возможно, придется использовать деревья выражений, поскольку, похоже, драйвер Mongo DB не может работать со словарями / хэшами. Или используйте Mongo DB aggregation framework, как вы пытались.

2. да, это динамично. eventsToRetrieve, будь то диктант или кортеж, или что-то еще, предназначено для описания < «EntityId, который я хочу»,»С какого sequnceNumber и далее?» > . Мне может понадобиться идентификатор 1 только из seq 3. Или 1,3 и 2,4. Или 2,4 и 3,5 и 6,3 и т.д. И т.п.

Ответ №1:

Что-то вроде этого (не удалось проверить, но, надеюсь, это достаточно близко):

     var eventsToRetrieve = new Dictionary<Guid, long>()
    {
        { Guid.NewGuid() ,3},
        { Guid.NewGuid() ,4}
    };

    var parameterExpression = Expression.Parameter(typeof(Event));
    Expression idExpression = Expression.Property(parameterExpression, nameof(Event.Id));

    var body = 
        eventsToRetrieve.Aggregate(null, (Expression expression, KeyValuePair<Guid,long> etr) => 

        {
            var addedExpression =        
                                Expression.AndAlso(
                                Expression.Equal(Expression.Property(idExpression, nameof(EventId.EntityId)), Expression.Constant(etr.Key)),
                                Expression.LessThanOrEqual(Expression.Property(idExpression, nameof(EventId.SequenceNumber)), Expression.Constant(etr.Value)));
            return expression != null ? Expression.OrElse(expression,addedExpression) : addedExpression;
        });


    var filter = (Expression<Func<Event,bool>>) Expression.Lambda(body, parameterExpression);
  

Комментарии:

1. Не уверен, что я должен делать с этим лямбда-выражением?

2. Приведите его к типу фильтра mongo db?

3. Добавлено приведение. API mongo позволяет передавать эксперименты в виде фильтров, проекций и т. Д. Если я помню, вы можете просто сделать: collection . AsQueryable() . Где (фильтр); Где filter — это фильтр из моего кода.

4. Также его SiteEvent.Id.EntityId , событие. Id — это составной ключ, который имеет EntityId , порядковый номер.

5. Коллекция. AsQueryable() . Где (фильтр); пробовал, но не принял его.

Ответ №2:

Попробуйте выполнить следующее :

             var allEvents = new List<Event>()
            {
                new Event(1, 1),
                new Event(1, 2),
                new Event(1, 3),
                new Event(1, 4),
                new Event(1, 5),
                new Event(2, 1),
                new Event(2, 2),
                new Event(2, 3),
                new Event(2, 4),
                new Event(2, 5),
                new Event(2, 6),
                new Event(3, 1),
                new Event(3, 2),
                new Event(3, 3),
            };

            Dictionary<int, List<int>> dict = allEvents
                .GroupBy(x => x.Id, y => y.Seq)
                .ToDictionary(x => x.Key, y => y.ToList());

            Dictionary<int, List<int>> results = dict.Where(x => x.Value.Any(y => y >= 4))
                .GroupBy(x => x.Key, y => y.Value)
                .ToDictionary(x => x.Key, y => y.Where(z => z.Any(a => a >= 4)).SelectMany(a => a.Where(b => b >= 4)).ToList());

        }
  

Комментарии:

1. Привет, LINQ работает нормально. Это фильтр mongo, для которого требуется другой подход. Спасибо.