Search code examples
rethinkdbrethinkdb-javascript

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 :

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

  • fields to replace (title, description) are not always the same

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


Solution

  • 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);