【问题标题】:How to prevent dublicated items in mongoDB when an item is updated更新项目时如何防止mongoDB中的重复项目
【发布时间】:2019-10-12 17:19:33
【问题描述】:

我正在创建一个多用户词典,其中多个用户可以添加单词。我不希望同一个词有重复。我能够在添加单词时防止重复,但在更新单词时使用防止重复的功能。例如,数据库中有一个单词“apple”,假设有人想更新一个单词“apply”并且不小心写了“apple”,在这种情况下,更新的单词“apple”不应该进入数据库,因为有那里已经是这样的词了。请帮我。

Words 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.log(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
                });

在更新时我应该写什么代码来做同样的事情?

【问题讨论】:

  • Mongo 可以为您进行检查。当您将 ugrWordCyr 设置为唯一时,如果您尝试创建副本,mongo 将返回错误。见索引docs.mongodb.com/manual/core/index-unique
  • 这与 React 有什么关系?
  • 我试图用 createIndex 实现它,但不明白它是如何工作的。
  • 创建索引并将唯一设置为真。
  • 你能告诉我,请使用我的代码吗?那我试试看。

标签: node.js reactjs mongodb redux


【解决方案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.log(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.log(err);
          }
        }
        ).then(word => {
             res.json(word);
          });
         .catch(err => {
             return res.status(400).json({ error: "could not update word" })
          })
       })

【讨论】:

  • 嗨,Christopher,感谢您的建议,我已经尝试过了,显然,它不起作用。我不确定是什么。
  • @NZMAI darn,也许试试我回答的第二部分,我们只需要修改您的用户模型,将您的架构中的主要属性之一设置为唯一。
  • @NZMAI 太棒了,不客气,很高兴听到!请考虑将我的解决方案标记为正确答案:)
  • 我怎样才能让它不区分大小写?我的意思是,如果单词匹配,不管它们的大小写它们不应该潜入数据库。
  • @NZMAI 我不认为 mongoDB 检查区分大小写。这是您将在服务器端处理的事情。您可以做的是在定义后修改 wordInput 对象中的字符串。您可以将输入显式更改为小写单词。假设您的 wordInput 对象看起来像 { ugrWordCyr: "Woof" } 然后只需使用 wordInput["ugrWordCyr"] = wordInput.ugrWordCyr.toLowerCase() 这将在我们使用 findOneAndUpdate() 中的整个对象之前强制所需的属性为小写
猜你喜欢
  • 1970-01-01
  • 2013-02-23
  • 1970-01-01
  • 2016-05-13
  • 1970-01-01
  • 1970-01-01
  • 2012-09-25
  • 2019-04-24
  • 2017-09-07
相关资源
最近更新 更多