Luke Pighetti
Luke Pighetti

Reputation: 4841

Bulk upsert in ArangoJS?

I have found that I can .save(objects) which creates objects if the key doesn't exist. I have found that I can .bulkUpdate(objects) which updates objects whose keys exist.

My question is: how do I do a bulk upsert? If the key exists, update it (merge), if it doesn't, create it.

Is there a way to do this without a lot of overhead?

Is it acceptable to do this?

db.collection("threads").bulkUpdate(keyedThreads);
db.collection("threads").save(keyedThreads);

Upvotes: 2

Views: 931

Answers (3)

Ryan King
Ryan King

Reputation: 3696

This can be done with ArangoDB >= 3.7 and arangojs >= 7.5.0 https://arangodb.github.io/arangojs/latest/modules/collection.html#collectioninsertoptions

const db = new Database();
const collection = db.collection("some-collection");
const result = await collection.saveAll(
  [
    { _key: "a", color: "blue", count: 1 },
    { _key: "b", color: "red", count: 2 },
  ],
  { overwriteMode: "update" }
);

Upvotes: 1

RienNeVaPlu͢s
RienNeVaPlu͢s

Reputation: 7632

@pluma (ArangoDB) wrote on 9 Jun 2017:

Upsert is only available via AQL. There is no collection.upsert method.

And @fceller (ArangoDB) added:

One of the major issues with upsert is that you access to old data. This can easily be handled in AQL, but not in a simple call to an upsert endpoint. Therefore we decided against such a call and instead implemented it in AQL.

Looks like there won't ever be a native implementation of collection.bulkUpsert; however you can create your own helper function using AQL's UPSERT and MERGE functions.

TypeScript Example:

/**
 * Upserts (merges) the content of the documents with the given documents and
 * optionally returns an array containing the documents’ metadata.
 *
 * @param {string|ArangoDB.Collection} collection - collection to use
 * @param {Document[]} documents - list of documents to upsert
 * @param opts? - optionally aggregate a result {inserted:number,updated:number}
 *  also see https://www.arangodb.com/docs/stable/aql/operations-upsert.html
 */
export async function bulkUpsert(
    collection: string | DocumentCollection,
    documents: Array<ArangoDB.DocumentData>,
    { aggregate, ...upsertOptions }: {
        ignoreRevs?: boolean,
        exclusive?: boolean,
        aggregate?: boolean
    } = {}
): Promise<{inserted:number,updated:number} | true>
{
    return await (
        await db.query({
            query:  'FOR doc IN @documents'
                  + ' UPSERT { _key: doc._key } INSERT doc UPDATE MERGE(OLD, doc)'
                  + ' IN '+(typeof collection === 'string' ? collection : collection.name)
                  + ' OPTIONS '+JSON.stringify(upsertOptions)
                  + (aggregate ?
                    ' COLLECT AGGREGATE inserted = SUM(OLD ? 0 : 1), updated = SUM(OLD ? 1 : 0)'
                  + ' RETURN {inserted,updated}' :
                    ' RETURN true'),
            bindVars: {documents}
        })
    ).next();
}

Source: collection.bulkUpsert.ts Gist

Upvotes: 2

Jan Christoph Uhde
Jan Christoph Uhde

Reputation: 711

REST Insert has the overwrite option. That is probably what you are looking for.

https://github.com/arangodb/arangojs/blob/master/src/collection.ts#L721

If not you need to use the AQL UPSERT.

Upvotes: 1

Related Questions