Поиск MongoDB $ в двухуровневом вложенном документе без использования $ unwind - PullRequest
0 голосов
/ 06 августа 2020

У меня есть следующие документы

creditRequest (Написание только ключей, которые я хочу спроецировать)

{
  "_id": "5f2bf26783f65d33026ea592",
  "lendingpartner": { 
     /* some keys here */
  },
  "loans": [
    {
      "loanid": 43809,
      "loanamount": 761256,
      "jewels": [
        "5f2bf26783f65d33026ea593",
        "5f2bf26783f65d33026ea594"
        "5f2bf26783f65d33026ea595"
      ],
    }
  ]
}

pledgedJewel

{
  "_id": "5f2bf26783f65d33026ea593",
  "netweight": 8.52,
  "purity": 19,
}

Я хочу достичь

{
  "_id": "5f2bf2b583f65d33026ea603",
  "lendingpartner": { 
     /* some keys here */
  },
  "loans": [
    {
      "loanid": 40010,
      "loanamount": 100000,
      "jewels": [
        {
          "_id": "5f2bf26783f65d33026ea593",
          "netweight": 8.52,
          "purity": 19,
        },
        {
          "_id": "5f2bf26783f65d33026ea594",
          "netweight": 5.2,
          "purity": 40,
        },
        {
          "_id": "5f2bf26783f65d33026ea595",
          "netweight": 4.52,
          "purity": 39,
        }
      ]
    }
  ]
}

Поскольку я хочу, чтобы детали драгоценностей были заполнены внутри массива драгоценностей каждого займа, $unwind мне не поможет. (Я пробовал поэкспериментировать с этим)

Я думал, что могу запустить $map в массиве ссуд, а затем запустить $lookup для каждого драгоценного камня ссуды (двойная карта?), Но не смог придумать работоспособное решение. В любом случае, это казалось неправильным подходом.

Это лучшее, что я мог придумать (далеко не желаемый результат). Я использую карту для выборочного выбора ключей из объекта ссуд.

const loanrequests = await db.collection('loanrequest').aggregate([
  { $match: { requester: ObjectID(user.id) } },
  {
    $project: {
      lendingpartner: {
        name: 1,
        branchname: '$branch.branchname',
      },
      loans: {
        $map: {
          input: '$loans',
          as: 'loan',
          in: {
            loanid: '$$loan.loanid',
            loanamount: '$$loan.amount',
            jewels: '$$loan.jewels',
          },
        },
      },
    },
  },
  /*
  * I experimented with unwind here. Tried adding 
  * { $unwind: '$loans' },
  * { $unwind: '$loans.jewels' }
  * but it does not give me the result I need (as already said before)
  */
]).toArray();

Я полагаю, мне нужно выполнить $lookup перед проекцией, но мне сложно написать работоспособное решение из-за Двухуровневая вложенная структура документа (сначала массив loans, а затем loans.jewels)

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

Спасибо!

Ответы [ 2 ]

1 голос
/ 06 августа 2020

Если нет других вещей, которых вы пытаетесь достичь с помощью агрегата, вы можете использовать .populate в mon goose.

LoanReqests
  .find(
    {requester: user.id},
    {name: 1, branch: 1, loans: 1} // Projection
  )
  .populate('loans.jewels');

Если вам нужно использовать агрегат, чтобы сделать что-то, чего нет в вашем примере, тогда $ unwind действительно ваш лучший выбор, но затем $ grouping после поиска $, чтобы получить желаемый результат. Если это не сработает, не могли бы вы подробнее рассказать о проблеме с $ unwind? Я предполагаю, что это связано с полями, не указанными в вашем вопросе.

https://mongoplayground.net/p/O5pxWNy99J4

db.loanRequests.aggregate([
  {
    $project: {
      name: 1,
      loans: 1,
      branch: "$branch.name"
    }
  },
  {
    $unwind: "$loans"
  },
  {
    $lookup: {
      localField: "loans.jewels",
      foreignField: "_id",
      from: "jewels",
      as: "loans.jewels"
    }
  },
  {
    $group: {
      _id: "$_id",
      name: {
        $first: "$name"
      },
      branch: {
        $first: "$branch"
      },
      loans: {
        $push: "$loans"
      }
    }
  }
])
0 голосов
/ 07 августа 2020

Как упоминалось @ GitGitBoom в предыдущем ответе, подходом должно было быть $unwind с последующим $group.

Конечно, до группировки (я думаю об этом как "нераспространение" результата запуска размотки), мне нужно было запустить $lookup, чтобы заполнить loans.jewels

Вот полное решение, построенное на основе предыдущего ответа.

const loanRequests = await db.collection('loanRequest').aggregate([
  { $match: { requester: ObjectID(user.id) } },
  {
    $project: {
      lender: '$lendingpartner.name',
      branch: '$lendingpartner.branch.branchname',
      loans: 1,
    },
  },
  { $unwind: '$loans' },
  {
    $lookup: {
      localField: 'loans.jewels',
      from: 'pledgedJewel',
      foreignField: '_id',
      as: 'loans.jewels',
    },
  },
  {
    $group: {
      _id: '$_id',
      branch: { $first: '$branch' },
      lender: { $first: '$lender' },
      loans: { $push: '$loans' },
    },
  },
  {
    $project: {
      _id: 1,
      branch: 1,
      lender: 1,
      loans: 1,
    },
  },
]).toArray();

Проблема с несовпадением типов

Другая проблема заключалась в том, что мой $lookup не работал из-за несовпадения типов. В коллекции creditRequest, в которой я запускаю агрегат, идентификаторы внутри loans.jewels имеют тип string, тогда как внешнее поле _id в pledgedJewel - ObjectId

Это можно решить с помощью используя $toObjectId или $toString (поддерживается только в версии mongodb> = 4.0)

{ $project: { jewelObjId: { $toObjectId: '$loans.jewels' } } },   // for mongodb >= 4.0
{
  $lookup: {
    localField: 'jewelObjId',  // for mongodb >= 4.0
    from: 'pledgedjewel',
    foreignField: '_id',
    as: 'loans.jewels',
  },
},

Но я работал на более ранней версии mongodb, поэтому эти агрегации не работали для меня. Единственным решением этой проблемы было изменение типа loans.jewels на ObjectId вместо того, чтобы сохранять его как string, что я сделал.

Подробнее о несоответствии типов

Нужен обходной путь для поиска строки в objectID foreignField

Mongodb Присоединиться к полю _id из String в ObjectId

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...