Вопрос или проблема
В настоящее время я создаю папку входящих сообщений в приложении для обмена сообщениями. Для этого я хотел бы извлечь все уникальные идентификаторы пользователей (ObjectID), которые я сохранил в своей схеме сообщений, чтобы увидеть все уникальные разговоры, которые ведет пользователь. Проблема в том, что идентификаторы хранятся в двух отдельных полях: “to” и “from”. Есть ли способ получитьdistinct значения из обоих полей? (т.е. если userA есть как в поле to, так и в поле from, он будет извлечен только один раз)
В настоящее время я пытался использовать как $setUnion, так и $addToSet без успеха.
Моя схема сообщений выглядит следующим образом
const MessageSchema = new Schema(
{
message: { type: String, required: true, maxLength: 128 },
image: { type: String },
from: { type: Schema.Types.ObjectId, required: true, ref: "User" },
to: [
{
type: Schema.Types.ObjectId,
required: true,
refPath: "docModel",
},
],
docModel: { type: String, required: true, enum: ["Group", "User"] },
},
{ timestamps: true },
);
Вот несколько примеров документов:
[
{
"_id": "671175112a0bcfdb66f833fc",
"message": "hi",
"from": "6706c5576692b95bb39dabae",
"to": [
"6708368712a817cd73810b5e"
],
"docModel": "User",
"createdAt": "2024-10-17T20:35:29.164Z",
"updatedAt": "2024-10-17T20:35:29.164Z",
"__v": 0
},
{
"_id": "6711755e2a0bcfdb66f83406",
"message": "bye",
"from": "671175392a0bcfdb66f83401",
"to": [
"6706c5576692b95bb39dabae"
],
"docModel": "User",
"createdAt": "2024-10-17T20:36:46.259Z",
"updatedAt": "2024-10-17T20:36:46.259Z",
"__v": 0
},
{
"_id": "671181f7c2db995b7ef3f976",
"message": "1",
"from": "6706c5576692b95bb39dabae",
"to": [
"671175392a0bcfdb66f83401"
],
"docModel": "User",
"createdAt": "2024-10-17T21:30:31.032Z",
"updatedAt": "2024-10-17T21:30:31.032Z",
"__v": 0
}
]
В приведенном выше примере результат, который я хочу получить, – это массив:
[6706c5576692b95bb39dabae, 671175392a0bcfdb66f83401, 6708368712a817cd73810b5e]
При этом это уникальные значения, которые появляются или в поле from, или в поле to (и если они появляются и в поле from, и в поле to, они добавляются в массив только один раз)
А вот мой код для запроса к MongoDB. Я пытаюсь запросить все документы, где идентификатор пользователя появляется либо в “to”, либо в “from”, что работает. Затем я пытаюсь получить уникальные значения двух полей вместе, что не работает.
Я планирую удалить идентификатор пользователя запрашивающего пользователя, как только я извлеку все уникальные идентификаторы, чтобы получить окончательный список уникальных разговоров.
const uniqueUsers = await Message.aggregate([
{
$match: {
$or: [
{
from: new mongoose.Types.ObjectId(`${req.user.id}`),
},
{
to: {
$in: [
new mongoose.Types.ObjectId(`${req.user.id}`),
],
},
},
],
},
},
{
$group: {
_id: null,
unique: { $setUnion: ["$from", "$to"] },
},
},
]);
На этапе группировки вам нужно будет $addToSet
добавить все идентификаторы из полей from
и to
в 2 массива соответственно. После этого выполните $setUnion
для комбинирования 2 массивов вместе.
db.collection.aggregate([
{
"$match": {
$expr: {
$let: {
vars: {
userIdInput: "6706c5576692b95bb39dabae"
},
"in": {
"$or": [
{
$eq: [
"$$userIdInput",
"$from"
]
},
{
$in: [
"$$userIdInput",
"$to"
]
}
]
}
}
}
}
},
{
"$unwind": "$to"
},
{
"$group": {
"_id": null,
"froms": {
"$addToSet": "$from"
},
"tos": {
"$addToSet": "$to"
}
}
},
{
"$project": {
allIds: {
"$setUnion": [
"$froms",
"$tos"
]
}
}
}
])
Ответ или решение
Чтобы получить уникальные значения из двух полей в MongoDB (Mongoose), когда вы работаете с схемой сообщений, вы должны использовать агрегацию. Запрос должен эффективно обрабатывать значки пользователей, хранящиеся в полях "from" и "to". Ваша цель — извлечь уникальные идентификаторы пользователей в формате ObjectId
, присутствующие в обеих колонках.
Ваша схема сообщений выглядит следующим образом:
const MessageSchema = new Schema(
{
message: { type: String, required: true, maxLength: 128 },
image: { type: String },
from: { type: Schema.Types.ObjectId, required: true, ref: "User" },
to: [
{
type: Schema.Types.ObjectId,
required: true,
refPath: "docModel",
},
],
docModel: { type: String, required: true, enum: ["Group", "User"] },
},
{ timestamps: true },
);
Допустим, у вас есть следующие документы:
[
{
"_id": "671175112a0bcfdb66f833fc",
"message": "hi",
"from": "6706c5576692b95bb39dabae",
"to": ["6708368712a817cd73810b5e"],
"docModel": "User",
},
{
"_id": "6711755e2a0bcfdb66f83406",
"message": "bye",
"from": "671175392a0bcfdb66f83401",
"to": ["6706c5576692b95bb39dabae"],
"docModel": "User",
},
{
"_id": "671181f7c2db995b7ef3f976",
"message": "1",
"from": "6706c5576692b95bb39dabae",
"to": ["671175392a0bcfdb66f83401"],
"docModel": "User",
}
]
Вы хотите собрать все уникальные идентификаторы, чтобы получить результат в виде:
["6706c5576692b95bb39dabae", "671175392a0bcfdb66f83401", "6708368712a817cd73810b5e"]
Решение через агрегацию
Вам нужно создать агрегированный запрос, чтобы сначала сопоставить сообщения, а затем объединить уникальные ObjectId
из обоих полей. Вот как вы можете это сделать:
const uniqueUsers = await Message.aggregate([
{
$match: {
$or: [
{ from: mongoose.Types.ObjectId(req.user.id) },
{ to: mongoose.Types.ObjectId(req.user.id) }
]
}
},
{
$unwind: "$to" // Разворачиваем массив to в отдельные документы
},
{
$group: {
_id: null,
froms: { $addToSet: "$from" }, // Собираем уникальные ids из поля from
tos: { $addToSet: "$to" } // Собираем уникальные ids из поля to
}
},
{
$project: {
allIds: {
$setUnion: ["$froms", "$tos"] // Объединяем уникальные идентификаторы в один массив
}
}
}
]);
Пояснение этапов агрегации:
-
$match: Начинаем с фильтрации сообщений, чтобы получить только те, которые связаны с текущим пользователем, находящимся в полях "from" или "to".
-
$unwind: Разворачиваем массив "to", чтобы каждый элемент этого массива стал отдельным документом.
-
$group: Группируем результаты, собирая уникальные значения из полей "from" и "to" в два разных массива:
froms
иtos
. -
$project: Используем
setUnion
, чтобы объединить массивыfroms
иtos
в один, содержащий уникальные идентификаторы пользователей.
Заключение
Таким образом, с помощью агрегации в MongoDB (Mongoose) вы можете эффективно получить все уникальные идентификаторы пользователей, участвующих в переписке, сохраняя их только один раз, даже если они встречаются в обоих полях. Этот метод оптимален для получения информации о пользователях и может быть использован для дальнейшей обработки данных, таких как визуализация переписки в вашем мессенджере.