Добавить поле в результат поиска $ в MongoDB - PullRequest
3 голосов
/ 05 августа 2020

Я пытаюсь добавить поле к результату поиска с помощью драйвера node.js MongoDB. У пользователя есть два типа каналов: 1. Его или ее собственные каналы и 2. Общие каналы. Так что мне нужно получить оба, а затем объединить их. Это мой исходный запрос, который отлично работает:

client.db('atlas').collection('users').aggregate([
    { $match: { user_id: userId } },
    {
      $lookup: {
        from: 'feeds',
        localField: "user_id",
        foreignField: "user_id",
        as: "feeds"
      }
    },
    {
      $lookup: {
        from: 'shares',
        localField: 'email',
        foreignField: "share",
        as: "shares"
      }
    },
    {
      $lookup: {
        from: 'feeds',
        localField: 'shares.feed_id',
        foreignField: "_id",
        as: "shared_feeds"
      }
    },
    {
      $addFields: { 'shared_feeds.share': true }
    },
    {
       $project: {
         user_id: 1,
         first_name: 1,
         last_name: 1,
         email: 1,
         feeds: { $concatArrays: [ '$feeds', '$shared_feeds' ] }
       }
    }
])

Но я хочу добавить поле во второй поиск по «каналам», поэтому я попытался использовать такой конвейер:

{
  $lookup: {
    from: 'feeds',
    let: { shareId: '$shares.feed_id' },
    pipeline: [
      { $match: { '_id': ObjectId('$$shareId') } }
    ],
    as: "shared_feeds"
  }
}

Но я не могу заставить совпадение работать с ObjectId. Переменная не работает. Что я делаю не так?

Примеры коллекций: пользователи:

[
    {
    "_id" : ObjectId("5e970a1f797c08e70ce4e2f6"),
    "user_id" : "testuserid",
    "first_name" : "Jacopo",
    "last_name" : "Belbo",
    "email" : "test@test.com"
   }
]

каналы:

 [
  {
      "_id" : ObjectId("5f29b02318ecbf0083f01f1e"),
      "name" : "test csv",
      "user_id" : "auth0|5eac6faa1cc1ac0c147b5b16",
      "type" : "feed",
      "created_at" : ISODate("2020-08-04T18:59:47.640Z"),
      "updated_at" : ISODate("2020-08-04T18:59:47.640Z")
  },
  {
    "_id" : ObjectId("5f29b05d18ecbf0083f01f1f"),
    "name" : "typtap-small",
    "user_id" : "auth0|5f1b2da0e60194003d714680",
    "url" : "http://localstack:4572/atlas/usertes",
    "type" : "feed",
    "created_at" : ISODate("2020-08-04T19:00:45.886Z"),
    "updated_at" : ISODate("2020-08-04T19:00:45.886Z")
  }
]

акции:

[
    {
    "_id" : ObjectId("5f29b07318ecbf0083f01f20"),
    "feed_id" : ObjectId("5f29b05d18ecbf0083f01f1f"),
    "share" : "test@test.com",
    "type" : "share",
    "recipient" : "email",
    "created_at" : ISODate("2020-08-04T19:01:07.668Z"),
    "updated_at" : ISODate("2020-08-04T19:01:07.668Z")
}
]

Ответы [ 2 ]

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

В let: { shareId: '$shares.feed_id' }, вы передаете ObjectId shareId. Таким образом, вы не можете обернуть ObjectId() на этапе match. Когда вы используете конвейер внутри поиска для нескольких условий Просто используйте $expr

    {
      $match: {
        $expr: {
          $eq: [
            "$$shareId",
            "$_id"
          ]
        }
      }
    }

Обновление 1.

когда вы используете конвейер внутри $lookup, вам нужно чтобы использовать $expr, обратитесь к $ expr в поиске . Как только вы получите результат, у вас будет массив, поэтому $unwind и выполните соединение. Затем сгруппируйте его, чтобы избежать дублирования.

Рабочий Пн go Детская площадка

Обновление 2

Пн go скрипт

[
  {
    $match: {
      user_id: "testuserid"
    }
  },
  {
    $lookup: {
      from: "feeds",
      localField: "user_id",
      foreignField: "user_id",
      as: "feeds"
    }
  },
  {
    $lookup: {
      from: "shares",
      localField: "email",
      foreignField: "share",
      as: "shares"
    }
  },
  {
    $unwind: {
      path: "$shares",
      preserveNullAndEmptyArrays: false
    }
  },
  {
    $lookup: {
      from: "feeds",
      let: {
        shareId: "$shares.feed_id"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $eq: [
                "$$shareId",
                "$_id"
              ]
            }
          }
        }
      ],
      as: "shared_feeds"
    }
  },
  {
    $group: {
      _id: "$_id",
      user_id: {
        $first: "$user_id"
      },
      first_name: {
        $first: "$first_name"
      },
      last_name: {
        $first: "$last_name"
      },
      email: {
        $first: "$email"
      },
      feeds: {
        $first: "$feeds"
      },
      shares: {
        $addToSet: "$shares"
      },
      shared_feeds: {
        $addToSet: "$shared_feeds"
      }
    }
  },
  {
    $addFields: {
      shared_feeds: {
        $reduce: {
          input: "$shared_feeds",
          initialValue: [],
          in: {
            $setUnion: [
              "$$this",
              "$$value"
            ]
          }
        }
      }
    }
  },
  {
    $addFields: {
      "shared_feeds.share": true
    }
  },
  {
    $project: {
      user_id: 1,
      first_name: 1,
      last_name: 1,
      email: 1,
      feeds: {
        $concatArrays: [
          "$feeds",
          "$shared_feeds"
        ]
      }
    }
  }
]
0 голосов
/ 06 августа 2020

Вам нужно использовать $ expr с $eq в условии соответствия, потому что без выражения оно не будет соответствовать ссылочным полям, есть один пример в Поиск MongoDB с конвейером ,

  • здесь необходимо использовать $_id вместо _id, потому что выражение не найдет поле без $, а $eq не будет соответствовать условию,
{
  $lookup: {
    from: 'feeds',
    let: { shareId: '$shares.feed_id' },
    pipeline: [
      { 
        $match: { 
          $expr: {
            $eq: ['$$shareId', '$_id'] 
          }
        }
      }
    ],
    as: "shared_feeds"
  }
}

Я не уверен, что за тип этого поля shares.feed_id, если обе коллекции имеют разные типы, либо один в строке, либо один в идентификаторе объекта, тогда вы можете использовать toObjectId ,

Вы можете преобразовать строку в идентификатор объекта следующим образом:

{
  $lookup: {
    from: 'feeds',
    let: { shareId: '$shares.feed_id' },
    pipeline: [
      { 
        $match: { 
          $expr: {
            $eq: [
              { '$toObjectId': '$$shareId' }, 
              '$_id'
            ] 
          }
        }
      }
    ],
    as: "shared_feeds"
  }
}
...