Search code examples
rethinkdbreal-time-updatesrethinkdb-javascript

RethinkDB: Realtime Changes On Array, return only newly appended


Brief: I'm using RethinkDB's change feeds to watch changes on a specific document (not the entire table). Each record looks like this:

{
  "feedback": [ ],
  "id":  "bd808f27-bf20-4286-b287-e2816f46d434" ,
  "projectID":  "7cec5dd0-bf28-4858-ac0f-8a022ba6a57e" ,
  "timestamp": Tue Aug 25 2015 19:48:18 GMT+00:00
}

I have one process that is appending items to the feedback array, and another process that needs to watch for changes on the feedback array... and then do something (specifically, broadcast only the last item appended to feedback via websockets). I've got it wired up so that it will monitor updates to the entire document - however, it requires receiving the complete document, then getting just the last item in the feedback array. This feels overly heavy, when all I need to get back is the last thing added.

Current code used to update the document:

r.table('myTable').get(uuid)
.update({feedback:r.row('feedback').append('message 1')})
.run(conn, callback)(...}

^ That will run several times over the course of a minute or so, adding the latest message to 'feedback'.

Watching changes:

r.table('myTable').get(uuid)
.changes()
.run(conn, function(err, cursor){
  cursor.each(function(err, row){
    var indexLast = row.old_val ? row.old_val.feedback.length : 0,
        nextItem = row.new_val.feedback[indexLast];
    // ... do something with nextItem
  })
})

Finally, here's the question (2 parts really):

1: When I'm updating the document (adding to feedback), do I have to run an update on the entire document (as in my code above), or is it possible to simply append to the feedback array and be done with it?

2: Is the way I'm doing it above (receiving the entire document and plucking the last element from feedback array) the only way to do this? Or can I do something like:

r.table('myTable').get(uuid)
.changes()
.pluck('feedback').slice(8) // <- kicking my ass
.run(conn, function(err, cursor){
  cursor.each(function(err, row){
    var indexLast = row.old_val ? row.old_val.feedback.length : 0,
        nextItem = row.new_val.feedback[indexLast];
    // ... do something with nextItem
  })
})

Solution

  • Let's go over your questions

    1: When I'm updating the document (adding to feedback), do I have to run an update on the entire document (as in my code above),

    No, you don't. As you did, you only update feedback field. Not an entirely document, doesn't it?

    or is it possible to simply append to the feedback array and be done with it?

    It's possible. And you already do it.

    The way it writes looks like your client driver has to fetch content of feedback array, then append a new element, and update the whole content back. But that's not the case here. The whole query r.row('feedback').append('message 1') is serialized as a JSON string and pass to the RethinkDB. RethinkDB run it, atomically, on the server. The content of feedback and the appending isn't done on client nor sending back to server.

    If you used tcpdump like this:

    tcpdump -nl -w - -i lo0 -c 500 port 28015|strings
    

    You can see this JSON string is sender to RethinkDB server when you run your query:

    [1,[53,[[16,[[15,["myTable"]],1]],[69,[[2,[3]],{"feedback":[29,[[170,[[10,[3]],"feedback"]],"doc2"]]}]]]],{}]
    

    Yes, that single JSON query was transmited over network, not the whole document. Hope it makes sense. More information of that JSON string can be found on http://rethinkdb.com/docs/writing-drivers/ and https://github.com/neumino/rethinkdbdash/blob/master/lib/protodef.js#L84

    2: Is the way I'm doing it above (receiving the entire document and plucking the last element from feedback array) the only way to do this? Or can I do something like:

    Ideally we will want to use bracket to get a field of document, and listen for change on that field. Unfortunately it doesn't work yet. So we have to use map to transform it. Again, this run on server and only the last document transmit to client, not the whole document:

    r.table('myTable').get(1).changes().map(function(doc) {
      return doc('new_val')('feedback').default([]).nth(-1)
    })