Проект “Data Keeper” предлагает универсальное решение пользовательской классификации объектов, он имеет как свои плюсы, так и минусы. Но сама идея прекрасна и востребована: иметь возможность пользовательской настройки системы классификации. Поэтому проект “Logos” модно назвать двоюродным братом “Data Keeper“. Он также имеет возможность настройки классификации свойств хранимых объектов, но вместо универсального решения с сомнительной производительностью, предлагает специальные алгоритмы и структуру данных, которые позволят проиллюстрировать принципы пользовательской классификации на примере словаря.

Слова, в зависимости от того, какой частью речи они являются, могут иметь различный набор признаков. Хотя в примере используется русский язык, в большинстве других языков действуют те же принципы. Поэтому для начала рассмотрим структуру хранения данных.

Схема данных

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

Word

Словарные формы

  • word – написание формы слова
  • description – значение слова
  • transcription – транскрипция (звучание слова)
  • sillable – правила переноса
  • addDate – дата добавления в словарь
  • id_wpropA – часть речи
  • verifed – вычисляемый признак заполненности значимых полей
  • propList – вычисляемый список свойств
  • codeList – вычисляемый список свойств в виде сокращений

Для формирования списков значений свойств используется функция GROUP_CONCAT:

(( SELECT wpropA.name FROM wpropA WHERE wpropA.id = word.id_wpropA )||'; '||      
COALESCE(( 
  SELECT LOWER( GROUP_CONCAT(data.Name,", ")  ) 
  FROM
  ( SELECT wpropB.Name 
    FROM word_prop                                    
    LEFT JOIN wpropB ON wpropB.id = word_prop.id_wpropb
    LEFT JOIN propExt ON propExt.id = wpropb.id_propExt
    WHERE id_word = word.id                           
    ORDER BY propExt.orderNum ) as data
  ),
''))   Code language: SQL (Structured Query Language) (sql)

WPropA

Части речи

  • name – название
  • code – сокращенное название
  • description – описание

PropExt

Дополнительные свойства (признаки), характерные только для данной части речи.

  • id_wpropA – часть речи
  • name – название признака
  • description – описание признака
  • orderNum – порядковый номер

WPropB

Справочные значения дополнительных свойств.

  • id_propExt – дополнительное свойство
  • name – название
  • code – сокращение
  • description – описание
  • orderNum – порядковый номер

Word_Prop

Признаки слов

  • id_word
  • id_wpropB

Формы

Используя технологию Dynamic Table Form (DTF), создание форм сводится к описанию их свойств в файле dforms.ini:

[Word]
caption=Словарь
table=word
fields=word.verifed,word.word,word.transcription,word.codeList
captions=*,Слово,Транскрипция,Признаки
sort=word.word
[WPropA]
caption=Часть речи
table=wpropA
fields=wpropA.code,wpropA.name
captions=Сокр.,Название
sort=wpropA.code
detailForms=dtfPropExt,dtfWPropB
parentControl=frmPOS.panWPropA
[PropExt]
caption=Параметры
table=propExt
fields=propExt.orderNum,propExt.name
captions=#,Название
sort=propExt.orderNum
masterField=id_wpropA
isDetail=True
masterForm=dtfWPropA
parentControl=frmPOS.panPropExt
detailForms=dtfWPropB
[WPropB]
caption=Значения
table=wpropB
fields=wpropB.orderNum,wpropB.name
captions=#,Название
sort=wpropB.orderNum
masterField=id_propExt
isDetail=True
masterForm=dtfPropExt
parentControl=frmPOS.panWPropBCode language: PHP (php)

Для справочника частей речи необходимо создать статическую форму-контейнер frmPos, на которой автоматически будут созданы необходимые табличные элементы.

Также необходимо создать формы редактирования для каждого табличного представления, не забывая о соглашении по наименованию форм, согласно которому форма редактирования должна начинаться с префикса efm и иметь то же название, что и форма табличного представления.

efmWord

Самой сложной является форма редактирования efmWord, так как кроме обычных полей для свойств таблицы word, на ней расположены таблицы для редактирования списка word_prop.

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

Также на форме находятся пара кнопок для открытия популярных интернет-ресурсов для уточнения значения слова или его произношения.

Скрипты

Значения морфологических признаков формируются через SQL-запрос, который создается при клике на невидимую кнопку btnWPropBUpd. Для визуализации чекера используется юникод-символ с кодом 10003.

procedure efmWord_btnWPropBUpd_OnClick (Sender: TObject; var Cancel: boolean);
// выборка данных для списка с чекерами
var
  tmpSQL:string;
  tmpIDWord: string;
  tmpIDPropExt: string;
begin
  tmpIDWord := IntToStr(efmWord.btnSave.dbGeneralTableId); // слово
  tmpIDPropExt := IntToStr( efmWord.tgrPropExt.dbItemID ); // свойство части речи
  tmpSQL := //
    ' SELECT ' + //
    '   ( case when word_prop.id is null then "" else char(10003) end  ),'+ // отобразить чекер, если есть такое значение в базе
    '   wpropB.name, '+ // название
    '   wpropB.id '+ // ID, загружается в последнюю колонку, невидимую
    ' FROM wpropB '+ // выборка из списка всех свойств части речи
    ' LEFT JOIN word_prop ON word_prop.id_wpropB = wpropB.id and word_prop.id_word = '+tmpIDWord+ // добавляем значения свойств с учетом текущего слова
    ' WHERE wpropB.id_PropExt = '+tmpIDPropExt; // с фильтрацией по части речи
  efmWord.btnWPropBUpd.dbSQL := tmpSQL;
