Cédric
Cédric

Reputation: 411

RethinkDB replace document but not specific property

I would like to apply in one query a document replacement but i would like to keep a specific property (createdAt) from the old document.

The old document :

{id: "xxx" title: "my title", description: "my desc", createdAt: "1507844981006"}

The new document

{id: "xxx" title: "my title2", description: "my desc2", createdAt: "1507844981006"}

The constraints are :

My initial query look like this

const data = {id: "xx"....};

r.table('mytable')
 .get(id)
 .replace((row) => {

    return r.expr(data).merge(row.pluck('createdAt'))

 }
 .run()

Does anybody have a suggestion to perform this ?

Thanks in advance

Upvotes: 0

Views: 176

Answers (1)

Lyubomyr Shaydariv
Lyubomyr Shaydariv

Reputation: 21105

i want to perform a replacement and not an update to apply a document insertion, in case the document do not exists.

What you're looking for is "upsert" (or "replsert" if we can name it like this in RethinkDB), and as far as I know, there is no such an operation in RethinkDB. .replace() requires at least a document to be found therefore it cannot do insert. However, it can be easily implemented and can be flavored with ReQL spices as well.

The example below can be tested directly in Data Explorer:

const OLD_DOCUMENT = { id: 'xxx', title: 'my title', description: 'my desc', createdAt: '1507844981006' };
const NEW_DOCUMENT = { id: 'xxx', title: 'my title2', description: 'my desc2' };

r.db('TEST')
  .table('TEST')
  .delete();

// Comment out the following query to test the .insert() scenario,
// or uncomment it to test the .replace() scenario
r.db('TEST')
  .table('TEST')
  .insert(OLD_DOCUMENT);

r.db('TEST')
  .table('TEST')
  .replace((doc) => r.expr(NEW_DOCUMENT).merge(doc.pluck('createdAt')))
  .do(replaceData => r.branch(
    // Did the .replace() operation succeed with a real replacement?
    replaceData.getField('replaced').gt(0),
    // Then just return the replace result
    replaceData,
    // Otherwise, perform an insert
    r.db('TEST')
      .table('TEST')
      .insert(
        // Bonus (not just .insert(NEW_DOCUMENT))
        r.branch(
          // Does the document have the createdAt property set?
          r.expr(NEW_DOCUMENT).hasFields('createdAt'),
          // If yes, then no transformation is required
          NEW_DOCUMENT,
          // Else add the timestamp in your format
          r.expr(NEW_DOCUMENT).merge({ createdAt: r.now().toEpochTime().mul(1000).coerceTo('string') })
        )
      )
));

r.db('TEST')
  .table('TEST');

.insert() scenario results

{
  "createdAt": "1507890036302", // this property value will grow in time
  "description": "my desc2",
  "id": "xxx",
  "title": "my title2"
}

.replace() scenario results

{
  "createdAt": "1507844981006", // this value never changes in your test
  "description": "my desc2",
  "id": "xxx",
  "title": "my title2"
}

A matter of style and preferences, the following expressions can be reused:

  • const TABLE_EXPR = r.db('TEST').table('TEST');
  • const NEW_DOCUMENT_EXPR = r.expr(NEW_DOCUMENT);

Upvotes: 1

Related Questions