Alex Ironside
Alex Ironside

Reputation: 5039

Editing a value of an object in an array

I have this object:

{
  "_id" : ObjectId("5a8d83d5d5048f1c9ae877a8"),
  "websites" : [
          "",
          "",
          ""
  ],
  "keys" : [
          {
                  "_id" : ObjectId("5a8d83d5d5048f1c9ae877af"),
                  "name" : "Google",
                  "value" : ""
          },
          {
                  "_id" : ObjectId("5a8d83d5d5048f1c9ae877ae"),
                  "name" : "Built With",
                  "value" : ""
          },
          {
                  "_id" : ObjectId("5a8d83d5d5048f1c9ae877ad"),
                  "name" : "Check Host",
                  "value" : ""
          },
          {
                  "_id" : ObjectId("5a8d83d5d5048f1c9ae877ac"),
                  "name" : "Alexa",
                  "value" : ""
          },
          {
                  "_id" : ObjectId("5a8d83d5d5048f1c9ae877ab"),
                  "name" : "Facebook",
                  "value" : ""
          },
          {
                  "_id" : ObjectId("5a8d83d5d5048f1c9ae877aa"),
                  "name" : "Instagram",
                  "value" : ""
          },
          {
                  "_id" : ObjectId("5a8d83d5d5048f1c9ae877a9"),
                  "name" : "Moz",
                  "value" : ""
          }
  ],
  "username" : "admin@admin",
  "isPremium" : false,
  "accType" : "admin",
  "hash" : "very long hash",
  "salt" : "long salt",
}

Now. Using NodeExpress and Mongoose I need to be able to edit the value field inside of every object inside the keys array.

My GET operation is this:

// GET: /websites/:_id - show edit form
router.get('/keys/edit/:_id', isAdmin, function(req, res, next) {
  // console.log('tada');
  // console.log(req.params._id);

  Account.findOne({ _id: req.user._id }, function(err, user) {
    var selectedKey = findById(user.keys, req.params._id);
    // var keys = user.keys.findOne(req.params._id);
    console.log(selectedKey);
    res.render('admin/edit', {
      title: 'Edit websites',
      user: req.user,
      value: selectedKey.value,
    });
  });
});

How the app works is: The admin logs in. He sees all users and chooses which one he wants to modify, then admin sees all keys. I will attach screenshots to explain it more clearly.

enter image description here

enter image description here

enter image description here

Now. I think I know what I need to do, but I have no clue how to translate it to code.

I think I need to: Find the index of the array element, like in the GET request, update the value with the posted value. I think I need to find the index in the array.

But as I said I have no clue how to do it.

My POST looks like this right now:

// POST: /keys/edit/_id - save updates
router.post('/keys/edit/:_id', isAdmin, function(req, res, next) {
  var p = req.params;
  var b = req.body;
  Account.findOne({ _id: req.user._id }, function(err, user) {
    var selectedKey = findById(user.keys, req.params._id);
    // console.log('Key value: ' + req.body.keyValue);
    // console.log('Selected key: ' + selectedKey);
    console.log('id:' + req.params._id);
    if (err) {
      console.log(err);
    } else {
      console.log(user);
      user.keys.set(req.params._id, req.body.keyValue);
      user.save(err => {
        if (err) {
          console.log(err);
        } else {
          console.log('all good');
        }
        res.redirect('/admin');
      });
    }
  });

EDIT: So I was working on it for a while now and I figured out this. I am using the correct user, I am grabbing the keys array inside, but I don't know how to find the id of the object in the array, which (object) I need to edit.

There is a lot of nesting and this might cause some issues.

EDIT 2: I'm attacking my account model. Forgot about it earlier. Sorry.

var mongoose = require('mongoose');

var website = require('./website');

var plm = require('passport-local-mongoose');

    var accountSchema = new mongoose.Schema({
      isPremium: Boolean,
      accType: String,
      websites: [],
      keys: [
        { name: String, value: String },
        { name: String, value: String },
        { name: String, value: String },
        { name: String, value: String },
        { name: String, value: String },
        { name: String, value: String },
        { name: String, value: String },
      ],
    });

accountSchema.plugin(plm);

module.exports = mongoose.model('Account', accountSchema);

Upvotes: 0

Views: 82

Answers (2)

s7vr
s7vr

Reputation: 75934

You can perform the update atomically using $positional operator.

You include the field (_id) from the keys to locate the index of element and replace the placeholder($) with the found index from query part in the update part to set the value in keys.

 router.post('/keys/edit/:_id', isAdmin, function(req, res, next) {
  var p = req.params;
  var b = req.body;
  Account.findOneAndUpdate(
   {_id: req.user._id,'keys._id':req.params._id }, 
   {$set:{'keys.$.value':req.body.keyValue}},
   {new: true}, 
   function(err, account) {}
);

Upvotes: 1

Cisco
Cisco

Reputation: 22952

The question isn't entirely clear to me what you're looking to do, but what I can infer is that you want to do the following:

You have some object that has an Array of keys that has the following shape:

{
    "_id" : ObjectId("5a8d83d5d5048f1c9ae877af"),
    "name" : "Google",
    "value" : ""
}

Judging from your sample object, I'm inferring the schema is defined something like:

const mongoose = require('mongoose')

const definition = {
  websites: [String],
  keys: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Key'
  }]
}

const accountSchema = new mongoose.Schema(definition)

module.exports = mongoose.model('Account', topicSchema)

By the looks of the route, you want to update/edit that object at the given index: keys[i]. If this is the case, then there is no need to manually traverse the array, update the model directly:

const Key = require('./path/to/models/Key')

router.post('/keys/edit/:id', async (req, res) => {
    const { keyValue } = req.body
    const conditions = { _id: req.params.id }
    await Key.findOneAndUpdate({ id }, { value: keyValue }).exec()
    res.status(201).json()
})

The item in the array will be updated when you query the parent model.

Upvotes: 0

Related Questions