Как предотвратить дублирование элементов в mongoDB при обновлении элемента

Я создаю многопользовательский словарь, в который несколько пользователей могут добавлять слова. Я не хочу, чтобы одно и то же слово могло иметь дубликаты. Мне удалось предотвратить дубликаты при добавлении слова, но я столкнулся с проблемой предотвращения дубликатов при обновлении слова. Например, в базе данных есть слово «яблоко», допустим, кто-то хотел обновить слово «применить» и случайно написал «яблоко», в таком случае обновленное слово «яблоко» не должно попасть в базу данных, так как там там уже есть такое слово. Пожалуйста помогите.

API слов

const express = require('express');
const router = express.Router();
const Word = require('../../models/Word');
const validateWordInput = require('../../validation/word');
const passport = require('passport');

// @route  GET api/words/test
// @desc   tests words route
// @access Public
router.get('/test', (req, res) => res.json({ msg: 'Words works' }));

// @route  POST api/words
// @desc   Add words to profile
// @access Private
router.post(
  '/',
  passport.authenticate('jwt', { session: false }),
  (req, res) => {
    const { errors, isValid } = validateWordInput(req.body);

    // Check validation
    if (!isValid) {
      // Return any errors
      return res.status(400).json(errors);
    }

    Word.find({}).then(word => {
      if (
        word.filter(
          wrd =>
            wrd.ugrWordCyr.toString().toLowerCase() ===
            req.body.ugrWordCyr.toLowerCase()
        ).length !== 0
      ) {
        return res
          .status(404)
          .json({ wordalreadyexists: 'Word already exists' });
      } else {
        const newWord = new Word({
          user: req.user.id,
          ugrWordCyr: req.body.ugrWordCyr,
          rusTranslation: req.body.rusTranslation,
          example: req.body.example,
          exampleTranslation: req.body.exampleTranslation,
          origin: req.body.origin,
          sphere: req.body.sphere,
          lexis: req.body.lexis,
          grammar: req.body.grammar,
          partOfSpeech: req.body.partOfSpeech,
          style: req.body.style
        });

        newWord.save().then(word => res.json(word));
      }
    });
  }
);

// @route  Put api/words/:id
// @desc   Update a word by id
// @access Private

router.put(
  '/:id',
  passport.authenticate('jwt', { session: false }),
  (req, res) => {
    const { errors, isValid } = validateWordInput(req.body);

    // Check validation
    if (!isValid) {
      // Return any errors
      return res.status(400).json(errors);
    }

    Profile.findOne({ user: req.user.id }).then(profile => {
      Word.findById(req.params.id)
        .then(word => {
          // Check for word owner
          if (word.user.toString() !== req.user.id) {
            return res
              .status(401)
              .json({ notauthorized: 'User not authorized' });
          }

          const wordID = req.params.id;
          const wordInput = req.body;

          // Update
          Word.findByIdAndUpdate(
            { _id: wordID },
            { $set: wordInput },
            { returnOriginal: false },
            (err, word) => {
              if (err) {
                console.info(err);
              }
            }
          ).then(word => {
            res.json(word);
          });
        })
        .catch(err => res.status(404).json({ nowordfound: 'No word found' }));
    });
  }
);

// @route  GET api/words
// @desc   Dislay all words
// @access Public
router.get('/', (req, res) => {
  Word.find()
    .sort({ date: -1 })
    .then(words => res.json(words))
    .catch(err => res.status(404).json({ nonwordsfound: 'No words found' }));
});

//@route  Get api/words/:id
//@desc   Get word by id
//@access Public
router.get('/:id', (req, res) => {
  Word.findById(req.params.id)
    .then(word => res.json(word))
    .catch(err =>
      res.status(404).json({ nonwordfound: 'No word found with that ID' })
    );
});

//@route  DELETE api/words/:id
//@desc   DELETE word
//@access Private

router.delete(
  '/:id',
  passport.authenticate('jwt', { session: false }),
  (req, res) => {
    Profile.findOne({ user: req.user.id }).then(profile => {
      Word.findById(req.params.id)
        .then(word => {
          // Check for post owner
          if (word.user.toString() !== req.user.id) {
            return res
              .status(401)
              .json({ notauthorized: 'User not authorized' });
          }

          // Delete
          word.remove().then(() => res.json({ success: true }));
        })
        .catch(err => res.status(404).json({ postnotfound: 'No post found' }));
    });
  }
);

module.exports = router;

