Как правильно использовать IF THEN в AQL?

Я пытаюсь использовать AQL в стиле IF THEN, но единственным подходящим оператором, который я смог найти в документации по AQL, был тернарный оператор. Я попытался добавить синтаксис IF THEN в свой уже работающий AQL, но он выдает синтаксические ошибки независимо от того, что я пытаюсь сделать.

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

Выдает следующее сообщение об ошибке:

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

person LJ Talbot    schedule 05.02.2019    source источник
comment
Если синтаксическая ошибка указывает на определенное место, и вы не сообщаете об этом в своем вопросе, как вы ожидаете, что мы дадим целевой ответ? Не могли бы вы отредактировать вопрос и скопировать и вставить в него точное сообщение об ошибке вместе с любым окружающим контекстом, который он дает?   -  person Ed Grimm    schedule 05.02.2019
comment
Окружающего контекста нет, но я отредактирую вопрос.   -  person LJ Talbot    schedule 05.02.2019
comment
Э-э, боже мой, я полностью пропустил вашу первую тройку... что было очень плохо с моей стороны, потому что это явно было целью всего этого. Если это вообще сработает, вам нужно будет заключить все это в скобки между первым ? и соответствующим ему :, потому что правила приоритета никак не позволят вам сделать это без посторонней помощи. Тем не менее, я гораздо более склонен думать, что они ожидают, что вы будете использовать связанный язык arangojs для выполнения этой логики.   -  person Ed Grimm    schedule 05.02.2019
comment
Мне просто кажется странным, что язык, который должен быть похож на SQL, не может сделать IF THEN, как вы могли бы с SQL. Это довольно сильный удар по удобству использования языка.   -  person LJ Talbot    schedule 05.02.2019


Ответы (1)


Тернарный оператор нельзя использовать как конструкцию if/else так, как это пытались сделать. Это для условных (под-)выражений, которые вы используете для вычисления mult. Оно не может стоять само по себе, его нельзя вернуть или присвоить, если вы напишете его как if-выражение.

Более того, для этого потребуются фигурные скобки, но фактическая проблема в том, что тело содержит такие операции, как LET, UPSERT и RETURN. Это языковые конструкции, которые нельзя использовать внутри выражений.

Если я правильно понял, вы хотите:

  • вставить новый документ, если документ с ключом a-b еще не существует в коллекции xb
  • если он существует, то обновите его, но только если последнее обновление было 45 секунд или больше назад

Следующий запрос работает для вас?

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

Я добавил _key к INSERT, иначе документ получит автоматически сгенерированный ключ, который, похоже, не предназначен. Использование цикла FOR и FILTER действует как конструкция IF (без ELSE). Поскольку это запрос на изменение данных, нет необходимости явно RETURN что-либо указывать, и в исходном запросе вы все равно RETURN null для случая ELSE. В то время как ваш результат будет [ null ], мой выдает [ ] (действительно пустой результат), если вы попытаетесь выполнить запрос в быстрой последовательности, и ничего не будет обновлено или вставлено.

Обратите внимание, что необходимо использовать PARSE_IDENTIFIER() для получения ключа из строки идентификатора документа и выполнить INSERT { _key: key }. С INSERT { _key: doc._key } вы столкнетесь с ошибкой invalid document key в случае вставки, потому что, если нет документа xp/a-b, DOCUMENT() возвращает null, а doc._key, следовательно, также null, что приводит к _key: null, что является недопустимым.

person CodeManX    schedule 05.02.2019
comment
Это сработало, как только я исправил ваши опечатки xb, спасибо! - person LJ Talbot; 05.02.2019
comment
Исправлено и добавлено больше деталей, почему я добавил PARSE_IDENTIFIER(). - person CodeManX; 05.02.2019
comment
На самом деле, ключ документа является единственным входом для этого, это моя ошибка из-за расплывчатости, но приведенный выше AQL был тем, что я пытался заставить запрос работать в арангоше, прежде чем я использовал его с арангоджами. Имя коллекции также предустановлено, поэтому я могу спокойно делать это без чего-либо вроде PARSE_IDENTIFIER(). Спасибо за помощь! - person LJ Talbot; 06.02.2019