There is a way to enforce referential integrity in MongoDB [duplicate]

守給你的承諾、 提交于 2019-12-24 22:16:14

问题


When you search for referential integrity in relation to Mongo-DB the standard response is "MongoDB does not support this". The standard explanation is that MongoDB supports refs and populate, however there is nothing that prevents you changing the ref to an invalid value. This is a major deterrent for many developers coming from a SQL background.


回答1:


THERE IS A SOLUTION!

Sure - there is no way to prevent someone changing a reference through Compass or another GUI but there is definitely a way to do it when you are developing your own API and in control of all read and write operations to the database.

I will illustrate with an example from some code I am currently working on. Firstly the UnitType.js defines a very simple collection with simply a name and an ID. The magic is in Unit.js - Have a look at the custom validator for unittype field. It issues an asynch request to the UnitType model to find the related UnitType by its _id field. If the ID provided on save or update is invalid, an error message will be returned "Invalid Object ID" otherwise record will be saved. In this way it is never possible to create or modify a record with an invalid UnitType record.

This process can be repeated if there are multiple references.

Provided you are using the same model throughout your application, there is no need to write additional code to support referential integrity

I hope this helps

// 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






回答2:


In relation to referential integrity on deletes, provided all delete requests are served by your application then it can be handled by checking ID does not exists in related collections prior to deleting records. I do this as follows

CRUD Operations (We are only concerned with Delete here - note how I am passing an array of objects being the collection and field that needs to be matched against ID of document (record) we are deleting

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 Delete Handler This is a generic delete request handler that I use for CRUD operations I passes an array of Collection / Field values and checks to see if there is a single record that matches the ID of the document being deleted.

// 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)
    }
  })
}


来源:https://stackoverflow.com/questions/56402942/there-is-a-way-to-enforce-referential-integrity-in-mongodb

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!