MongoDB-猫鼬-TypeError:保存不是函数 [英] MongoDB - Mongoose - TypeError: save is not a function

查看:87
本文介绍了MongoDB-猫鼬-TypeError:保存不是函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图通过首先使用.findById获取文档,然后使用新值更新该文档中的字段来对MongoDB文档进行更新(使用猫鼬).我对此还是有点陌生​​,所以我使用了一个教程来弄清楚如何使其工作,然后我就根据自己的需要更新了代码.这是本教程:带有Angular的MEAN应用程序教程4 .原始代码已定义了一个架构,但我的要求是使用通用的MongoDB接口,该接口将简单地接收发送给它的所有负载并将其一起发送到MongoDB.原始教程有这样的内容:

I am attempting to perform an update to a MongoDB document (using mongoose) by first using .findById to get the document, then updating the fields in that document with new values. I am still a bit new to this so I used a tutorial to figure out how to get it working, then I have been updating my code for my needs. Here is the tutorial: MEAN App Tutorial with Angular 4. The original code had a schema defined, but my requirement is for a generic MongoDB interface that will simply take whatever payload is sent to it and send it along to MongoDB. The original tutorial had something like this:

exports.updateTodo = async function(todo){
    var id = todo.id

    try{
        //Find the old Todo Object by the Id

        var oldTodo = await ToDo.findById(id);
    }catch(e){
        throw Error("Error occured while Finding the Todo")
    }

    // If no old Todo Object exists return false
    if(!oldTodo){
        return false;
    }

    console.log(oldTodo)

    //Edit the Todo Object
    oldTodo.title = todo.title
    oldTodo.description = todo.description
    oldTodo.status = todo.status


    console.log(oldTodo)

    try{
        var savedTodo = await oldTodo.save()
        return savedTodo;
    }catch(e){
        throw Error("And Error occured while updating the Todo");
    }
}

但是,由于我不想使用架构并且不想让任何内容通过,所以我不想为特定的字段名称(例如标题,描述,状态等)分配静态值.因此,我想到了这个:

However, since I don't want a schema and want to allow anything through, I don't want to assign static values to specific field names like, title, description, status, etc. So, I came up with this:

exports.updateData = async function(update){
    var id = update.id

    // Check the existence of the query parameters, If they don't exist then assign a default value
    var dbName = update.dbName ? update.dbName : 'test'
    var collection = update.collection ? update.collection : 'testing'; 

    const Test = mongoose.model(dbName, TestSchema, collection);

    try{
        //Find the existing Test object by the Id
        var existingData = await Test.findById(id);
    }catch(e){
        throw Error("Error occurred while finding the Test document - " + e)
    }

    // If no existing Test object exists return false
    if(!existingData){
        return false;
    }

    console.log("Existing document is " + existingData)

    //Edit the Test object
    existingData = JSON.parse(JSON.stringify(update))

    //This was another way to overwrite existing field values, but
    //performs a "shallow copy" so it's not desireable
    //existingData = Object.assign({}, existingData, update)

    //existingData.title = update.title
    //existingData.description = update.description
    //existingData.status = update.status

    console.log("New data is " + existingData)

    try{
        var savedOutput = await existingData.save()
        return savedOutput;
    }catch(e){
        throw Error("An error occurred while updating the Test document - " + e);
    }
}

我最初的问题是,我在获取新值以覆盖旧值时遇到很多问题.现在已经解决了,我得到的错误是"TypeError:existedData.save不是函数".我认为数据类型已更改或某些内容,现在不被接受.当我取消注释旧教程代码中的静态值时,它将起作用.在加入对象之前和之后,控制台日志进一步支持了这一点,因为第一个打印实际数据,第二个打印[对象对象].但是,我似乎无法弄清楚它的期望.任何帮助将不胜感激.

My original problem with this was that I had a lot of issues getting the new values to overwrite the old ones. Now that that's been solved, I am getting the error of "TypeError: existingData.save is not a function". I am thinking the data type changed or something, and now it is not being accepted. When I uncomment the static values that were in the old tutorial code, it works. This is further supported by my console logging before and after I join the objects, because the first one prints the actual data and the second one prints [object Object]. However, I can't seem to figure out what it's expecting. Any help would be greatly appreciated.

