3

I'm having a rather large amount of difficulty with trying to remove nested objects from my table, without accidentally deleting all my data in the process (happened three times now, thank god I made copies).

My Object:

{
  "value1": thing,
  "value2": thing,
  "value3": thing,
  "roles": {
    "1": {
      "name": "Dave",
      "id": "1"
    },
    "2": {
      "name": "Jeff",
      "id": "2"
    },
    "3": {
      "name": "Rick",
      "id": "3"
    },
    "4": {
      "name": "Red",
      "id": "4"
    }
  }
}`

I've tried a number of rethink queries, but none have worked thus far. It should be noted that 1, 2, 3, & 4 are variables that can have any amount of numbers, and thus my query must reflect that.

Some attempted queries:

function removeRole(id, roleName) {
        let role = `${roleName}`
        return this.r.table('guilds').get(id).replace(function(s){
            return s.without({roles : {[role] : { "name": role }}})
        })
    }
function removeRole(id, roleName) {
        return this.r.table('guilds').getAll(id).filter(this.r.replace(this.r.row.without(roleName))).run()
    }
function removeRole(id, roleName) {
        return this.r.table('guilds').get(id)('roles')(roleName).delete()
    }

Any assistance is greatly appreciated, and if the question has issues, please let me know. Still rather new to this so feedback is appreciated.

Lyubomyr Shaydariv
  • 20,327
  • 12
  • 64
  • 105
Laz
  • 63
  • 1
  • 5

1 Answers1

1

I'm not sure if I understood your intention, but the following query seems to do what you're trying to accomplish:

r.db('test')
  .table('test')
  .get(id)
  .replace((doc) => {
    // This expression makes sure that we delete the specified keys only
    const roleKeys = doc
      .getField('roles')
      .values()
      // Make sure we have a role name is in the names array
      .filter(role => r.expr(names).contains(role.getField('name')))
      // This is a bit tricky, and I believe I implemented this in a not efficient
      // way probably missing a first-class RethinkDB expression that supports
      // such a case out of box. Since we are going to delete by nested dynamic
      // ids, RethinkDB requires special syntax to denote nested ids:
      //     {roles: {ID_1: true, ID_2: true}}
      // Well, this is just a JavaScript syntax workaround, so we're building
      // such an object dynamically using fold.
      .fold({}, (acc, role) => acc.merge(r.object(role.getField('id'), true)));
    return doc.without({roles: roleKeys});
  })

For example, if names is an array, say ['Jeff', 'Rick'], the nested roleKeys expession will be dynamically evaluated into:

{2: true, 3: true}

that is merged into the roles selector, and the above query will transform the document as follows:

{
  "value1": ...,
  "value2": ...,
  "value3": ...,
  "roles": {
    "1": {"name": "Dave", "id": "1"},
    "4": {"name": "Red", "id": "4"}
  }
}
Lyubomyr Shaydariv
  • 20,327
  • 12
  • 64
  • 105
  • Worked like a charm, thanks a bunch. Saved me another set of stress-filled hours today. If I may, why is this method inefficient? It seemed to work perfectly as intended with little room for error that I can see. – Laz Apr 25 '18 at 10:39
  • 1
    @Laz It's nice to hear that I could save some time for you, thank you too! Actually, I'm not really sure if the `roleKeys` expression can be re-written in a more simple way with some RethinkDB built-ins that can do exactly what I implemented in my answer. I would say that a chain of `values`, `filter`, and `fold`+`merge`+`object` looks somewhat clunky to me. If you find a nicer way getting rid of the chain, please feel free to modify my answer. – Lyubomyr Shaydariv Apr 25 '18 at 11:15