Користувач:Bunyk/Редакція
Качаємо останню версію freebase-python. Розпаковуємо архів, переходимо в нього і пишемо:
sudo python setup.py
Після цього пробуємо виконати простенький запит, наприклад дізнатись хто режисер фільму Hot Fuzz:
import freebase
print freebase.mqlread({"name":"Hot Fuzz","type":"/film/film","directed_by":[]})
Словник що передається у функцію - запит MQL. Запити MQL записуються в форматі JSON, і всі поля що містять значення "[]", або "null" заповнюються даними з бази. Детальна інструкція по MQL тут.
Архітектура Metaweb
[ред.]Тепер трохи відійдемо від практики, і розглянемо структуру бази даних.
База даних Metaweb відрізняється від звичайних реляційних БД. Вона являє собою граф з вузлів та відношень. Також є додаткова частина - сховище медіа (content store). Вона призначена для зберігання бінарних файлів (BLOB в SQL), кожному з яких відповідає вузол графа. Ключем до файлу є його SHA2 хеш, це забезпечує відсутність дублікатів.
Вузли та зв'язки
[ред.]На найнижчому рівні база даних Metaweb - це граф вузлів та зв'язків між ними. Кожен вузол має унікальний ідентифікатор та запис про те коли, й ким він був створений. Окрім ідентифікатора дати та автора вузли не містять жодної інформації. Все найцікавіше зберігається в зв'язках між вузлами (чи між вузлами та примітивами).
Зв'язки - це кортежі з 4 елементів (насправді 6, зв'язок теж має автора та час створення). Перший елемент From - перший (лівий) вузол зв'язку. Другий - Property - тип зв'язку. Наступне значення - To другий (правий) вузол зв'язку. Value - значення. Вузол може перебувати у відношенні з іншим вузлом, або зі значенням, або з обома одночасно, залежно від типу (Property) зв'язку.
Наприклад:
From | Property | To | Value |
---|---|---|---|
/en/the_police |
/type/object/name |
/lang/en |
The Police |
/en/zenyatta_mondatta |
/type/object/name |
/lang/en |
Zenyatta Mondatta |
/en/zenyatta_mondatta |
/music/album/artist |
/en/the_police |
Перший рядок каже, що вузол /en/the_police
, пов'язаний зв'язком /type/object/name
(ім'я) з вузлом /lang/en
(англійська мова) та значенням "The Police", тобто англійською мовою вузол /en/the_police
називається "The Police". Другий дає подібну інформацію про альбом "Zenyatta Mondatta". Третій - каже про те, що альбом та група пов'язані через /music/album/artist
, тобто "Zenyatta Mondatta" - альбом групи "The Police".
Властивості
[ред.]Властивості об'єктів (вузлів) в БД задаються типами зв'язків. Можна було помітити що колонка Property попередній таблиці містить ідентифікатори подібні до ідентифікаторів вузлів. А все тому, що тип зв'язку теж є вузлом, і його теж можуть створити користувачі, і він теж може попадати в поля From та To. Наприклад:
From | Property | To | Value |
---|---|---|---|
/type/object/name |
/type/object/name |
/lang/en |
Name |
/music/album/artist |
/type/object/name |
/lang/en |
Artist |
/music/track/album |
/type/object/name |
/lang/en |
Appears On |
/music/album/artist |
/type/property/unique |
true
| |
/music/track/album |
/type/property/unique |
false
|
Перший рядок цієї таблиці має доволі цікавий сенс. Він каже нам про те, що ім'я властивості ім'я англійською мовою "Name".
Напрямки властивостей
[ред.]Напевне досить очевидно, що граф бази даних - напрямлений, так як для зв'язку конкретно вказано куди і звідки. Але Metaweb вміє переміщуватись по графу в обидві сторони.
Наприклад розглянемо зв'язок між альбомами та треками. Кожна композиція (трек) має властивість "альбом[и]" в яких вона була видана. Ми можемо здійснити запит "дізнатись в яких альбомах публікувалась Driven to Tears", і Metaweb знайде всі значення поля "To" кортежів, в яких поле "From" містить "Driven to Tears", а поле "Property" - /music/track/album
. Можна задати протилежний запит - "які треки містить альбом Zenyatta Mondatta?". Metaweb дасть нам значення поля "From" всіх кортежів які містять "Zenyatta Mondatta" в полі "To", та /music/track/album
в "Property".
Metabase має як властивість альбоми в яких видавалась композиція (/music/track/album
), так і протилежну - композиції альбому. (/music/album/track
). Такі властивості називаються взаємними (англ. reciprocal properties), і такий їх зв'язок теж описується в графі:
From | Property | To | Value |
---|---|---|---|
/music/track/album |
/type/property/reverse_property |
/music/album/track |
Значення властивостей
[ред.]Деякі властивості є базовими, тобто вбудовані в архітектуру і мають чітко прописане значення. Наприклад на /type/object/name
накладається обмеження, що кожен об'єкт хоча й може мати багато імен, але лише одне кожною мовою. Іншою властивістю вбудованою в архітектуру є наприклад /type/property/unique
, яка показує чи властивість вказує на один об'єкт, чи на множину.
Щоправда більшість властивостей не такі. /music/album/artist
не є частиною архітектури. Його створили користувачі freebase.com для запису інформації про музику. Сам ідентифікатор властивості дає нам підказку про те що вона означає, яка підтверджується тим, що ця властивість має ім'я "Artist" (через зв'язок /type/object/name
). Ми навіть можемо отримати коротку інструкцію використання властивості, якщо використаємо властивість /freebase/documented_object/tip
.
Таким чином значення /type/property/unique
закодоване прямо в реалізації БД, а значення /music/album/artist
існує тільки в головах користувачів.
Назви, ідентифікатори та простори імен
[ред.]Кожен вузол має унікальний ідентифікатор, але не зобов'язаний мати унікальне ім'я. Вузли /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
, а через []
. Тоді потрібне нам значення потрапить в перший елемент списку.
Так само працює зовнішній запит. Якщо заповнити всі дані можна лише одним способом, то ми могли б написати наприклад:
{
"name":"Ukrainian"
"id":null,
"type":[]
}
але база даних скаже що результатів аж ніяк не один, тому треба буде помістити цей запит всередину списку []
. Ну, або уточнити тип аби залишився лише один результат.
Вкладені запити
[ред.]Як ми знаємо, властивості - це теж вузли, і вони теж мають властивості, такі як, наприклад, ім'я, очікуваний тип, та якусь документацію. Ми уточнюємо наш запит вставивши всередину списку "properties
" підзапит що задає поля властивостей, які нас цікавлять:
[{
"id": "/film/film",
"type": "/type/type",
"properties": [{
"id": null,
"name": null,
"expected_type": null,
"/freebase/documented_object/tip": null
}]
}]
Так само ми можемо зробити з акторами що грають в фільмі. Поле "starring"
має очікуваний тип /film/performance
, всередині якого збегігають дані про актора та персонажа якого він грає. Тому ми можемо написати такий запит:
[{
"name":"Yes man",
"type":"/film/film",
"starring":[{"actor":null, "character":null}]
}]
і отримаємо в відповіді такий текст:
"result": [{
"name": "Yes Man",
"starring": [
{
"actor": "Terence Stamp",
"character": "Terrence Bundley"
},
{
"actor": "Bradley Cooper",
"character": "Peter"
},
і так далі
ми можемо зробити ще один вкладений запит, і наприклад попросити вказувати окрім імені ще і стать актора:
[{
"name": "Yes man",
"type": "/film/film",
"starring": [{
"actor": {
"name": null,
"/people/person/gender": null
},
"character": null
}]
}]
Зауважте що властивість /people/person/gender
вказана повністю. Це потрібно тому, що актор має очікуваний тип /film/actor
, а тип /film/actor
не передбачає жодної статі. Зате кожен конкретно взятий актор також є людиною, тому також належить типу /people/person/
, який вже має таку властивість як gender
.
Помінявши поля в яких ми вказуємо null, і заповнивши наприклад ім'я актора, можна отримати всі фільми в яких знімалась Зоуі Дешанель, разом з персонажами яких вона грала, якщо такі відомі:
[{
name:null,
"type": "/film/film",
"starring": [{
"actor": {
"name": "Zooey Deschanel"
},
"character": null
}]
}]
Аналогічний, і навіть трохи краще оформлений результат можна отримати вивернувши запит навиворіт:
[{
"name": "Zooey Deschanel",
"type": "/film/actor",
"film": [{
"film": null,
"character": null
}]
}]
Тепер в кожному фільмі не буде явно вказано те, що в ньому грала Зоуі Дешанель, а просто даний список фільмів та персонажів.
Основні властивості
[ред.]Фільм в попередньому запиті, як і персонаж описувався своєю назвою:
"character": "Trillian",
"film": "The Hitchhiker's Guide to the Galaxy"
Але ми вже точно знаємо, що фільм, на відміну від персонажа, про якого в базі даних може справді бути лише одне ім'я - це складний об'єкт з купою властивостей, а не один рядок тексту. А суть в тому, що на запити в яких вказано заповнити масив, або повернути значення, рушій повертає "основну" (англ. default) властивість об'єкта. Для фільмів - це назва. Для об'єктів що не мають назви - ідентифікатор. Якщо замість []
, чи null
запитати {}
то нам повернуть словник в якому заповнені поля id
, name
та type
. Ну, або ми можемо отримати потрібні нам поля за допомогою вкладеного запиту.
Предметна область - кінематограф
[ред.]Загалом вивчення формату в якому представлена певна предметна область на Freebase полягає у вивченні типів. Для цього можна скористатись розділом Schema (наприклад схема для /film/film
), чи скриптом з аналогічною функцією:
import freebase
query = {
"id": "/film/film",
"type": "/type/type",
"properties": [{
"id": None,
"name": None,
"expected_type": None,
"/freebase/documented_object/tip": None
}]
}
res = freebase.mqlread(query)
props = res['properties']
print "{|\n|-\n! Ідентифікатор !! Назва !! Очікуваний тип !! Довідка \n"
for p in props:
print "|-\n| <code>%s</code> || <code>%s</code> || <code>%s</code> || %s" %( p['id'], p['name'], p['expected_type'], p['/freebase/documented_object/tip'])
print "|}"
Результат його роботи, з наступним ручним опрацюванням:
Ідентифікатор | Назва | Очікуваний тип | Довідка |
---|---|---|---|
/film/film/initial_release_date |
Initial release date |
/type/datetime |
Прем'єра. (YYYY-MM-DD) |
/film/film/directed_by |
Directed by |
/film/director |
Режисер |
/film/film/produced_by |
Produced by |
/film/producer |
Продюсер |
/film/film/written_by |
Screenplay by |
/film/writer |
Сценарист |
/film/film/cinematography |
Cinematography |
/film/cinematographer |
Оператор |
/film/film/edited_by |
Edited by |
/film/editor |
Монтаж |
/film/film/music |
Music by |
/film/music_contributor |
Композитор |
/film/film/language |
Languages |
/language/human_language |
Мови |
/film/film/rating |
Rated |
/film/content_rating |
None |
/film/film/estimated_budget |
Estimated budget |
/measurement_unit/dated_money_value |
Бюджет |
/film/film/country |
Country of origin |
/location/country |
Країна |
/film/film/starring |
Performances |
/film/performance |
В ролях |
/film/film/runtime |
Runtime |
/film/film_cut |
Тривалість |
/film/film/soundtrack |
Soundtrack |
/music/soundtrack |
None |
/film/film/genre |
Genres |
/film/film_genre |
Жанри |
/film/film/film_series |
Film Series |
/film/film_series |
None |
/film/film/story_by |
Story by |
/film/film_story_contributor |
None |
/film/film/sequel |
Sequel |
/film/film |
Сиквел |
/film/film/prequel |
Prequel |
/film/film |
Приквел |
/film/film/film_format |
Film format |
/film/film_format |
None |
/film/film/costume_design_by |
Costume design by |
/film/film_costumer_designer |
Responsible for the overall design of costumes of the film, individual designs/gowns for a star should be listed in Other crew |
/film/film/other_crew |
Other crew |
/film/film_crew_gig |
Used for all the other crew members of a film production that do not have an existing specifically named crew property (eg. gaffer, best boy, greens supervisor, make-up artist, etc.). |
/film/film/trailers |
Trailers |
/common/webpage |
Трейлери |
/film/film/distributors |
Distributors |
/film/film_film_distributor_relationship |
None |
/film/film/other_film_companies |
Other film companies |
/film/film_film_company_relationship |
None |
/film/film/production_companies |
Production companies |
/film/production_company |
None |
/film/film/tagline |
Tagline |
/type/text |
Слоган |
/film/film/release_date_s |
Release date(s) |
/film/film_regional_release_date |
Прем'єри в регіонах |
/film/film/film_festivals |
Film festivals |
/film/film_festival_event |
Фестивалі на яких демонструвався фільм. |
/film/film/nytimes_id |
NY Times ID |
/type/enumeration |
None |
/film/film/featured_song |
Featured Song |
/film/film_featured_song |
Song(s) that were written specifically for the film (or pre-existing song(s) used for great effect within the film). This is meant for the notable (or so noted in the credits for pre-1980's films) and not every single song that is placed within a production (think of Casablanca's "As Time Goes By," Dirty Dancing's "We're Having the Time of Our Lives," Goldfinger's "Goldfinger," Mortal Kombat's "Theme From Mortal Kombat," and Ghostbuster's "Ghostbusters"). |
/film/film/metacritic_id |
Metacritic film ID |
/type/enumeration |
None |
/film/film/apple_movietrailer_id |
Apple movie trailer ID |
/type/enumeration |
None |
/film/film/rottentomatoes_id |
Rotten Tomatoes ID |
/type/enumeration |
None |
/film/film/executive_produced_by |
Executive produced by |
/film/producer |
Виконавчий продюсер |
/film/film/film_casting_director |
Casting director |
/film/film_casting_director |
The lead person(s) involved in casting of this film production |
/film/film/film_production_design_by |
Production design by |
/film/film_production_designer |
The lead production designer(s) responsible on the film production |
/film/film/film_art_direction_by |
Art direction by |
/film/film_art_director |
The art director(s) of this film production |
/film/film/film_set_decoration_by |
Set Decoration by |
/film/film_set_designer |
Set designer for film production (assistants and other associated set designer roles are to be listed in Other crew) |
/film/film/traileraddict_id |
Trailer Addict ID |
/type/enumeration |
None |
/film/film/gross_revenue |
Gross revenue |
/measurement_unit/dated_money_value |
Касовий збір |
/film/film/pre_production |
Pre-production |
/measurement_unit/time_interval |
The start and end dates of the film's pre-production (includes all phases of development for the film) before actual filming begins. |
/film/film/filming |
Filming |
/measurement_unit/time_interval |
The start and end dates for the production (shooting film) phase of creating the the film. |
/film/film/post_production |
Post-production |
/measurement_unit/time_interval |
The start and end dates for the post-production (effects, editing, scoring etc.). |