MongoDB C# – Как выполнить сортировку и разбиение на страницы массива объектов в записи

У меня есть MongoDB с коллекцией: люди.

[
  {
    _id: 0,
    name: "lucas",
    properties: [
      {
        Name: "powers",
        Values: [
          "fireball",
          "icebeam",
          "thunderbolt"
        ]
      },
      {
        Name: "weakness",
        Values: [
          "poison",
          "rain"
        ]
      }
    ]
  },
  {
    _id: 1,
    name: "cage",
    properties: [
      {
        Name: "powers",
        Values: [
          "flight",
          "strength",
          "fireball"
        ]
      },
      {
        Name: "weakness",
        Values: [
          "lightning",
          "ice"
        ]
      }
    ]
  },
  {
    _id: 2,
    name: "joe",
    properties: [
      {
        Name: "powers",
        Values: [
          "immortality",
          "strength",
          "flight"
        ]
      }
    ]
  },
  {
    _id: 3,
    name: "bob",
    properties: [
      {
        Name: "weakness",
        Values: [
          "cold",
          "flu",
          "no food"
        ]
      }
    ]
  }
]

Я намеренно исключил свойство «слабость» для «Джо» и «силы» для «Боб», поскольку не у всех людей есть слабости или способности. Я не уверен, как сортировать все записи на основе свойств «степени» по их значениям. Предполагая, что у меня 50 героев, я хочу вернуть все записи, но отсортированные по свойствам, и вернуть их с разбивкой на страницы.

🤔 А знаете ли вы, что...
C# поддерживает атрибуты, которые позволяют добавлять метаданные к коду.


1
50
1

Ответ:

Решено

В запросе MongoDB вам необходимо извлечь массив степеней и его размер (powersLength) перед выполнением сортировки.

После этого удалите оба поля, чтобы они не отображались в результате.

db.collection.aggregate([
  {
    $set: {
      properties: {
        $map: {
          input: "$properties",
          in: {
            $mergeObjects: [
              "$$this",
              {
                Values: {
                  $sortArray: {
                    input: "$$this.Values",
                    sortBy: 1
                  }
                }
              }
            ]
          }
        }
      }
    }
  },
  {
    $set: {
      powers: {
        $getField: {
          field: "Values",
          input: {
            $first: {
              $filter: {
                input: "$properties",
                cond: {
                  $eq: [
                    "$$this.Name",
                    "powers"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  {
    $set: {
      powersLength: {
        $size: {
          $ifNull: [
            "$powers",
            []
          ]
        }
      }
    }
  },
  {
    $sort: {
      powersLength: -1,
      powers: 1
    }
  },
  {
    $unset: [
      "powers",
      "powersLength"
    ]
  }
])

Демо @ Mongo Playground


Я думаю, что в синтаксисе драйвера MongoDB .NET это сложно и, возможно, недостижимо с полным синтаксисом Fluent API.

Но вы можете перевести запрос на BsonDocument через MongoDB Compass (функция экспорта в язык).

var pipeline = new BsonDocument[]
{
    new BsonDocument("$set",
    new BsonDocument("powers",
    new BsonDocument("$getField",
    new BsonDocument
                {
                    { "field", "Values" },
                    { "input",
    new BsonDocument("$first",
    new BsonDocument("$filter",
    new BsonDocument
                            {
                                { "input", "$properties" },
                                { "cond",
    new BsonDocument("$eq",
    new BsonArray
                                    {
                                        "$$this.Name",
                                        "powers"
                                    }) }
                            })) }
                }))),
    new BsonDocument("$set",
    new BsonDocument("powersLength",
    new BsonDocument("$size",
    new BsonDocument("$ifNull",
    new BsonArray
                    {
                        "$powers",
                        new BsonArray()
                    })))),
    new BsonDocument("$sort",
    new BsonDocument
        {
            { "powersLength", -1 },
            { "powers", 1 }
        }),
    new BsonDocument("$unset",
    new BsonArray
        {
            "powers",
            "powersLength"
        })
};

var result = await _col.Aggregate<People>(pipeline)
    .ToListAsync();

Или поработайте с Fluent API частично:

var setStageFirst = new BsonDocument("$set",
    new BsonDocument("powers",
        new BsonDocument("$getField",
            new BsonDocument
            {
                { "field", "Values" },
                { "input",
                    new BsonDocument("$first",
                        new BsonDocument("$filter",
                            new BsonDocument
                            {
                                { "input", "$properties" },
                                { "cond",
                                    new BsonDocument("$eq",
                                        new BsonArray
                                        {
                                            "$$this.Name",
                                            "powers"
                                        })
                                }
                            }))
                }
            })));

var setStageSecond = new BsonDocument("$set",
    new BsonDocument("powers",
        new BsonDocument("$getField",
            new BsonDocument
            {
                { "field", "Values" },
                { "input",
                    new BsonDocument("$first",
                        new BsonDocument("$filter",
                            new BsonDocument
                            {
                                { "input", "$properties" },
                                { "cond",
                                    new BsonDocument("$eq",
                                        new BsonArray
                                        {
                                            "$$this.Name",
                                            "powers"
                                        }) 
                                }
                            })) 
                }
            })));

var setStageThird = new BsonDocument("$set",
    new BsonDocument("powersLength",
        new BsonDocument("$size",
            new BsonDocument("$ifNull",
                new BsonArray
                {
                    "$powers",
                    new BsonArray()
                }))));

var sortStage = new BsonDocument
        {
            { "powersLength", -1 },
            { "powers", 1 }
        };

var unsetStage = new BsonDocument("$unset",
    new BsonArray
        {
            "powers",
            "powersLength"
        });

var result = await _col.Aggregate()
    .AppendStage<BsonDocument>(setStageFirst)
    .AppendStage<BsonDocument>(setStageSecond)
    .AppendStage<BsonDocument>(setStageThird)
    .Sort(sortStage)
    .AppendStage<People>(unsetStage)
    .ToListAsync();