MongoDB - проверить, был ли $ addToSet дубликатом или нет - PullRequest
1 голос
/ 25 января 2020

Я создаю похожую функциональность для моего приложения, используя MongoDB. Недавно я наткнулся на проблему.

Мой код для любителя:

async function like(articleId, userId) {
const db = await makeDb();
return new Promise((resolve, reject) => {
  const result = db.collection(collectionName).findOneAndUpdate(
    { articleId: ObjectID(articleId) },
    {
      $setOnInsert: {
        articleId: ObjectID(articleId),
        creationDate: new Date()
      },
      $addToSet: { likes: ObjectID(userId) }
    },
    { upsert: true }
  );
  resolve(result);
});

Что он делает:

  1. Если в коллекции нет документа, создайте один и заполните все поля
  2. Если документ существует, обновите только массив likes с новым идентификатором пользователя

Это работает, как и ожидалось, и проблем нет, благодаря $addToSe t У меня нет дубликатов и т. Д. c.

Однако после того, как приведенный выше код выполняется и возвращается result, мне нужно сделать некоторые дополнительные вещи, и для его работы мне нужно знать, если Mongos $addToSet что-то изменил.

Короче говоря:

  1. , если в массив likes был добавлен новый userId, я хочу что-то сделать
  2. , если userId уже находится в массиве likes и $addToSet ничего не изменил. Я не хочу предпринимать никаких действий.

Есть ли способ отличить gui sh если $addToSet что-то сделал?

Текущий console.log() результата выглядит так:

* 10 38 *

Единственное, что приходит мне в голову (но очень плохо с точки зрения производительности), - сравнивать массив likes перед обновлением, если он содержит идентификатор пользователя с javascript includes методом.

Есть идеи получше?

1 Ответ

0 голосов
/ 26 января 2020

Я нашел решение!

99% кредита идет на Neil Lunn за этот ответ: { ссылка }

Это привело меня на правильный путь , Я отказался от метода findOneAndUpdate в пользу метода updateOne. Параметры запроса et c. все осталось прежним, изменилось только имя метода и, очевидно, тип результата этого метода.

Проблема метода updateOne заключается в том, что он не возвращает объект, который был обновлен (или старый). Но он возвращает upsertedId, который является единственным, что мне нужно в моей ситуации.

Итак, в итоге вы получаете очень длинный CommandResult объект, который начинается с:

 CommandResult {
  result: { n: 1, nModified: 0, ok: 1 },
  (...)
 }

, но когда я посмотрел очень внимательно, я заметил, что у объекта есть именно те поля, которые Мне понадобилось все время:

  modifiedCount: 0,
  upsertedId: null, // if object was inserted, this field will contain it's ID
  upsertedCount: 0,
  matchedCount: 1 

Так что вам нужно просто взять нужные детали из объекта CommandResult и просто продолжить;)

Мой код после изменения:

async function like(articleId, userId) {
    const db = await makeDb();
    return new Promise((resolve, reject) => {
      db.collection(collectionName)
        .updateOne(
          { articleId: ObjectID(articleId) },
          {
            $setOnInsert: {
              articleId: ObjectID(articleId),
              creationDate: new Date()
            },
            $addToSet: { likes: ObjectID(userId) }
          },
          { upsert: true }
        )
        .then(data => {
          // wrap the content that You need
          const result = {
            upsertedId: data.upsertedId,
            upsertedCount: data.upsertedCount,
            modifiedCount: data.modifiedCount
          };

          resolve(result);
        });
    });
  }

Надеюсь, этот ответ поможет кому-то с подобной проблемой в будущем :)

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