end;Code language: Delphi (delphi)

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

procedure efmWord_tgrWPropB_OnCellClick (Sender: TObject; ACol, ARow: Integer);
// редактирование таблицы значений морфологических свойств слова.
var
  tmpSQL: string;
  tmpIDWord: string;
  tmpIDPropExt: string;
  tmpIDWPropB: string;
begin
  if ACol = 0 then // срабатывает только при клике по первой колонке
  begin
    tmpIDWord := IntToStr(efmWord.btnSave.dbGeneralTableId); // слово
    tmpIDPropExt := IntToStr( efmWord.tgrPropExt.dbItemID ); // часть речи
    tmpIDWPropB := IntToStr( efmWord.tgrWPropB.dbIndexToID(ARow) ); // значение свойства
    // удаляем предыдущие значений
    tmpSQL :=
      'DELETE FROM word_prop '+
      'WHERE id_word = '+tmpIDWord+
      '  AND id_wpropb in ( SELECT id FROM wpropB WHERE id_propExt ='+tmpIDPropExt+' )';
    SQLExecute(tmpSQL);
    // если чекера не было, то добавляем
    if efmWord.tgrWPropB.Cells[ACol,ARow] = '' then
    begin
      if tmpIDWord = '-1' then // если запись новая, то сохранить данные
      begin // чтобы получить ID слова
        SaveWithoutClose(efmWord.btnSave);
        tmpIDWord := IntToStr(efmWord.btnSave.dbGeneralTableId);
      end; // если сохранение удалось, то добавляем запись
      if tmpIDWord <> '-1' then
      begin
        // добавляем новое значение
        tmpSQL :='INSERT INTO word_prop (id_word,id_wpropb) VALUES ('+tmpIDWord+','+tmpIDWPropB+')';
        SQLExecute(tmpSQL);
      end;
    end;
    // BClick - это безопасный с точки зрения внутренних механизмов стека вызов обработчика нажатия кнопки
    BClick( efmWord.btnWPropBUpd ); // обновить отображение таблицы
    BClick( efmWord.btnUpdMemPropList ); // обновить отображение списка свойств слова
  end;
end;
Code language: Delphi (delphi)

Обновление списка свойств на форме редактирования позволяет сразу видеть результат:

procedure efmWord_btnUpdMemPropList_OnClick (Sender: TObject; var Cancel: boolean);
// обновить отображение текста
var
  tmpSQL: string;
  tmpIDWord: string;
begin
  tmpIDWord := IntToStr(efmWord.btnSave.dbGeneralTableId);
  tmpSQL := //
    ' SELECT LOWER( GROUP_CONCAT(data.Name,", ") ) FROM '+
    '   ( SELECT wpropB.Name '+
    '     FROM word_prop ' +
    '     LEFT JOIN wpropB ON wpropB.id = word_prop.id_wpropb '+
    '     LEFT JOIN propExt ON propExt.id = wpropb.id_propExt '+
    '     WHERE id_word = '+tmpIDWord+
    '     ORDER BY propExt.orderNum ) as data ';
  efmWord.memPropList.Text := LowerCase(efmWord.cmbWPropA.Text) + '; '+ SQLExecute(tmpSQL);
end;
Code language: Delphi (delphi)

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

procedure efmWord_OnShow (Sender: TObject; Action: string);
// отображение формы редактирования
begin
  efmWord.cmbWPropA.DoOnChange; // вызвать обработчик смены части речи
  efmWord.btnWPropBUpd.Click; // обновить (сбросить) содержимое списка свойств
end;

procedure efmWord_cmbWPropA_OnChange (Sender: TObject);
// выбор части речи
begin
  // отфильтровать свойства по части речи
  efmWord.tgrPropExt.dbFilter := 'id_WPropA = '+IntToStr( efmWord.cmbWPropA.dbItemID );
  efmWord.tgrPropExt.dbUpdate;
end;

procedure efmWord_btnOnLine_2_OnClick (Sender: TObject; var Cancel: boolean);
begin
  OpenURL('https://frazbor.ru/'+efmWord.edtName.Text);
end;

procedure efmWord_btnOnLine_OnClick (Sender: TObject; var Cancel: boolean);
begin
  OpenURL('https://gramota.ru/poisk?query='+efmWord.edtName.Text+'&mode=all');
end;

procedure BClick(AButton: TdbButton);
  // клик без рекурсии в скриптах
  // нельзя создавать рекурсию для методов-обработчиков событий, поэтому клик заменяем на PostMessage
begin
  PostMessage(AButton.Handle, wm_LButtonDown, 0, 0);
  PostMessage(AButton.Handle, wm_LButtonUp, 0, 0);
end;
Code language: Delphi (delphi)

Ссылки

  • Logos 1.0 – исходный код проекта

P.S.

При отладке данного проекта у меня возникла ошибка уровня среды исполнения: MVDB вылетал без подсветки кода и указания на строку скрипта, косвенно указывая на проблему использования библиотеки ntdll.dll при обращении к БД.

По совету коллег на форуме я решил обновить sqlite3.dll, скачав с официального сайта самую последнюю версию. После этого данные ошибки исчезли. Но стоит учитывать, что теперь в проекте “Logos” находится 64-битная версия этой библиотеки:

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *