Статьи

Переменные в запросах HyperGraphDB

Вступление

Функция, которая запрашивалась несколько раз, — это возможность параметризовать запросы HyperGraphDB с переменными, что-то вроде того, что позволяет JDBC PreparedStatement . Эта функция теперь реализована с введением именованных переменных. Именованные переменные могут использоваться вместо обычных параметров условия и могут содержать произвольные значения, независимо от того, что ожидает условие. Преимущества имеют два аспекта: повышают производительность, избегая перекомпиляции запросов, в которых изменяется только несколько параметров условий, и более чистого кода с меньшим дублированием запросов. Повышение производительности предварительно скомпилированных запросов вполне ощутимо, и им нельзя пренебрегать. Почти все предопределенные условия запроса поддерживают переменные, кроме TypePlusConditionкоторый раскрывается в дизъюнкцию типа во время компиляции запроса и поэтому не может быть легко предварительно скомпилирован в параметризованную форму. Также следует отметить, что некоторые оптимизации, сделанные на этапе компиляции, в частности связанные с использованием индекса, не могут быть выполнены при параметризации условия. Простым примером этого ограничения является параметризованное  TypeCondition — индексы определяются для каждого типа, поэтому, если тип не известен во время анализа запроса, индекс не будет использоваться.

API

API разработан для соответствия существующим выражениям условий запроса. Переменная создается путем вызова
 метода
hg.var, и результат этого метода передается как параметр условия. Например:

HGQuery<HGHandle> q = hg.make(HGHandle.class, graph).compile(
    hg.and(hg.type(typeHandle),hg.incident(hg.var("targetOfInterest"))));

Это создаст запрос, который ищет ссылки определенного типа и которые указывают на данный атом, параметризованный переменной «targetOfInterest». Перед запуском запроса необходимо установить значение переменной:

q.var("targetOfInterest", targetHandle);
q.execute();

Также возможно указать начальное значение переменной как второе в
методе
hg.var :

q = hg.make(HGHandle.class, graph).compile(
    hg.and(hg.type(typeHandle),hg.incident(hg.var("targetOfInterest", initialTarget))));
q.execute();

Обратите внимание на новый  статический метод hg
.make : он принимает тип результата и экземпляр графа и возвращает объект запроса, к которому еще не присоединено условие. Чтобы прикрепить условие, необходимо вызвать
метод
HGQuery.compile сразу после make. Эта версия make устанавливает переменный контекст, присоединенный к вновь созданному запросу. Все условия запроса, созданные после вызова make и до вызова
compile, будут неявно предполагать, что переменные принадлежат этому контексту. Поэтому, чтобы избежать неожиданностей, вы должны вызывать
compile сразу после
make .

После того, как запрос таким образом создан, можно выполнить его много раз, присваивая новые значения переменным через
hg.var.метод, как показано выше.

Реализация


Требование вызова
HGQuery.compile справа от
метода
make исходит из того факта, что существующий API создает условия в статической среде. Условие полностью создается до того, как оно передается методу компиляции. Таким образом, чтобы сделать контекст переменной доступным в процессе конструирования условия (оценка выражения условия запроса), мы делаем здесь что-то относительно неортодоксальное и присоединяем этот контекст (по сути, карту имени / значения) как локальную переменную потока в
makeметод. Альтернативой было бы создание обхода выражения условия, которое собирает все переменные, используемые в них. Либо этот процесс обхода должен был бы знать обо всех типах условий, либо HGQueryCondition пришлось бы расширить с помощью некоторого дополнительного интерфейса для «составных условий» и «переменных, используемых в условии». Мне не понравилась ни одна из этих возможностей. Двухэтапный процесс вызова
hg.make (…). Compile (….) кажется чистым и достаточно лаконичным.

Переменные реализованы через несколько новых интерфейсов:
org.hypergraphdb.util.Ref, org.hypergraphdb.util.Var. Когда условие создается с постоянным значением (вместо переменной),
реализация
org.hypergraphdb.util.Constant объекта
Реф интерфейс используется. Все условия теперь содержат
закрытую переменную-член
Ref <T>, где раньше они содержали переменную-член типа T. Я не буду больше говорить об этих ссылочных абстракциях, за исключением того, что следует избегать их использования, если только не выполняется пользовательское условие … они считаются внутренними.

Кстати, эта идея создания ссылочной абстракции, по сути, реализует давнюю стратегию введения другого уровня косвенности для решения проблемы проектирования. Я использовал эту концепцию во многих местах сейчас, в том числе для таких вещей, как кэширование и работа со сложными областями в контейнерах компонентов (Spring), поэтому я потратил некоторое время, пытаясь создать более общую библиотеку вокруг этого. Первоначально я намеревался создать эту библиотеку как отдельный проект и использовать ее в HyperGraphDB. Но я не придумал что-то достаточно удовлетворительное, поэтому вместо этого я создал пару интерфейсов, упомянутых выше. Однако в конечном итоге эти интерфейсы и классы могут быть заменены отдельной библиотекой и в конечном итоге удалены из кодовой базы HyperGraphDB.