Adi Gonnen
Adi Gonnen

Reputation: 113

How to insert a new item to mLab array according to a specific id

I use mLab for my database. I have a collection of lists where each list has an object with _id, userId and an array of items.

Now I want to add a new item to the array of a specific list.

The function that connects to Mongo has the new item object and the list id.

How do I find the correct list and add the new item to items array?

the JSON of each list looks like this:

{
    "_id": {
        "$oid": "5ded5eb7e7179a3015b0672f"
    },
    "userID": "1234",
    "items": [
        {
            "_id": "abc12345",
            "name": "abc",
        },
        {
            "_id": "abc12346",
            "name": "def",
        }
    ]
}

and the function that connects to Mongo looks like this:

function addItem({item, listId}) {
    return MongoService.connect()
    .then(db => {
        const collection = db.collection('lists');
        //need to do something here
    })
}

connect to Mongo : (which works fine, I do connect and load the lists)

function connectToMongo() {
    if (dbConn) return Promise.resolve(dbConn);
    const MongoClient = require('mongodb').MongoClient;
    const url = 'mongodb:// ***my user and password ***';
    return MongoClient.connect(url)
        .then(client => {
            console.log('Connected to MongoDB');
            client.on('close', ()=>{
                console.log('MongoDB Diconnected!');
                dbConn = null;
            })
            dbConn = client.db()
            return dbConn;
        })
}
module.exports = {
    connect : connectToMongo
}

Upvotes: 1

Views: 116

Answers (2)

Tunmise Ogunniyi
Tunmise Ogunniyi

Reputation: 2573

I see you are using the MongoDB node native driver. Documentation exists on how to update a MongoDB document with the driver here.

In order to add an item to list.items array, this is what the addItem function should look like:

function addItem({ item, listId }) {
  return MongoService.connect()
    .then(db => {
      const collection = db.collection('lists');
      // The update query below would return a promise
      return collection.updateOne({
        { _id: new ObjectID(listId) }
      }, { '$push': { items: item } })
    })
    .then(result => { // Anything you want to do with the result })
}

Upvotes: 1

tex
tex

Reputation: 2766

I'd generally reach for a library like Ramda and/or partial.lenses to solve a problem like this. Either of those libraries will simplify working with complicated and/or nested data that often comes from mongodb.

partial.lenses is probably the more powerful option for dealing with complicated, nested data structures, but I'm not sure how to embed it in a snippet here, and Ramda is no slouch in this area, so here's a Ramda example:

const lists = [
  {
    _id: { $oid: '1234' },
    items: []
  },
  {
    _id: { $oid: '2345' },
    items: []
  },
  {
    _id: { $oid: '3456' },
    items: []
  }
]

function addItem({item, listId}) {
  const ix = lists.findIndex(
    ({ _id }) => _id.$oid === listId
  )
  return R.over(R.lensPath([ix, 'items']), R.append(item), lists)
}

console.log(
  addItem({ item: 'hi', listId: '1234' })
)

console.log(
  addItem({ item: 'hi', listId: '3456' })
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

For posterity, here's a link to a working solution based on partial.lenses.

Here's a solution that has no library dependencies. Honestly, it's a bit messy, and if I were not able to use a library, I would spend some time writing a handful of reusable utility functions that would become a part of any actual production solution I wrote. I've done this on previous projects, but the code isn't open source, and I think that exercise is outside the scope of this answer.

I hope this working example will get you moving in the right direction:

const _lists = [
  {
    _id: { $oid: '1234' },
    items: []
  },
  {
    _id: { $oid: '2345' },
    items: []
  },
  {
    _id: { $oid: '3456' },
    items: []
  }
]

const getListById = (listId, lists) =>
  lists.find(({ _id }) => _id.$oid === listId)

const setListById = (listId, newList, lists) =>
  lists.reduce(
    (acc, list, i) =>
      list._id.$oid === listId
        ? (acc.push(newList), acc)
        : (acc.push(list), acc),
    []
  )

function addItem({item, listId}) {
  const oList = getListById(listId, _lists)
  const newList = {
    ...oList,
    items: [...oList.items, item]
  }
  return setListById(listId, newList, _lists)
}

console.log(
  addItem({ item: 'hi', listId: '1234' })
)

console.log(
  addItem({ item: 'hi', listId: '3456' })
)

Also, a caveat: All of these examples assume there will always be a list object matching the provided listId. If that's not a safe assumption, you'll need to add some logic to handle the failure case (i.e. findIndex will return -1 rather than an actual array index and find will returned undefined rather than a list object).

Upvotes: 0

Related Questions