conditional-statementsarangodbupsertaqlarangojs

What is the proper way to use IF THEN in AQL?


I'm trying to use IF THEN style AQL, but the only relevant operator I could find in the AQL documentation was the ternary operator. I tried to add IF THEN syntax to my already working AQL but it gives syntax errors no matter what I try.

LET doc = DOCUMENT('xp/a-b')
LET now = DATE_NOW()
doc == null || now - doc.last >= 45e3 ? 
  LET mult = (doc == null || now - doc.last >= 6e5 ? 1 : doc.multiplier)
  LET gained = FLOOR((RAND() * 3 + 3) * mult)
  UPSERT {_key: 'a-b'}
  INSERT {
    amount: gained,
    total: gained,
    multiplier: 1.1,
    last: now
  }
  UPDATE {
    amount: doc.amount + gained,
    total: doc.total + gained,
    multiplier: (mult < 4 ? FLOOR((mult + 0.1) * 10) / 10 : 4),
    last: now
  }
  IN xp
  RETURN NEW
 : 
  RETURN null

Gives the following error message:

stacktrace: ArangoError: AQL: syntax error, unexpected identifier near 'doc == null || now - doc.last >=...' at position 1:51 (while parsing)

Solution

  • The ternary operator can not be used like an if/else construct in the way to tried. It is for conditional (sub-)expressions like you use to calculate mult. It can not stand by itself, there is nothing it can be returned or assigned to if you write it like an if-expression.

    Moreover, it would require braces, but the actual problem is that the body contains operations like LET, UPSERT and RETURN. These are language constructs which can not be used inside of expressions.

    If I understand correctly, you want to:

    Does the following query work for you?

    FOR id IN [ 'xp/a-b' ]
        LET doc = DOCUMENT(id)
        LET key = PARSE_IDENTIFIER(id).key
        LET now = DATE_NOW()
        FILTER doc == null || now - doc.last >= 45e3
        LET mult = (doc == null || now - doc.last >= 6e5 ? 1 : doc.multiplier)
        LET gained = FLOOR((RAND() * 3 + 3) * mult)
        UPSERT { _key: key }
        INSERT {
            _key: key,
            amount: gained,
            total: gained,
            multiplier: 1.1,
            last: now
        }
        UPDATE {
            amount: doc.amount + gained,
            total: doc.total + gained,
            multiplier: (mult < 4 ? FLOOR((mult + 0.1) * 10) / 10 : 4),
            last: now
        }
        IN xp
        RETURN NEW
    

    I added _key to INSERT, otherwise the document will get an auto-generated key, which does not seem intended. Using a FOR loop and a FILTER acts like an IF construct (without ELSE). Because this is a data modification query, it is not necessary to explicitly RETURN anything and in your original query you RETURN null for the ELSE case anyway. While yours would result in [ null ], mine produces [ ] (truly empty result) if you try execute the query in quick succession and nothing gets updated or inserted.

    Note that it is necessary to use PARSE_IDENTIFIER() to get the key from the document ID string and do INSERT { _key: key }. With INSERT { _key: doc._key } you would run into an invalid document key error in the insert case, because if there is no document xp/a-b, DOCUMENT() returns null and doc._key is therefore also null, leading to _key: null - which is invalid.