Продовжуємо спілкування з Freebase
Назви, ідентифікатори та простори імен
Кожен вузол має унікальний ідентифікатор, але не зобов’язаний мати унікальне ім’я. Вузли /lang/en
, /en/english_people
, та /authority/gnis/57724
відповідають мові, нації, та місту в штаті Акранзас, і всі мають однакову назву англійською – “English”. А якщо є два вузли що мають однакові ідентифікатори – то це один і той самий вузол. Ідентифікатори не є незмінними, їх можна редагувати як і вузли. Можна змінити ідентифікатор вузла, і можна зробити так щоб ідентифікатор показував на інший вузол.
Такий ідентифікатор як наприклад /type/object/name
складається з двох частин – /type/object
, яка називається простором імен (і в свою чергу теж є ідентифікатором з простору імен /type
), та name
– який називається ключем. Це трохи нагадує структуру директорій Unix.
Ідентифікатори на зразок /en/the_police
написані майже англійською мовою, тому досить зрозумілі. Щоправда такі ідентифікатори вдається надати не всім вузлам, тому окрім них, кожен вузол ідентифікується ключами з простору імен guid (англ. globally-unique identifier), наприклад пісня “Driven to Tears” має ідентифікатор /guid/9202a8c04000641f800000000129a87a
. На рівні реалізації БД, зв’язки між вузлами встановлюються записом в поля кортежу From, To та Property саме цих ідентифікаторів, а не тих, що були показані в таблицях. guid – фундаментальна властивість кожного вузла, на відміну від інших ідентифікаторів. Ці інші ідентифікатори можна отримати через зв’язки типу /type/object/key
де в полі “To” дається простір імен, а в “Value” – ключ. Простір імен теж вузол, тому теж має ідентифікатор.
Об’єкти та типи
Дотепер ми казали що база даних складається з вузлів та зв’язків, і старанно уникали слова “об’єкт” (можете перевірити). Але воно зустрічалось в таких ідентифікаторах як наприклад /type/object/key
, чи /freebase/documented_object/tip
. Тому, хоча дуже потрібно знати що на найнижчому рівні Freebase складається з кортежів що задають зв’язки між вузлами, але варто поглянути на неї через об’єктно-орієнтовані окуляри O^O.
Тому замість того щоб думати про зв’язки між вузлами пов’язаними з вузлом групи “The Police” дані можна представити таким псевдокодом:
{ id: "/en/the_police", name: "The Police", /music/artist/album: { id: "/en/zenyatta_mondatta", name: "Zenyatta Mondatta", /music/album/track: { name: "Driven to Tears", /music/track/length: 200.266 } /music/album/track: { name: "Canary in a Coalmine", /music/track/length: 146.506 } } }
Нагадує JSON, правда? Це не випадково. Ми бачимо об’єкт, де id = /en/the_police
та name = "The Police"
. id
та name
– скорочення що відповідають універсальним властивостям /type/object/id
та /type/object/name
, які доступні всім об’єктам. Всередині нього є об’єкт що відповідає альбому, а всередині альбому – об’єкти для треків (загалом їх більше, але вони були опущені для лаконічності). Відомості про альбоми та треки покладаються на властивості /music/artist/album
та /music/album/track
.
Щоб завершити огляд Freebase з об’єктно-орієнтованого погляду потрібно розповісти про типи. Типи – це колекції однотипних властивостей, і об’єкт в Metaweb може бути екземпляром одного чи кількох типів. /en/the_police
– екземпляр типу /music/artist
, /en/zenyatta_mondatta – екземпляр типу /music/album
, і об’єкт що представляє трек є екземпляром /music/track
. Типи, як і властивості інше представлені вузлами. Властивості є екземплярами типу /type/property
, а кожен тип – екземпляр /type/type
(що означає що /type/type
– екземпляр самого себе!) Об’єкти що мають властивості використовують свої ідентифікатори в якості простору імен для ідентифікаторів типів цих властивостей. Не переживайте якщо не зрозуміли попереднє речення, я й сам його ледве зрозумів. Поясню на прикладах. Властивості /music/track
, такі як /music/track/album
чи /music/track/length
, мають ідентифікатори що починаються з /music/track
.
Типи та властивості пов’язані унікальною властивістю /type/property/schema
, яка описує зв’язок між властивістю, та типом до якого ця властивість входить. Зворотня властивість /type/type/properties
дає всі властивості типу.
Існує ще дві, важливіші властивості що включають типи. Це /type/object/type
, що аналогічно до /type/object/name
використовується практично для кожного об’єкта в базі. Задає всі типи, екземплярами яких є об’єкт. Наприклад /en/the_police
є екземляром /music/artist
, /common/topic
, /music/producer
, та /music/musical_group
.
Знати тип потрібно з двох причин: якщо ми знаємо що об’єкт – це /music/artist
, ми знаємо що є сенс запитати про значення його властивості /music/artist/album
. І тип об’єкта – чудовий уточнювач. Ми можемо знайти багацько об’єктів що називаються “English”, але можемо суттєво звузити пошук, якщо скажемо що нас цікавлять лише екземпляри /type/lang
.
На додачу до /type/object/type
є ще одна властивість пов’язана з типами. Це унікальна властивість /type/property/expected_type
(очікуваний тип), що для кожної властивості задає тип значення пов’язаного з цією властивістю. Наприклад очікуваним типом властивості /music/artist/album
є /music/album
, а очікуваним типом /music/album/track
є /music/track
.
Додавання типу об’єкта дозволяє нам суттєво спростити його запис:
{ id: "/en/the_police", type: "/music/artist", name: "The Police", album: { id: "/en/zenyatta_mondatta", name: "Zenyatta Mondatta", track: { name: "Driven to Tears", length: 200.266 } track: { name: "Canary in a Coalmine", length: 146.506 } } }
Ми додали в зовнішній об’єкт властивість type
, який задав тип /music/artics
. Це дозволяє нам використати просте ім’я властивості замість /music/artist/album
. Більше того, так як ми знаємо що очікуваним типом для /music/artist/album
є /music/album
, ми можемо простом використати ім’я властивості track
замість /music/album/track
. Аналогічно /music/track/length
скорочується до length
.
Metaweb Query Language
Для зручності дослідження Freebase, та вивчення мови запитів, розробники створили “Query Editor” – зручний інструмент що працює прямо в браузері, та має підказки з автодоповненням та інші зручності.
Як вже згадувалось вище, MQL базується на JSON, тобто структура даних аналогічна до тієї яку ми можемо створити з словників, масивів та базових типів (рядків та чисел) Python. Відмінність між JSON та Python покажемо в таблиці:
Python | JSON | Значення |
---|---|---|
None |
null |
Немає даних |
False |
false |
|
True |
true |
|
'"' |
"\"" |
Рядки в JSON – тільки в подвійних лапках |
На цьому здається все. Відмінностей від Python не так і багато.
Спробуємо написати простий запит, який наприклад нам дасть список властивостей, які характеризують тип /film/film
:
[{ "id": "/film/film", "type": "/type/type", "properties": [] }]
В результаті виконання запиту, в правому полі (якщо ви писали запит в Query Editor) буде приблизно такий текст:
{ "code": "/api/status/ok", "result": [{ "id": "/film/film", "properties": [ "/film/film/initial_release_date", "/film/film/directed_by", далі багато властивостей пропущено для компактності ], "type": "/type/type" }], "status": "200 OK", "transaction_id": "cache;cache03.p01.sjc1:8101;2011-04-02T19:19:20Z;0041" }
Як вже було сказано, відповідь на запит – це цей же запит, з заповненими полями, які мали значення []
чи null
. Цю відповідь ми можемо бачити в полі “result” відповіді. Окрім “result” є інші поля з службовою інформацією.
А тепер спробуємо написати в нашому запиті замість []
null
. Тобто так:
[{ "id": "/film/film", "type": "/type/type", "properties": [] }]
Відповідь сервера зміниться. Він повідомить нас про помилку:
{ "code": "/api/status/error", "messages": [{ "code": "/api/status/error/mql/result", "info": { "count": 52, "result": [ "/film/film/initial_release_date", "/film/film/directed_by", знову багато пропущено... ] }, "message": "Unique query may have at most one result. Got 52", "path": "properties", "query": [{ "error_inside": "properties", "id": "/film/film", "properties": null, "type": "/type/type" }] }], "status": "200 OK", "transaction_id": "cache;cache03.p01.sjc1:8101;2011-04-02T19:25:14Z;0013" }
Суть помилки пояснюють в полі “message”: написавши в полі null
ми очікуємо що база даних заповнить його значенням, але база даних знайшла їх аж 52 штуки. Простим вирішенням помилки є повернення до попередньої версії запиту, коли ми просили базу даних заповнити список.
Тут варто зауважити що навіть об’єкт може мати навіть кілька типів, тому наприклад запит
[{ "id": "/film/film", "type":null }]
теж дасть аналогічну помилку. З іншого боку, якщо навіть властивість може мати лише одне значення (як наприклад id
), ми все одно можемо запитати не через null
, а через []
. Тоді потрібне нам значення потрапить в перший елемент списку.
далі буде…
Залишити коментар