Это интересная проблема. Я бы рассмотрел подход, состоящий из нескольких этапов:
- анализ выражений (возможно, снизу вверх) дерева выражений и маркировка узлов как «удаленных», «локальных» и «нейтральных»
- нисходящая идентификация «удаленных» подвыражений
- удаленное выполнение запроса (исключение подвыражения)
- локальное выполнение запроса
Ниже приводится более подробная информация для каждой фазы. В разделе Замечания в конце моего ответа приведены некоторые важные замечания.
Отказ от ответственности: Мой ответ довольно схематичный, и я уверен, что он не охватывает много аспектов и случаев, которые могут произойти в отношении семантики отдельных операций, разрешенных в дереве выражений. Я думаю, что придется сделать определенные компромиссы, чтобы сделать реализацию достаточно простой.
Этап 1: Анализ экспрессии и тегирование
Можно считать, что каждый узел в дереве выражений относится к следующим категориям:
- «удаленные» узлы соответствуют операциям, которые должны выполняться удаленно;
- «локальные» узлы соответствуют операциям, которые должны выполняться локально;
- «нейтральные» узлы соответствуют операциям, которые могут быть выполнены любым обработчиком запросов.
Подход снизу вверх для обхода и обработки дерева выражений кажется подходящим для этого случая. Причина в том, что при обработке данного узла X, имеющего подузлы Y_1-Y_n, категория узла X сильно зависит от категорий его подузлов от Y_1 до T_n.
Перепишем предоставленный вами образец:
entityX.SomeProperty == "Hello" &&
entityY.SomeOtherProperty == "Hello 2" &&
entityX.Id == entityY.Id
в схему соответствующего дерева выражений:
&&(&&(==(Member(SomeProperty, Var(entityX)), "Hello"),
==(Member(SomeOtherProperty, Var(entityY)), "Hello 2")),
==(Member(Id, Var(entityX)), Member(Id, Var(entityY)))
Это дерево выражений будет помечено снизу вверх. R
для «удаленного», L
для «локального», N
для «нейтрального». Если entityX
удаленно, а entityY
локально, результат будет выглядеть следующим образом:
L:&&(L:&&(R:==(R:Member(SomeProperty, R:Var(entityX)), N:"Hello"),
L:==(L:Member(SomeOtherProperty, L:Var(entityY)), N:"Hello 2")),
L:==(R:Member(Id, R:Var(entityX)), L:Member(Id, L:Var(entityY)))
Как видите, для каждого оператора ваш анализатор должен будет выбрать категорию на основе категорий его подузлов. В приведенном выше примере:
- выполнение доступа к свойству объекта даст ту же категорию, что и объект;
- строковый литерал будет нейтральным;
- сравнение равенства локального и удаленного подвыражения будет иметь локальную категорию;
- оператор and снова будет отдавать предпочтение локальному, а не удаленному.
Тем не менее, вы можете рассмотреть возможность объединения подхода «снизу вверх» с проходом оптимизации «сверху вниз» для получения лучших результатов. Считайте это (символическим): R == R + L
. Как вы хотите выполнить сравнение равенства? При чистом подходе снизу вверх вы выполняете его локально. Однако в некоторых ситуациях может быть лучше предварительно рассчитать L
локально, заменить подвыражение фактическим значением (т.е. нейтральным) и выполнить сравнение на равенство удаленно. Другими словами, вы можете в конечном итоге реализовать оптимизатор плана запросов.
Фаза 2: Идентификация удаленных подвыражений
На следующем этапе дерево выражений с тегами будет обрабатываться сверху вниз, и каждое подвыражение будет помечено как удаленное из дерева и зачислено в набор выражений, удаленно оцененных для каждого элемента в удаленном наборе данных.
Из вышесказанного ясно, что некоторые удаленные подвыражения будут инкапсулировать локальные подвыражения. И, следовательно, локальные подвыражения могут содержать удаленные подвыражения. Только нейтральные узлы должны представлять подвыражения, которые являются однородными в терминах категории.
Следовательно, возможно, потребуется выполнитьзаданный запрос с несколькими обходами к удаленному обработчику запросов.Альтернативный подход состоял бы в том, чтобы разрешить двунаправленную связь между процессорами запросов, чтобы «удаленный» процессор мог идентифицировать «локальное» (на самом деле «удаленное» с его точки зрения) выражение и вызвать обратный вызов «локального» процессорачтобы выполнить его.
Этап 3: Удаленное выполнение запроса
На третьем этапе список удаленных подвыражений будет отправлен «удаленному» обработчику запросов для выполнения.(См. Также обсуждение на предыдущем этапе.)
Вопрос также состоит в том, как определить подвыражения, которые можно использовать для эффективного ограничения результирующего набора данных, возвращаемых удаленным процессором запросов.Для этого необходимо учитывать семантику операторов верхнего уровня в дереве выражений (обычно &&
и ||
).Оценка коротких замыканий &&
и ||
немного усложняет ситуацию, поскольку препроцессор запросов может не переупорядочивать операнды.
Этап 4: Локальное выполнение запроса
Когда выполняются все удаленные подвыраженияих вхождения в исходном дереве выражений заменяются собранными результатами.
Примечания
В конечном итоге вам может потребоваться ограничить только некоторые операции, которые могут быть разрешены в «удаленные »поддеревья для уменьшения сложности обработки - это будет компромисс между возможностями и временем, потраченным на реализацию препроцессора запроса.
Для обработки псевдонимов данных (как в PropX = entityX … i.PropX.SomeProperty == "Hello"
пример, который вы предоставили) вам придется выполнить анализ потока данных.Здесь вы, скорее всего, получите набор дел, которые будут слишком сложными и заслуживающими рассмотрения.