编辑:我知道了.显然,Mongoose具有自己的模型"数据类型,如果您通过使用诸如JSON.stringify之类的方法对基础数据进行疯狂的处理,它将被更改.我使用Object.prototype.constructor来找出实际的对象类型,如下所示:

I figured it out. Apparently Mongoose has its own data type of "Model" which gets changed if you do anything crazy to the underlying data by using things like JSON.stringify. I used Object.prototype.constructor to figure out the actual object type like so:

console.log("THIS IS BEFORE: " + existingData.constructor);
existingData = JSON.parse(JSON.stringify(update));
console.log("THIS IS AFTER: " + existingData.constructor);

我明白了:

THIS IS BEFORE: function model(doc, fields, skipId) {
  model.hooks.execPreSync('createModel', doc);
  if (!(this instanceof model)) {
    return new model(doc, fields, skipId);
  }
  Model.call(this, doc, fields, skipId);
}
THIS IS AFTER: function Object() { [native code] }

哪个向我展示了实际情况.我添加了此修复程序:

Which showed me what was actually going on. I added this to fix it:

existingData = new Test(JSON.parse(JSON.stringify(update)));

在相关说明中,我现在可能应该只使用本机MongoDB驱动程序,但是它可以正常工作,所以我现在将其放在待办事项列表中.

On a related note, I should probably just use the native MongoDB driver at this point, but it's working, so I'll just put it on my to do list for now.

推荐答案

您现在已经找到了一个解决方案,但是我建议使用MongoDB驱动程序,它可以使您的代码看起来与此类似并引起原始问题消失:

You've now found a solution but I would suggest using the MongoDB driver which would make your code look something along the lines of this and would make the origional issue disappear:

// MongoDB Settings
const MongoClient = require(`mongodb`).MongoClient;
const mongodb_uri = `mongodb+srv://${REPLACE_mongodb_username}:${REPLACE_mongodb_password}@url-here.gcp.mongodb.net/test`;
const db_name = `test`;
let db; // allows us to reuse the database connection once it is opened

// Open MongoDB Connection
const open_database_connection = async () => {
  try {
    client = await MongoClient.connect(mongodb_uri);
  } catch (err) { throw new Error(err); }
  db = client.db(db_name);
};


exports.updateData = async update => {

  // open database connection if it isn't already open
  try {
    if (!db) await open_database_connection();
  } catch (err) { throw new Error(err); }


  // update document
  let savedOutput;
  try {
    savedOutput = await db.collection(`testing`).updateOne( // .save() is being depreciated
      { // filter
        _id: update.id // the '_id' might need to be 'id' depending on how you have set your collection up, usually it is '_id'
      },
      $set: { // I've assumed that you are overwriting the fields you are updating hence the '$set' operator
        update // update here - this is assuming that the update object only contains fields that should be updated
      }

      // If you want to add a new document if the id isn't  found add the below line
      // ,{ upsert: true }

    );
  } catch (err) { throw new Error(`An error occurred while updating the Test document - ${err}`); }


  if (savedOutput.matchedCount !== 1) return false; // if you add in '{ upsert: true }' above, then remove this line as it will create a new document

  return savedOutput;
}

需要在此代码之前创建集合testing,但这只是一次性的事情,而且非常简单-如果您使用的是MongoDB Atlas,则可以使用MongoDB Compass/进入在线管理员来创建没有一行代码的集合...

The collection testing would need to be created before this code but this is only a one-time thing and is very easy - if you are using MongoDB Atlas then you can use MongoDB Compass / go in your online admin to create the collection without a single line of code...

据我所知,您应该需要复制update对象.上面的方法将数据库调用从2减少到1,并允许您在应用程序中的其他任何地方重用数据库连接,这将有助于加快处理速度.另外,请勿将您的MongoDB凭据直接存储在代码中.

As far as I can see you should need to duplicate the update object. The above reduces the database calls from 2 to one and allows you to reuse the database connection, potentially anywhere else in the application which would help to speed things up. Also don't store your MongoDB credentials directly in the code.

这篇关于MongoDB-猫鼬-TypeError:保存不是函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