有一种方法可以在MongoDB中强制执行参照完整性

时间:2019-06-01 00:17:45

标签: node.js mongodb mongoose referential-integrity

当您搜索与Mongo-DB相关的参照完整性时,标准响应为“ MongoDB不支持此功能”。标准解释是MongoDB支持引用和填充,但是没有什么可以阻止您将引用更改为无效值。对于许多来自SQL背景的开发人员来说,这是一个主要的威慑力量。

2 个答案:

答案 0 :(得分:0)

有解决方案!

当然-无法阻止某人通过Compass或另一个GUI更改引用,但是当您开发自己的API并控制对数据库的所有读写操作时,绝对有一种方法可以做到这一点。 / p>

我将用我目前正在处理的一些代码的示例进行说明。首先,UnitType.js定义了一个非常简单的集合,其中仅包含一个名称和一个ID。魔术出在Unit.js中-看看unittype字段的自定义验证器。它向UnitType模型发出请求,以通过其_id字段查找相关的UnitType。如果保存或更新时提供的ID无效,则会返回一条错误消息“ Invalid Object ID”,否则将保存记录。这样,永远不可能用无效的UnitType记录创建或修改记录。

如果有多个引用,则可以重复此过程。

如果您在整个应用程序中使用的是相同模型,则无需编写其他代码来支持参照完整性

我希望这对您有帮助

// UnitType.js - MongoDB Schema
const mongoose = require('mongoose')
const UnitType = mongoose.model(
  'UnitType',
  new mongoose.Schema(
    {
      name: {
        type: String,
        required: true,
        minlength: 5,
        maxlength: 40
      }
    },
    { collection: 'unittype' }
  )
)

exports.UnitType = UnitType

// Unit.js - MongoDB Schema
const mongoose = require('mongoose')
const { UnitType } = require('./Unittype')
const Unit = mongoose.model(
  'Unit',
  new mongoose.Schema(
    {
      door: {
        type: String,
        required: true,
        minlength: 2,
        maxlength: 10,
        index: true,
        unique: true
      },
      name: {
        type: String,
        required: true,
        minlength: 5,
        maxlength: 40
      },
      location: {
        type: String
      },
      description: {
        type: String
      },
      unittype: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'UnitType',
        // Ensure that UnitType iD is valid (note isAsync is deprecated)
        validate: {
          validator: async function(v) {
            return await UnitType.findById(v, (err, rec) => rec !== null)
          },
          message: 'Invalid Object ID'
        }
      }
    },
    { collection: 'unit' }
  )
)

exports.Unit = Unit



答案 1 :(得分:0)

关于删除的引用完整性,如果所有删除请求均由您的应用程序提供,则可以通过在删除记录之前检查相关集合中是否存在ID来处理该删除请求。我这样做如下

CRUD操作(我们在这里只关心Delete-请注意我如何传递一组对象,这些对象是需要与要删除的文档ID(记录)相匹配的集合和字段

const express = require('express')
const router = express.Router()
const iflexCRUD = require('../../lib/iflexCRUD')

const { UnitType } = require('../../models/Unittype')
const { Unit } = require('../../models/Unit')

iflexCRUD.create(router, '/', UnitType)
iflexCRUD.read(router, '/', UnitType, { sort: 'name' })
iflexCRUD.update(router, '/:id', UnitType)
iflexCRUD.deleteByID(router, '/:id', UnitType, [
  {
    model: Unit,
    field: 'unittype'
  }
])
iflexCRUD.delete(router, '/unittype/:unittype', UnitType)

module.exports = router

CRUD删除处理程序 这是我用于CRUD操作的通用删除请求处理程序 我传递了一个“集合/字段”值数组,并检查是否有一条记录与要删除的文档的ID相匹配。

// CRUD-DELETE
iflexCRUD.deleteByID = (router, route, Collection, refs = []) => {
  router.delete(route, async (req, res) => {
    try {
      let exception = false
      //Enforce Referential Integrity for deletes - Deny when ID is used in any of refs collections
      //Loop through any referenced files (first record) to ensure there are no other collections using this document
      for (let i = 0; i < refs.length; i++) {
        if (!exception) {
          let refObj = {}
          refObj[refs[0].field] = req.params.id
          const result = await refs[i].model.findOne(refObj, (err, rec) => {})
          exception = result !== null
        }
      }
      // Process deletion of there are no exceptions
      if (!exception) {
        const doc = await Collection.deleteOne({ _id: req.params.id })
        res.send(doc)
      } else {
        return res
          .status(401)
          .json(
            'Document is already use in related collection  - it cannot Delete!'
          )
      }
    } catch (e) {
      return res.status(401).json(e.message)
    }
  })
}