简体   繁体   中英

CRUD on an array inside an object in MongoDB

I'm using MongoDB for the first time. I'm used to SQL and didn't shift my thinking to NoSQL yet. This is my model:

var mongoose = require('mongoose');

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

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

var accountSchema = new mongoose.Schema({
  isPremium: Boolean,
  websites: [],
});

accountSchema.plugin(plm);

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

Now. I know how to add, edit and remove an account . But I have no clue how to edit the websites array content. This is an object after registration:

{
  _id: ObjectId('5a7fa2af53f3d71dcd28ccb1'),
  websites: ['websiteonefree.com', 'websitetwofree.com', 'webs'],
  username: 'free@free.com',
  isPremium: false,
  hash: 'Very long hash',
  salt: 'long salt',
  __v: 0,
}

The websites are being displayed in a table like this:

<table class="table table-striped table-hover">
  <tr>
    <th>Url</th>
    <% if (user) { %>
      <th>Actions</th>
      <% } %>
  </tr>
  <% for(let i=0;i<websites.length;i++){ let website = websites[i] %>
    <tr>
      <td>
        <%= website %>
      </td>
      <td>
        <a href="/websites/<%= website %>" class="btn btn-info">Edit</a>
        <a href="/websites/delete/<%= website %>" class="btn btn-danger confirmation">Delete</a>
      </td>
    </tr>
    <% } %>
</table>

Right now the edit function looks like this:

// POST: /websites/_id - save updates
router.post('/:_id', isLoggedIn, function(req, res, next) {
  var p = req.params;
  var b = req.body;
  var website = Website({
    _id: p._id,
    url: b.url,
  });

  Website.update(
    {
      _id: p._id,
    },
    website,
    function(err) {
      if (err) {
        throwError();
        return;
      }
      res.redirect('/websites');
    }
  );
});

This works if the websites are a collection of their own. Now I was thinking to do something like, sending the :_id and website at the Edit button click. Then I would query the db, put it into the view and then update it all.

The only problems are:

a) I have no idea how to implement it in code.

b) I'm not sure how to determine the position in the array, which I need to update. Example: websites[0]='a.com' so how to make sure it updates the right element of the array.

Basically: In SQL I would just edit it using the foreign key. How do I do it with NoSQL ?

You can do with this the mongoDB aggregation queries https://docs.mongodb.com/manual/reference/operator/update/positional/

Note: Make sure you have compatible version of mongodb

Update all documents in array

db.coll.update({}, {$set: {“a.$[].b”: 2}})
Input: {a: [{b: 0}, {b: 1}]}
Output: {a: [{b: 2}, {b: 2}]}

Update all matching documents in array

db.coll.update({}, {$set: {“a.$[i].b”: 2}}, {arrayFilters: [{“i.b”: 0}]})
Input: {a: [{b: 0}, {b: 1}]}
Output: {a: [{b: 2}, {b: 1}]}

Update all matching scalars in array

db.coll.update({}, {$set: {“a.$[i]”: 2}}, {arrayFilters: [{i: 0}]})
Input: {a: [0, 1]}
Output: {a: [2, 1]}

Update all matching documents in nested array

db.coll.update({}, {$set: {“a.$[i].c.$[j].d”: 2}}, {arrayFilters: [{“i.b”: 0}, {“j.d”: 0}]})
Input: {a: [{b: 0, c: [{d: 0}, {d: 1}]}, {b: 1, c: [{d: 0}, {d: 1}]}]}
Output: {a: [{b: 0, c: [{d: 2}, {d: 1}]}, {b: 1, c: [{d: 0}, {d: 1}]}]}

Update all scalars in array matching a logical predicate

db.coll.update({}, {$set: {“a.$[i]”: 2}}, {arrayFilters: [{$or: [{i: 0}, {i: 3}]}]})
Input: {a: [0, 1, 3]}
Output: {a: [2, 1, 2]}

Each array filter must be a predicate over a document with a single field name. Each array filter must be used in the update expression, and each array filter identifier $[] must have a corresponding array filter. must begin with a lowercase letter and not contain any special characters. There must not be two array filters with the same field name.

I ended up using findById and save .

The add POST method looks like this.

// POST handler for add to process the form
router.post('/add', isLoggedIn, function(req, res, next) {
  var user = req.user;
  var websites = user.websites;
  var filledWebsites = findFilledWebsites(user);
  console.log(websites);
  if (
    (user.isPremium == true && filledWebsites.length < 5) ||
    (user.isPremium == false && filledWebsites.length < 3)
  ) {
    let freeIndexes = [];
    for (let i = 0; i < websites.length; i++) {
      if (websites[i] == '') {
        freeIndexes.push(i);
      }
    }
    Account.findById(req.user._id, function(err, acc) {
      if (err) {
        console.log(err);
      } else {
        acc.websites.set(freeIndexes[0], req.body.url);
        acc.save((err, webs) => {
          if (err) {
            console.log(err);
          } else {
            console.log('Website Added');
            res.redirect('/websites');
          }
        });
      }
    });
  } else {
    console.log('failed');
    res.redirect('/websites');
  }
});

Here's the findFilledWebsites method:

function findFilledWebsites(acc) {
  var filledWebsites = [];
  for (let i = 0; i < acc.websites.length; i++) {
    if (acc.websites[i] != '') {
      filledWebsites.push(acc.websites[i]);
    }
  }
  return filledWebsites;
}

The add method can be easily edited to do the rest of the CRUD operations. In this case they are all updating, because empty records are filtered in the view.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM