Qmap хранит расширенный контекст итератора

  !   Данная информация предназначена только только для IT-специалистов по системной интеграции модулей БИОСОФТ-М. (см. Руководства пользователя к программным продуктам)

Приложениям и всем множественным уровням инкапсуляции запросов и итераторов бывает необходимо сохранить какой то свой локальный приватный контекст, связанный с итератором.

Чаще всего речь идет о необходимости создания некоего кеша перед первой итерацией запроса и использование его на последующих шагах.

Связывание локального контекста с iter

Такой контекст можно добавлять под своим приватным ключем (key) в CQueryIfaceGp::x_rQmap проперти итератора. Метод CQmapIfaceGp::MapObject() одновременно позволяет присоединить по ключу прикладной объект любого локального типа к итератору и получить его потом на следующих итерациях. Возвращаемое значение можно использовать для выбора момента инициализации:

CMyStorage::Iterate/Query()
{
 
    ref<CMyContextData> rMyContextData;
    if (out_i.GetCurrentQuery()->
            x_rQmap->
                MapObject(
                    _m_keyMyPrivateContext,
                    QMAP_OBJECT(rMyContextData)))
    {
        // init rMyContextData before first iteration
        rMyContextData->BuildCaches();
    }

Макрос QMAP_OBJECT() должен использоваться вместо вторых двух параметров метода для передачи контекстного объекта с безопасным контролем его типа.

Уникальный ключ контекста

Разные уровни инкапсуляции итератора могут добавлять свои контексты к нему, каждый под своим уникальным ключем. Ключ рекомендуется генерировать случайный и хранить в соответствующем инкапсуляторе:

class CMyStorage
{
public
 
    ... Iterate/Query(...);
 
private:
 
    key _m_keyMyPrivateContext = key::GNewUniqueKey();

Уникальную константу key::GConstKeyStr() потенциально можно было бы использовать вместо этого но она создает как проблемы придумывания уникальных строчек так и локализации контекста в разных экземплярах вложенных инкапсуляторах одного типа. Поэтому рекомендуется именно хранить случайный уникальный ключ в каждом объекте инкапсулятора.

Применения

Имеется например хранилище, с итератором, получающим список критериев который требует нетривиальной конвертации в список другого формата для передачи в хранилище более низкого уровня. Список полей Bics в Costo:

bool CCostoRelaStoreImpl::OnIterateBicsObjectValuesForObject(
        ....
        ref<CBicsObjectFieldListIfaceGp> rBicsObjectFieldList,
        out iter& out_i,
        ...)

Конверсия делается перед первой итерацией, результат сохраняется "в итераторе" и используется до конца цикла:

bool CCostoRelaStoreImpl::OnIterateBicsObjectValuesForObject(
        ...
{
    // convert Bics field list to Rego field map on initial iteration
    ref<CRegoFieldMapIfaceGp> rRegoFieldMap;
    if (out_i.GetCurrentQuery()->
            x_rQmap->
                MapObject(
                    _m_keyQmapRegoFieldMap,
                    QMAP_OBJECT(rRegoFieldMap)))
    {
        rASSERT(out_i.IsBeforeFirstIteration());
 
        // Conversion
        ref<CBicsObjectFieldIfaceGp> rIterBicsObjectField;
        for (
            iter i;
            rBicsObjectFieldList->
                Iterate(
                    out i,
                    out rIterBicsObjectField);)
        {
            rRegoFieldMap->
                AddFieldKey(
                    rIterBicsObjectField->
                        GetBicsUniqueKey());
        }
    }
    else
    {
        rASSERT(!out_i.IsBeforeFirstIteration());
    }
 
    // Call lower level
    ref<CRegoObjectFieldIfaceGp> rRegoObjectField;
    rp<CRegoValueIfaceGp> rpRegoValue;
    if (x_rRegoStore->
            IterateBicsObjectValuesForObject(
                rRegoFieldMap, // converted list

На практике сконвертированные списки критериев в целях оптимизации запросов может понадобится хранить "глобально" а не только в рамках одного цикла. Но в контексте итератора все равно нужно однократно определить и выбрать ссылку на параметры запроса в эффективном формате.

Реализация Qmap

В CQmapIfaceGp прослеживаются явные аналогии с unidef. Это маппинг данных неопределенного типа по ключам. В отличии от unidef объекты не образуют иерархию, не являются скалярами а ref<object>, и ключи не явные иерархические строковые пути а одноуровневый key.

Применение Qmap за пределами вышеописанного сценария сейчас запрещено.