Блоґ одного кібера

Історія хвороби контуженого інформаційним вибухом

Продовжуємо спілкування з Freebase

leave a comment »

Першу частину читайте тут.

Назви, ідентифікатори та простори імен

Кожен вузол має унікальний ідентифікатор, але не зобов’язаний мати унікальне ім’я. Вузли /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, а через []. Тоді потрібне нам значення потрапить в перший елемент списку.

далі буде…

Written by bunyk

2 Квітня, 2011 at 22:14

Опубліковано в Кодерство, Павутина

Tagged with ,

Залишити коментар

Цей сайт використовує Akismet для зменшення спаму. Дізнайтеся, як обробляються ваші дані коментарів.