Я использовал следующий код, чтобы предотвратить дублирование при добавлении.

   if (
                word.filter(
                  wrd =>
                    wrd.ugrWordCyr.toString().toLowerCase() ===
                    req.body.ugrWordCyr.toLowerCase()
                ).length !== 0
              ) {
                return res
                  .status(404)
                  .json({ wordalreadyexists: 'Word already exists' });
              } else {
                const newWord = new Word({
                  user: req.user.id,
                  ugrWordCyr: req.body.ugrWordCyr,
                  rusTranslation: req.body.rusTranslation,
                  example: req.body.example,
                  exampleTranslation: req.body.exampleTranslation,
                  origin: req.body.origin,
                  sphere: req.body.sphere,
                  lexis: req.body.lexis,
                  grammar: req.body.grammar,
                  partOfSpeech: req.body.partOfSpeech,
                  style: req.body.style
                });

Какой код я должен написать, чтобы сделать то же самое при обновлении?

Монго может сделать проверку для вас. Когда вы устанавливаете ugrWordCyr как уникальный, mongo вернет ошибку, если вы попытаетесь создать дубликат. См. индексы docs.mongodb.com/manual/core/index-unique

Molda 27.05.2019 09:22

как это связано с React?

Roy.B 27.05.2019 09:22

Я пытался реализовать это с помощью createIndex, но не мог понять, как это работает.

NZMAI 27.05.2019 10:39

создайте индекс и установите уникальный как истинный.

Vikash_Singh 27.05.2019 10:44

Не могли бы вы показать мне, пожалуйста, используя мой код? Я попробую это тогда.

NZMAI 27.05.2019 10:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
43
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Перед выполнением Word.findByIdAndUpdate() вы можете проверить, соответствует ли текст в req.body каким-либо существующим словам в вашей базе данных.

// @route  Put api/words/:id
// @desc   Update a word by id
// @access Private

router.put(
  '/:id',
  passport.authenticate('jwt', { session: false }),
  (req, res) => {
    const { errors, isValid } = validateWordInput(req.body);

    // Check validation
    if (!isValid) {
      // Return any errors
      return res.status(400).json(errors);
    }

    Profile.findOne({ user: req.user.id }).then(profile => {
      Word.findById(req.params.id)
        .then(word => {
          // Check for word owner
          if (word.user.toString() !== req.user.id) {
            return res
              .status(401)
              .json({ notauthorized: 'User not authorized' });
          }

          const wordID = req.params.id;
          const wordInput = req.body;

         //find all words
          Word.find()
              .then((allWords) => {
                  //create an array of strings using each word ("apple", "apricot", ...etc)
                  const wordStrings = allWords.map((word) => word.ugrWordCyr) //not sure if thats the property that has the word-spelling

                  //check if user input already exists in all words
                  if (wordStrings.includes(req.body.ugrWordCyr)){
                       return res.status(400).json({ error : "word already exists" })
                  }

                  // Update
                  Word.findByIdAndUpdate(
                      { _id: wordID },
                      { $set: wordInput },
                      { returnOriginal: false },
                      (err, word) => {
                          if (err) {
                             console.info(err);
                          }
                      }).then(word => {
                           res.json(word);
                      });

               })
               .catch((errors) => {
                   return res.status(400).json({ errors: "could not find any words" })
               })

        })
        .catch(err => res.status(404).json({ nowordfound: 'No word found' }));
    });
  }
);

Кроме того, вы также можете обновить свою модель Word и использовать свойство unique при настройке схемы мангуста. Я предполагаю, что ваша схема выглядит примерно так:

const mongoose = require("mongoose")

    const wordSchema = new mongoose.Schema({
      user: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "User"
      },
      ugrWordCyr: { type: String, unique: true}, <-- set unique to true
      rusTranslation: { type: String },
      example: { type: String },
      exampleTranslation: { type: String },
      origin: { type: String },
      sphere: { type: String },
      lexis: { type: String },
      grammar: { type: String },
      partOfSpeech: { type: String },
      style: { type: String }
    })

const Word = mongoose.model("Word", userSchema)

module.exports = Word

Теперь, когда вы используете findByIdAndUpdate(), он не будет завершен, если вы не передадите новую/уникальную строку в ugrWordCyr или что-то еще, что вы используете в качестве явного слова.

      Word.findByIdAndUpdate(
        { _id: wordID },
        { $set: wordInput },
        { returnOriginal: false },
        (err, word) => {
          if (err) {
            console.info(err);
          }
        }
        ).then(word => {
             res.json(word);
          });
         .catch(err => {
             return res.status(400).json({ error: "could not update word" })
          })
       })

Привет, Кристофер, спасибо за твое предложение, я пробовал, видимо, не работает. Я не уверен, что.

NZMAI 27.05.2019 10:51

@NZMAI, черт возьми, попробуйте вторую часть моего ответа, нам просто нужно изменить вашу пользовательскую модель, чтобы сделать одно из основных свойств в вашей схеме уникальным.

Chris Ngo 27.05.2019 11:09

@NZMAI здорово, добро пожаловать, и это приятно слышать! Не могли бы вы пометить мое решение как правильный ответ :)

Chris Ngo 27.05.2019 11:32

Как я могу сделать его нечувствительным к регистру? Это означает, что если слова совпадают независимо от их падежей, они не должны проникать в БД.

NZMAI 27.05.2019 11:33

@NZMAI Я не думаю, что mongoDB проверяет чувствительность к регистру. Это то, что вы будете обрабатывать на стороне сервера. Что вы можете сделать, так это изменить строку в вашем объекте wordInput после ее определения. Вы можете явно изменить ввод на слово в нижнем регистре. Допустим, ваш объект wordInput выглядит как { ugrWordCyr: "Woof" } Тогда просто используйте wordInput["ugrWordCyr"] = wordInput.ugrWordCyr.toLowerCase() Это заставит желаемое свойство быть строчным, прежде чем мы используем весь объект в findOneAndUpdate()

Chris Ngo 27.05.2019 11:40

Другие вопросы по теме