Mongodb Присоединиться к полю _id от String to ObjectId
У меня есть две коллекции
-
Пользователь
{
"_id" : ObjectId("584aac38686860d502929b8b"),
"name" : "John"
}
-
Роль
{
"_id" : ObjectId("584aaca6686860d502929b8d"),
"role" : "Admin",
"userId" : "584aac38686860d502929b8b"
}
Я хочу присоединиться к этой коллекции на основе userId (в коллекции роль) - _id (в пользователе > коллекция).
Я попробовал следующий запрос:
db.role.aggregate(
{
$lookup:
{
from: 'user',
localField: 'userId',
foreignField: '_id',
as: 'output'
}
}
);
Это дает мне ожидаемые результаты, пока я храню userId как ObjectId. Когда мой userId является строкой, результатов нет.
Ps: Я пробовал
foreignField: '_id'.valueOf()
и
foreignField: '_id'.toString()
. Но не удастся совпадения/объединения на основе полей ObjectId.
Любая помощь будет оценена.
Ответы
Ответ 1
Это невозможно с MongoDB 3.4. Эта функция уже была запрошена, но еще не реализована. Вот соответствующие билеты:
На данный момент вам придется хранить userId как ObjectId
РЕДАКТИРОВАТЬ
Предыдущие билеты были исправлены в MongoDB 4.0. Теперь вы можете достичь этого с помощью следующего запроса:
db.user.aggregate([
{
"$project": {
"_id": {
"$toString": "$_id"
}
}
},
{
"$lookup": {
"from": "role",
"localField": "_id",
"foreignField": "userId",
"as": "role"
}
}
])
результат:
[
{
"_id": "584aac38686860d502929b8b",
"role": [
{
"_id": ObjectId("584aaca6686860d502929b8d"),
"role": "Admin",
"userId": "584aac38686860d502929b8b"
}
]
}
]
попробуйте это онлайн: mongoplayground.net/p/JoLPVIb1OLS
Ответ 2
Вы можете использовать агрегацию $toObjectId
из mongodb 4.0, которая преобразует id
String в ObjectId
db.role.aggregate([
{ "$lookup": {
"from": "user",
"let": { "userId": "$_id" },
"pipeline": [
{ "$addFields": { "userId": { "$toObjectId": "$userId" }}},
{ "$match": { "$expr": { "$eq": [ "$userId", "$$userId" ] } } }
],
"as": "output"
}}
])
Или вы можете использовать агрегацию $toString
из mongodb 4.0, которая преобразует ObjectId
в String
db.role.aggregate([
{ "$addFields": { "userId": { "$toString": "$_id" }}},
{ "$lookup": {
"from": "user",
"localField": "userId",
"foreignField": "userId",
"as": "output"
}}
])
Ответ 3
Я думаю, что предыдущий ответ имеет ошибку в случае $ toObjectId. Оператор let
применяется к коллекции БД, в которой вызывается aggregate
функции (т.е. "Роль"), а не к коллекции, на которую указывает "from" (т.е. "Пользователь").
db.role.aggregate([
{ "$lookup": {
"let": { "userObjId": { "$toObjectId": "$userId" } },
"from": "user",
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$userObjId" ] } } }
],
"as": "userDetails"
}}
])
Или же
db.role.aggregate([
{ "$project": { "userObjId": { "$toObjectId": "$userId" } } },
{ "$lookup": {
"localField": "userObjId",
"from": "user",
"foreignField": "$_id",
"as": "userDetails"
}}
])
А также
db.user.aggregate([
{ "$project": { "userStrId": { "$toString": "$_id" }}},
{ "$lookup": {
"localField": "userStrId",
"from": "role",
"foreignField": "userId",
"as": "roleDetails"
}}
])
Ответ 4
На самом деле это возможно в mongodb <4.0.
Вам необходимо добавить новое поле в документ Role, который представляет собой userId, преобразованный в ObjectID:
db.role.aggregate({
{ $addFields: { userobjectid: { $convert: { input: '$userId', to: 'objectId' } },
{ $lookup:
{
from: 'user',
localField: 'userobjectid',
foreignField: '_id',
as: 'output'
}
}
});