Я создаю механизм правил, в котором некоторые из правил являются логическими предикатами поверх низкоуровневых правил "если-то".
Другими словами, у меня есть таблица соединений, в которой хранятся совпадения между пользователями и правилами, которым эти пользователи были удовлетворены. После обработки правил более низкого порядка механизм оценивает правила более высокого уровня и создает новые соответствия для пользователей, которые их удовлетворяют.
'user_matches':
+---------+---------+-----------+
| rule_id | user_id | rule_type |
+---------+---------+-----------+
| 1 | 1 | simple |
+---------+---------+-----------+
| 2 | 1 | simple |
+---------+---------+-----------+
| 1 | 2 | simple |
+---------+---------+-----------+
| 3 | 1 | compound |
+---------+---------+-----------+
В приведенном выше примере правило № 3 требует, чтобы пользователи удовлетворяли как правилу № 1, так и правилу № 2. Пользователь # 1 соответствует этому правилу, пользователь # 2 - нет.
Поскольку я хочу избежать многократного обращения к БД, мне нужен способ превратить такие предикаты в простые SQL-запросы, которые я затем передам Arel::InsertManager
. Вот суть этого:
UserMatch.select(UserMatch.arel_table[:user_id])
.where(UserMatch.arel_table[:rule_id].eq(1)
.and(UserMatch.arel_table[:rule_id].eq(2))).to_sql
=> SELECT "user_matches"."user_id"
FROM "user_matches"
WHERE ("user_matches"."rule_id" = 1
AND "user_matches"."rule_id" = 2)
Я храню дерево правил в формате JSON:
{
"or":[
{
"and":[
{
"or":[
21,
42
]
},
84
]
},
{
"or":[
168,
336
]
}
]
}
Проблема, как вы можете видеть, заключается в том, что эти правила могут быть вложены бесконечным количеством способов. Так что мне, наверное, нужен рекурсивный цикл здесь. Не могу придумать один. Надеюсь, что вы, ребята, поможете мне и дадите мне хотя бы руководство о том, как преобразовать это псевдо-AST-дерево JSON в фактическое AST-дерево Arel.
Спасибо!
ОБНОВЛЕНИЕ: Оказывается, вы не можете использовать одну и ту же таблицу дважды в операторе SQL, см. Ответ ниже.