5

I'm using python-arango as a driver for ArangoDB, and there doesn't seem to be an UPSERT interface.

I intended to tag this with python-arango, but I have insufficient rep to create new tags.

I'm managing with something like the function shown below, but I'm wondering if there is a better way to do this?

def upsert_document(collection, document, get_existing=False):
    """Upserts given document to a collection. Assumes the _key field is already set in the document dictionary."""
    try:
        # Add insert_time to document
        document.update(insert_time=datetime.now().timestamp())
        id_rev_key = collection.insert(document)
        return document if get_existing else id_rev_key
    except db_exception.DocumentInsertError as e:
        if e.error_code == 1210:
            # Key already exists in collection
            id_rev_key = collection.update(document)
            return collection.get(document.get('_key')) if get_existing else id_rev_key
    logging.error('Could not save document {}/{}'.format(collection.name, document.get('_key')))

Note that in my case I ensure that all documents have a value for _key and before insertion, so therefore I can assume that this holds. If someone else wants to use this, modify accordingly.

EDIT: Removed use of _id field as this is not essential to the issue.

dothebart
  • 5,972
  • 16
  • 40
Thomas Fauskanger
  • 2,536
  • 1
  • 27
  • 42
  • 1
    FWIW, the id is always `{collection}/{_key}`, so you should only have to set the `_key`. The only other way I know is to use AQL and use upsert in the query: https://docs.arangodb.com/3.0/AQL/Operations/Upsert.html – Andrew Grothe Sep 01 '17 at 02:29
  • 1
    Thanks, @AndrewGrothe, that's a good point. I use the `_id` value in logic between my models before upserting, and thought I should point it out in case someone naively tried to implement a copy of my function. (In the example above it's only used to log errors, so I guess I could've omitted it in the question.) – Thomas Fauskanger Nov 27 '17 at 00:24

3 Answers3

2

The point in using upsert is to save a database roundtrip from the application, which is wy the try/except approach isn't as good.

However, at the time being the ArangoDB HTTP-API doesn't offer upserts, thus python-arango can't offer you an API for it.

You should instead use an AQL query to upsert your document to achieve this:

UPSERT { name: "test" }
    INSERT { name: "test" }
    UPDATE { } IN users
LET opType = IS_NULL(OLD) ? "insert" : "update"
RETURN { _key: NEW._key, type: opType }

via python-arango s db.aql.execute-interface

dothebart
  • 5,972
  • 16
  • 40
1

If you're using python-arango you should just be able to do the following:

collection.insert({'_key': xxx, ...}, overwrite_mode='replace')

or

collection.insert({'_key': xxx, ...}, overwrite_mode='update')
 :param overwrite_mode: Overwrite behavior used when the document key
            exists already. Allowed values are "replace" (replace-insert) or
            "update" (update-insert). Implicitly sets the value of parameter
            **overwrite**.
Caesurus
  • 21
  • 1
0

Couldn't you just use something like this?

try:
    collection.update({'_key': xxx, ...})
except db_exception.DocumentInsertError as e:
    document.insert({'_key': xxx, ...})
William
  • 705
  • 1
  • 6
  • 17