Россия, Звенигород |
RDF
11.6. Практика: модели данных в NoteTaker
Это практическое занятие посвящено процессу моделирования, результатом которого является набор фактов RDF.
Мы много экспериментировали с интерфейсом NoteTaker и скриптами JavaScript, однако пришло время заняться проектированием. У нас до сих пор нет ясного понимания того, какими данными должен манипулировать NoteTaker. Мы выбираем RDF в качестве формата для хранения этих данных. В процессе моделирования нам предстоит разработать модель данных, чтобы понять, какие факты RDF для этого понадобятся. Нашим языком моделирования будут факты, выраженные в форме триплетов.
На платформе Mozilla RDF лучше всего использовать для локального хранения данных, хотя документы RDF могут и передаваться через Internet. Кроме того, RDF особенно эффективен при работе с небольшими объемами данных. Обе эти особенности отвечают нашей задаче, поскольку NoteTaker будет хранить относительно небольшое количество заметок, созданных пользователем, на локальном диске, в каталоге профиля пользователя. Доступ к Internet не будет обязательным условием работы NoteTaker.
Выбор фактов в качестве языка моделирования не является очевидным. Вместо этого мы могли бы выбрать объектно-ориентированный или реляционный подход. Однако факты имеют одно важное преимущество – они непосредственно отображаются на синтаксис RDF. Язык UML хорош для объектно-ориентированных систем; модель "сущность – связь" хороша для реляционных баз данных; факты хороши для RDF.
При разработке и реализации модели данных мы будем придерживаться следующего плана:
- Описать данные на естественном языке, зафиксировав все, что нам о них известно.
- Выбрать значимые слова (термины).
- Сконструировать несколько полезных фактов.
- Убедиться, что это – факты о ресурсах.
- Проанализировать, каким образом должен осуществляться доступ к этим фактам.
- Организовать факты в структуры, поддерживающие нужные способы доступа.
- Выразить результаты на синтаксически корректном RDF.
Как правило, при моделировании данных приходится предпринять несколько попыток, прежде чем будет найден оптимальный вариант. Мы, однако, сразу же начнем с приемлемого подхода, обращая в процессе моделирования внимание на некоторые распространенные ошибки и тупиковые направления.
Мы можем без труда описать модель данных NoteTaker на естественном языке. Каждая заметка связана с одним URL. Она имеет короткое и длинное описание (аннотация и подробности). Она занимает на экране определенное положение, которое характеризуется двумя координатами ее левого верхнего угла, высотой и шириной. Кроме того, с заметкой связаны ключевые слова.
Ключевые слова – обычные слова, добавляемые пользователем для характеристики URL, к которому относится заметка. Если два ключевых слова были использованы для описания одной заметки, говорят, что эти слова связаны. Эта связь имеет значение и за пределами конкретной заметки – тот факт, что они появились вместе в каком-либо контексте, указывает на то, что эти слова связаны в широком смысле. Связи между ключевыми словами могут использоваться для контекстно-зависимой подсказки пользователю. Когда последний выбирает ключевое слово для заметки, одновременно отображается список всех слов, связанных с выбранным. Пользователь может отметить некоторые слова в этом списке, чтобы присвоить их заметке одновременно с первым.
В приложение NoteTaker, которое разрабатывается в этой книге, будет включен лишь минимум функциональности, связанной с ключевыми словами, для демонстрации некоторых приемов работы с XUL и RDF. Мы можем представить себе следующую версию NoteTaker, написанную в процессе развития нашего приложения. Эта версия может включать специальное окно Менеджера заметок, используемое для отображения списка всех заметок, его сортировки, управления заметками, их поиска по ключевым словам, а также позволяющее открыть web-документ, связанный с выбранной заметкой.
Интерфейс NoteTaker включает еще два элемента данных, которые отображаются как флажки "Отбросить запрос" и "Корневая страница" в диалоговом окне редактирования заметки. Эти данные не являются свойствами заметки, а скорее управляют процессом ее создания.
Если установлен флажок "Отбросить запрос", то из URL новой заметки будет удалена любая информация, относящаяся к запросу GET. Например, этот URL
http://www.test.com/circuits.cgi?voltage=240V;amps=50mA
будет сокращен до следующего
http://www.test.com/circuits.cgi
При этом вновь созданная заметка будет появляться при просмотре любой страницы, чей URL начинается с этой сокращенной строки. Если установлен флажок "Корневая страница", то URL будет сокращен еще больше, до
В этом случае заметка будет соответствовать всем страницам данного сайта. Если URL содержит каталог отдельного пользователя, как в примере:
http://www.test.com/~fred/mytests/test2.htm
то при установленном флажке "Корневая страница" он будет сокращен
При использовании любого из этих вариантов при просмотре конкретного URL будет отображаться только наиболее специфичная для него заметка. Иными словами, если одновременно определены заметка для корневой страницы сайта и заметка для другой его страницы, то при просмотре этой страницы будет отображаться лишь последняя заметка. В любом случае, наличие этих флажков никак не влияет на модель данных NoteTaker, поскольку результатом установки любого из них является преобразование URL при создании заметки, а не сохранение дополнительных параметров вместе с заметкой.
На этом выполнение первого этапа нашего плана завершено, и мы можем перейти ко второму. В результате процесса моделирования все наши данные должны быть представлены в виде фактов-триплетов. Пока же мы должны выбрать из описания значимые термины, которые в дальнейшем станут термами этих фактов. Поскольку факты включают информацию об отношениях, мы должны обращать внимание не только на существительные (потенциальные субъекты и объекты фактов). Однако выделение значимых существительных может быть хорошей отправной точкой для дальнейшей работы. Предположим, что при первой попытке анализа описания мы получили следующие результаты:
заметка ключевое-слово URL аннотация подробности сверху слева высота ширина
Здесь "сверху" и "слева" – расстояние левого верхнего угла заметки от верхнего и левого краев экрана соответственно. К этим существительным мы можем добавить следующие предполагаемые отношения:
связанное-ключевое-слово данные-заметки заметка-для-url
Теперь мы должны сконструировать факты на основе этих терминов. Факты могут рассматриваться как триплеты субъект-предикат-объект или, в терминологии RDF, как триплеты ресурс-свойство-значение. Для нашего анализа полезны обе точки зрения. Прежде всего, мы проанализируем каждый из терминов при помощи трех вопросов:
- Может ли этот термин быть самостоятельной сущностью (субъектом или ресурсом)?
- Может ли этот термин выражать отношение (быть предикатом)?
- Может ли этот термин использоваться для описания какой-либо другой сущности (быть свойством)?
Третий вопрос поможет нам выявить "слабые" термины, чтобы ограничить число субъектов в нашей модели. Результаты анализа представлены в таблице 11.4 (цифры указывают на пункты обсуждения в последующем тексте).
Термин | Сущность? | Отношение? | Свойство? |
---|---|---|---|
заметка | V | V 3. | |
ключевое-слово | V | 1. | |
URL | V | V 3. | |
аннотация | 2. | V | |
подробности | 2. | V | |
сверху | 2. | V | |
слева | 2. | V | |
ширина | 2. | V | |
высота | 2. | V | |
связанное-ключевое-слово | V | V | |
данные-заметки | V 4. | ||
заметка-для-url | V | V |
На основе этого анализа мы можем сделать некоторые выводы, а ряд вопросов требует дополнительного обсуждения. Начнем с выводов: ключевое-слово представляет собой сущность; данные-заметки – отношение; аннотация, подробности, сверху, слева, ширина и высота – свойства для описания других сущностей. Мы можем быть уверены в этом, поскольку в каждой из соответствующих строк таблицы присутствует лишь одна отметка.
Теперь обсудим проблемы, возникшие при анализе терминов.
1. Мы полагаем, что конкретные ключевые слова не могут рассматриваться как свойства другого термина, поскольку это означало бы, что на основе этих ключевых слов должны быть образованы свойства RDF (предикаты). Хотя с формальной точки зрения в этом нет ничего невозможного, обычно предполагается, что свойства имеют хорошо известные имена, например "цвет". Нам известно, что у каждой заметки может быть ноль или более ключевых слов, каждое из которых имеет собственное имя, указанное пользователем. Это означает, что для ключевых слов не существует постоянного хорошо известного имени и, следовательно, вряд ли имеет смысл использовать их в качестве свойств. Следует ожидать аналогичных проблем для любого элемента данных, который находится в отношении "многие к одному" с другим элементом данных.
2. Эти термины не имеют собственных свойств, поэтому нет оснований считать их самостоятельными сущностями. Очевидно, они являются свойствами каких-то других сущностей, например URL или заметки.
3. Нам не вполне ясна ситуация с заметкой и URL. Является ли один из этих терминов свойством другого или же они независимы? Пока лучше считать, что оба термина – сущности.
4. Смысл такого термина, как "данные заметки" не вполне ясен. Он не указывает ни на конкретные данные, ни на отношение между ними, ни на какую-либо другую определенную информацию. Можно сравнить этот неопределенный термин с "аннотацией", которая является конкретным фрагментом данных, связанным с заметкой. Фактически мы невольно привнесли в нашу модель элемент метаданных – "заметки имеют данные". Поэтому мы просто исключим этот термин из дальнейшего анализа – простые приложения не нуждаются в метаданных.
На основе нашего анализа мы можем сформулировать факты, перечисленные в листинге 11.15.
<- заметка, ?, URL -> <- URL, ?, заметка -> <- заметка, URL, ? -> <- URL, заметка, ? -> <- ключевое-слово, ?, ? -> <- заметка, аннотация, ? -> <- заметка, подробности, ? -> <- заметка, сверху, ? -> <- заметка, слева, ? -> <- заметка, ширина, ? -> <- заметка, высота, ? -> <- ?, связанное-ключевое-слово, ? -> <- ?, заметка-для-url, ? ->Листинг 11.15. Исходные факты для модели данных NoteTaker
Первые четыре факта представляют собой четыре варианта разрешения неопределенности, с которой мы столкнулись. Мы должны сделать выбор, исходя из потребностей приложения. Мы вернемся к этой проблеме после того, как разберем более простые вопросы.
Шесть следующих фактов с субъектом "заметка" тривиальны. Каждый из них будет содержать в качестве объекта простое значение, следовательно, этим объектом не может быть URI. Их объект должен относиться к одному из типов, поддерживаемых Mozilla ( Literal, Integer, Date, Blob ). Мы будем использовать тип Literal, который представляет собой простую строку. Тогда эти факты примут следующий вид:
<- заметка, аннотация, Literal ->
Факт с предикатом связанное-ключевое-слово связывает между собой два ключевых слова. Очевидно, что субъектом и объектом этого факта должны быть ключевые слова. Заменив для краткости предикат на "связано", получаем следующий факт:
<- ключевое-слово, связано, ключевое-слово ->
Использование ключевых слов в фактах создает проблему имен. Нам известно, что ключевое слово должно быть представлено посредством URI, поскольку оно является сущностью (ресурсом). Если не считать, что где- то существует сервер, определяющий все ключевые слова в мире (что вряд ли имеет практический смысл), для ключевых слов должны использоваться URN, а не URL. Откуда мы можем взять эти URN? Их можно построить на основании данных, предоставленных пользователем. Для этого мы добавим к строке ключевого слова, введенной пользователем, префикс urn:notetaker:keyword: (мы используем английское слово "keyword", поскольку синтаксис URL допускает лишь использование ASCII- символов7Согласно документу RFC 2141, символы, выходящие за пределы диапазона ASCII, для использования в URN должны преобразовываться в кодировку UTF-8. Затем результат преобразования побайтово кодируется так же, как это принято для URL – каждый байт представляется с помощью символа "%" и двух шестнадцатеричных цифр. (Прим. пер.) ). Например, ключевому слову foo будет присвоен следующий URI:
urn:notetaker:keyword:foo
Нам также понадобится доступ к самой строке ключевого слова. В последующих лекциях мы увидим, что извлечь подстроку из URI, используемого в RDF, довольно сложно. Поэтому мы введем свойство "метка" и будем хранить его как отдельную строку в составе следующего факта:
<- urn:notetaker:keyword:{строка ключевого слова}, метка, "{строка ключевого слова}" ->
Наконец мы должны разобраться с вопросом о соотношении заметки и URL. Каждому URL соответствует одна заметка, и каждой заметке соответствует один URL. Необходимо решить, являются ли они отдельными сущностями. Если это так, нам понадобится каким-то образом описать связь между ними. Если их нельзя рассматривать как отдельные сущности, то одна из них, вероятно, будет свойством другой.
Если считать URL и заметку отдельными сущностями, каждая из них должна иметь собственный URI. Необходимо подобрать для них эти URI. В случае URL выбор очевиден – это сам URL. Что же может использоваться в качестве URI для заметки? Это должен быть URN (мы могли бы использовать URL файла, но это было бы необычным решением). Поскольку заметка не имеет имени, мы можем сконструировать для нее произвольное имя (каким образом?) или оставить ее анонимной (но в этом случае документ RDF не будет полностью определенным). Оба решения довольно неуклюжи.
Таким образом, мы приходим к выводу, что заметке недостает собственной идентичности, и она должна каким-то образом зависеть от URL. Поскольку заметка не имеет идентичности (не может быть поименована), она не может быть субъектом или объектом никакого факта. Поскольку она не имеет "собственного значения", она не может быть представлена литералом. Таким образом, заметка как сущность, самостоятельная или зависимая, просто не существует. Мы исключаем ее из нашей модели, оставляя только URL, к которому относится эта заметка. Это решает большинство проблем, связанных с первыми четырьмя фактами листинга 11.15.
Такой результат может показаться странным, особенно для читателя, который знаком с объектно-ориентированным или реляционным моделированием. Разве заметка не является центральным объектом всего приложения NoteTaker? Разве мы не вправе создавать в нашей модели любые объекты или сущности, которые сочтем необходимыми? Ответ на первый вопрос – "да", а на второй – "нет". Да, заметка действительно является центром приложения, но, как выяснилось, не его модели данных. Нет, мы не вправе порождать сущности по своему усмотрению, поскольку мы работаем не с "чистой" системой обработки фактов, подобной Prolog, а с RDF. В RDF существует одна базовая концепция, которая предшествует любой конкретной модели – концепция ресурса, основанного на URI. Мы не вправе создавать любые объекты – мы можем использовать лишь сущности, имеющие URI, или литералы. Хотя наше приложение основано на идее заметки, в модели данных RDF любая значимая сущность должна иметь собственный URI. Например, мы смогли поместить ключевые слова в нашу модель данных лишь потому, что смогли подобрать URI для каждого из них.
Итог этого рассуждения прост – чтобы использовать сущность в модели данных RDF, необходимо подобрать для нее URL или URN, или представить ее как литерал. В противном случае для этой сущности нет места в модели данных.
В листинге 11.16 представлены факты после произведенных нами изменений:
<- URL, аннотация, Literal -> <- URL, подробности, Literal -> <- URL, сверху, Literal -> <- URL, слева, Literal -> <- URL, ширина, Literal -> <- URL, высота, Literal -> <- ключевое-слово, метка, Literal -> <- ключевое-слово, связано, ключевое-слово ->Листинг 11.16. Окончательные факты для модели данных NoteTaker
Очевидно, что в этом списке отсутствует факт связи между ключевым словом и заметкой (точнее, с URL, которому соответствует заметка). Мы не затрагивали этой связи в процессе анализа, поскольку нам не было ясно, являются ли ключевые слова сущностями или свойствами. Мы не можем использовать конкретные ключевые слова в качестве свойств (предикатов), поскольку каждое ключевое слово имеет свой URN. Мы можем попробовать один из вариантов:
<- URL, ключевое-слово, urn-ключевого-слова -> <- urn-ключевого-слова, url-заметки, URL->
Каждому URL (и заметке) может соответствовать ноль или более фактов первого типа. Вместо этого (или в дополнение к этому), каждому ключевому слову может соответствовать ноль или более фактов второго типа.
В предлагаемом решении каждая заметка (каждый URL) может иметь несколько свойств "ключевое слово". Можем ли мы использовать для их хранения контейнер RDF, например <Seq>? К сожалению, это не так просто. Пойдя по такому пути, мы должны будем использовать отдельный контейнер для каждого URL с определенной заметкой. Какое имя должен иметь такой контейнер? Это должен быть URN, и нам придется сконструировать его. В принципе, мы можем добавить URL, связанный с заметкой, к какому-либо префиксу. Это возможный, но громоздкий подход, и на этом пути мы столкнемся с трудностями обработки строк, речь о которых пойдет в следующих лекциях. Мы предпочтем придерживаться простых решений и обойтись без контейнера <Seq>. Мы будем использовать первый из предложенных вариантов:
<- URL, ключевое-слово, urn-ключевого-слова ->
Таким образом, мы завершили третий и четвертый этапы нашего плана – выразили всю необходимую информацию о модели данных при помощи фактов, включающих литералы и ресурсы, именованные при помощи URI. Пример набора фактов, описывающих заметку, которой присвоены два ключевых слова – "test" и "cool",– приведен в листинге 11.17.
<- http://saturn/test.html, аннотация, "Моя аннотация" -> <- http://saturn/test.html, подробности, "Мои подробности" -> <- http://saturn/test.html, сверху, "100" -> <- http://saturn/test.html, слева, "90" -> <- http://saturn/test.html, ширина, "80" -> <- http://saturn/test.html, высота, "70" -> <- http://saturn/test.html, ключевое-слово, urn:notetaker:keyword:test -> <- http://saturn/test.html, ключевое-слово, urn:notetaker:keyword:cool -> <- urn:notetaker:keyword:test, метка, "test" -> <- urn:notetaker:keyword:cool, метка, "cool" -> <- urn:notetaker:keyword:test, связано, urn:notetaker:keyword:cool ->Листинг 11.17. Пример фактов, описывающих одну заметку NoteTaker.
Последний факт выражает связь между ключевыми словами. Мы могли бы сформулировать и обратный факт, поменяв местами субъект и объект. Однако в этом случае мы получили бы множество избыточных фактов для заметки со множеством ключевых слов. Поэтому здесь мы ограничились минимумом фактов, необходимых для описания связи между ключевыми словами.
Пятый и шестой этапы нашего плана связаны с эффективностью доступа к необходимым данным документов RDF. Более подробно эти вопросы обсуждаются в "Шаблоны" "Шаблоны". Чтобы извлекать данные более эффективно, мы можем снабдить их дополнительной структурой, которую затем будут использовать скрипты и шаблоны. Единственные средства добавления такой структуры, которые поддерживает RED, – теги- контейнеры, ограниченный набор типов и возможность создавать неограниченное количество дополнительных фактов. Сценарии и шаблоны, входящие в состав NoteTaker, должны будут выполнять следующие задачи:
- Установить, создана ли заметка для данного URL, чтобы принять решение, нужно ли отображать ее.
- По заданному URL получить данные, относящиеся к заметке, включая список ключевых слов, присвоенных ей. Эти данные будут отображены в диалоговом окне редактирования заметки (в состав которого будет входить специальная панель для работы с ключевыми словами).
- По заданному URL получить аннотацию и подробности, относящиеся к заметке. Эти данные будут отображены в текстовых полях панели инструментов, а также в самой заметке.
- Получить список всех ключевых слов. Они будут показаны в раскрывающемся списке панели инструментов.
- Получить все ключевые слова, связанные с данным ключевым словом, все слова, связанные с этими словами, и т.д. Эта информация будет показана пользователю в панели "Ключевые слова" диалогового окна редактирования заметки.
Мы хотим, чтобы помимо выполнения этих видов поиска, приложение могло легко и быстро добавлять, удалять и изменять заметки. Эти задачи достаточно просты, поэтому мы сосредоточимся на приведенном списке, который фактически представляет собой набор запросов.
Mozilla может находить факты в документе RDF независимо от того, каким образом они организованы, но можно принять некоторые меры к тому, чтобы поиск работал быстрее. Кроме того, можно сделать документ RDF более удобным для чтения. В данном случае мы сделаем две вещи:
- Поместим все URL, для которых созданы заметки, в контейнер RDF <Bag>, имеющий URI urn:notetaker:notes. При выполнении запросов 1, 2 и 3 этот контейнер может использоваться для облегчения поиска заметки и ее данных.
- Поместим все URN ключевых слов в контейнер RDF <bag>, имеющий URI urn:notetaker:keywords. Запрос 4 сможет использовать этот контейнер для получения списка ключевых слов.
Запрос 5 очень сложен, поскольку он требует просмотра большого количества фактов, имеющих отношение к ключевым словам. На данном этапе мы вряд ли можем предпринять что-либо для ускорения этого запроса. Как бы он ни выполнялся, для этого понадобится посмотреть все факты о связях между ключевыми словами или большую их часть.
Поскольку предполагается, что в документе RDF <Bag> и другие теги-контейнеры представляют собой субъекты фактов, мы добавим тег верхнего уровня <Description>, имеющий URN urn:notetaker:root. Оба наших контейнера <Bag> будут значениями свойств этого тега.
К этому и сводится все необходимое нам моделирование. Седьмой этап плана представляет собой механическое преобразование модели данных в документ RDF. Каркас документа RDF (простейший документ, не содержащий информации о конкретных заметках и ключевых словах), показан в листинге 11.18.
<?xml version="1.0"?> <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmnls:NT="http://www.mozilla.org/notetaker-rdf#> <Description about="urn:notetaker:root"> <NT:notes> <Seq about="urn:notetaker:notes"/> </NT:notes> <NT:keywords> <Seq about="urn:notetaker:keywords"/> </NT:keywords> </Description> </RDF>Листинг 11.18. База данных NoteTaker в RDF в отсутствие заметок.
Если добавить к этому документу факты о заметке, представленные в листинге 11.17, результатом будет документ, представленный в листинге 11.19 8В окончательном документе RDF мы используем английские имена свойств: notes – заметки; keywords – ключевые слова; summary – аннотация; details – подробности; top – сверху; left – слева; width – ширина; height – высота; label – метка; related – связано.
<?xml version="1.0"?> <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmnls:NT="http://www.mozilla.org/notetaker-rdf#> <Description about="urn:notetaker:root"> <NT:notes> <Seq about="urn:notetaker:notes"> <li resource="http://saturn/test.html"/> </Seq> </NT:notes> <NT:keywords> <Seq about="urn:notetaker:keywords"> <NT:keyword resource="urn:notetaker:keyword:cool/> <NT:keyword resource="urn:notetaker:keyword:test/> </Seq> </NT:keywords> </Description> <!-- одна заметка --> <Description about="http://saturn/test.html"> <NT:summary>Моя аннотация<NT:summary/> <NT:details>Мои подробности<NT:details/> <NT:top>100<NT:top/> <NT:left>90<NT:left/> <NT:width>80<NT:width/> <NT:height>70<NT:height/> <NT:keyword resource="urn:notetaker:keyword:test"/> <NT:keyword resource="urn:notetaker:keyword:cool"/> </Description> <!-- строки ключевых слов --> <Description about="urn:notetaker:keyword:test label="test"/> <Description about="urn:notetaker:keyword:cool label="cool"/> <!-- связи между ключевыми словами, пока есть одна --> <Description about="urn:notetaker:keyword:test"> <NT:related resource="urn:notetaker:keyword:cool"> </Description> </RDF>Листинг 11.19. База данных NoteTaker в RDF с одной заметкой.
Некоторые теги <NT:keyword> повторяются в этом файле. При разработке всегда существует выбор между простотой данных и простотой системы, которая должна делать запросы к этим данным. В данном случае мы допустили некоторое дублирование данных, чтобы сделать более простым выполнение запросов (см. "Шаблоны" "Шаблоны").
Таким образом, мы завершили работу над моделью данных для NoteTaker и одновременно разработали формат файла для хранения этих данных. Мы используем RDF потому, что NoteTaker является приложением, работающим на стороне клиента (не получающим данные с сервера), и объемы хранимых данных будут, скорее всего, невелики.