Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RethinkDB - Updating nested array

I have a survey table that looks like so:

{
  id: Id,
  date: Date,
  clients: [{
    client_id: Id,
    contacts: [{
      contact_id: Id,
      score: Number,
      feedback: String,
      email: String
    }]
  }]
}

I need to updated the score and feedback fields under a specific contact. Currently, I am running the update like this:

function saveScore(obj){
  var dfd = q.defer();
  var survey = surveys.get(obj.survey_id);

  survey 
    .pluck({ clients: 'contacts' })
    .run()
    .then(results => {

      results.clients.forEach((item, outerIndex) => {
        item.contacts.forEach((item, index, array) => {
          if(Number(item.contact_id) === Number(obj.contact_id)) {
            array[index].score = obj.score;
            console.log(outerIndex, index);
          }
        });
      });

      return survey.update(results).run()
    })
    .then(results => dfd.resolve(results))
    .catch(err => dfd.resolve(err));

  return dfd.promise;
};

When I look at the update method, it specifies how to update nested key:value pairs. However, I can't find any examples to update an individual item in an array.

Is there a better and hopefully cleaner way to update items in a nested array?

like image 913
Jacob Turner Avatar asked May 15 '15 17:05

Jacob Turner


2 Answers

You might need to get the array, filter out the desired value in the array and then append it again to the array. Then you can pass the updated array to the update method.

Example

Let's say you have a document with two clients that both have a name and a score and you want to update the score in one of them:

{
  "clients": [
    {
      "name":  "jacob" ,
      "score": 200
    } ,
    {
      "name":  "jorge" ,
      "score": 57
    }
  ] ,
  "id":  "70589f08-284c-495a-b089-005812ec589f"
}

You can get that specific document, run the update command with an annonymous function and then pass in the new, updated array into the clients property.

r.table('jacob').get("70589f08-284c-495a-b089-005812ec589f")
  .update(function (row) {
    return {
      // Get all the clients, expect the one we want to update
      clients: row('clients').filter(function (client) {
        return client('name').ne('jorge')
      })
      // Append a new client, with the update information
      .append({ name: 'jorge', score: 57 })
    };
  });

I do think this is a bit cumbersome and there's probably a nicer, more elegant way of doing this, but this should solve your problem.

Database Schema

Maybe it's worth it to create a contacts table for all your contacts and then do a some sort of join on you data. Then your contacts property in your clients array would look something like:

{
  id: Id,
  date: Date,
  clients: [{
    client_id: Id,
    contact_scores: {
      Id: score(Number)
    },
    contact_feedbacks: {
      Id: feedback(String)
    }
  }]
}
like image 138
Jorge Silva Avatar answered Sep 26 '22 02:09

Jorge Silva


it works for me

r.table(...).get(...).update({
contacts: r.row('Contacts').changeAt(0,
  r.row('Contacts').nth(0).merge({feedback: "NICE"}))
 })
like image 42
DEO Avatar answered Sep 23 '22 02:09

DEO