Работа с Google Fusion Tables — JS и PHP

Summary: Возникла задача — создать высоконагруженное веб-приложение при крайне ограниченных ресурсах сервера.
В качестве хранилища данных был выбран Fusion Tables от Google.
Что описано в статье: работа с Fusion Tables из
а) Javascript — только выборка данных;
б) PHP (Zend Framework) — выборка, добавление и обновление;
в) пользовательский интерфейс от Google — создание таблиц и представлений.
Чего нет в статье:
а) нагрузочного тестирования;
б) подробного описания приложения — упор делается именно на использование Fusion Tables в своих PHP+Js проектах.

У компании Google есть большое количество различных продуктов. — без банальностей.
Только ленивый веб-программист не использовал в своих проектах карты от Google. Просто вставляется, красиво выглядит, не грузит собственный сервер, удобный, хорошо документированный (по крайней мере для v.2) API, высокая по крайней мере, не низкая скорость работы — плюсы есть. «Возможно, FT тоже будет приятным» — подумал я. Правда, не проверял. И вот результат.


Недавно у меня появилась идея написать какое-нибудь приложение для всем известной социальной сети — просто в качестве эксперимента. После небольшого раздумья выбор пал на простую доску объявлений — место, где можно предложить кому-нибудь ненужный тебе хлам. Приложение я здесь описывать не буду, ссылку не дам, пока оно на модерации. С флешем я, к сожалению, «на Вы», поэтому писал на стандартной связке PHP + Js. Необычного в данном приложении то, что вместо стандартного mySQL в качестве хранилища был выбран сервис от Google — Fusion Tables. Почему? Очень просто — нет никаких гарантий монетизации. Под рукой свободных серверных мощностей нет — есть старенький, плохо настроенный девелоперский сервер. Делать какие-либо вложения на начальном этапе не хотелось — напомню: проект писался просто ради интереса. А во время поиска ответа на какой-то вопрос по Google Maps API наткнулся на статью про Fusion Tables.

Немного про права доступа.


Запрашивать данные из таблицы будет напрямую яваскрипт. Т.е. придется сделать таблицу общедоступной (по крайней мере для чтения). Я не знаю, каким образом можно обфусцировать js настолько, чтобы человек, желающий взломать таблицу не смог из скрипта выудить логин и пароль.
Соответственно, давать всем подряд права на запись в таблицу нет желания. Поэтому запись данных в таблицу будет происходить только на серверной стороне. Т.е. пользователь добавляет объявление, js передает введенные пользователем данные на сервер, php-скрипт их подхватывает, авторизируется на Google FT и вставляет данные в таблицу.

Заранее согласен со всеми упреками к предыдущему абзацу! Повторюсь — я просто не знаю, как в js можно скрыть логин/пароль.

Итак, поехали:

Шаг 1 — создание таблиц


Через пользовательский интерфейс FT создается таблица. Интерфейс интуитивно понятен, не требует каких-то специфических знаний. Субъективно — чем-то напоминает MS Access.
Там же можно вставить несколько строк в таблицу. Обычно, при работе с mySQL, я для этого использую phpMyAdmin
На видео пример создания простой таблицы с тремя полями.


Да, скажу сразу: в FT имена таблицы — числа. К примеру, для созданной таблицы это 596524. «Data» — всего лишь псевдоним, его нельзя использовать в запросах.

Сразу забегу наперед: для простых таблиц поле id (первичный autoincrement-ключ) создавать не требуется, у каждой таблицы появится «скрытое» поле ROWID. Создавать id имеет смысл только в том случае, если Вы планируете делать JOIN по нескольким таблицам. Как это сделать? В верхнем меню на странице таблицы есть кнопка «Merge». Появившийся диалог позволит Вам создать что-то вроде представления (view в терминах mySQL).

Теперь нужно расшарить таблицу. Для того чтобы из js без авторизации получать данные. Таблица будет доступна только для чтения для всех. Изменять/добавлять данные сможет только автор.


Таблица готова. Вот ссылка на нее.

Шаг 2 — отображение данных — JavaScript


На моем почти сервере хранится html-страница и js файл. При загрузке страницы данные для отображения (список объявлений) загружается непосредственно с серверов Google.

Отображение данных из таблицы производится с помощью модуля visualization. Взято вот отсюда.
Для этого в заголовок index.html добавляем ссылку на javascript — www.google.com/jsapi, а в файл default.js строку
google.load('visualization', '1');

Более подробно о загрузке модулей Google API в Javascript можно почитать вот здесь.

Помимо этого нужно выполнить запрос к таблице и указать функцию — обработчик ответа.
//формирование запроса
        var queryText = 'SELECT id, name FROM 596524;';
//url запроса
        var reqUri = 'http://www.google.com/fusiontables/gvizdata?tq='  + encodeURIComponent(queryText);
//создаем объект visualization
        var query = new google.visualization.Query(reqUri);
//посылаем запрос и указываем функцию - обработчик ответа
        query.send(displayData);


UPD:Как заметил dkukushkin, при использовании IE (вплоть до 9-го) при наличии в выборке кириллицы (наверняка не только!) пользователь видит "?" вместо символов. Посему убираем Google.vizualization и делаем запрос своими силами — хвала jQuery, нет нужды писать страшный XMLHttpRequest руками
        var queryText = 'SELECT id, name FROM 596524;';
	$.ajax({
		url:'http://tables.googlelabs.com/api/query?sql='+queryText+'&jsonCallback=?',
		type: 'GET',
		success:function(resp){
			displayData(resp);
		},
		dataType:'json'
	});

У меня после этого баг прошел, и все сразу стало красиво. Хотя я планировал долгую битву — работа с заголовками например так, но ie на этот раз удивил и отобразил кириллицу нормально. Формат полученных данных немного отличается от того, который был при использовании Vizualization, так что функция displayData должна быть изменена тоже. Но там ничего страшного, возвращается обычный JSON объект, у которого есть параметр table, содержащий 2 массива cols и rows — все просто и понятно.

Как уже было написано выше — в качестве имен таблиц в запросах используются их ID (кстати, можно увидеть интересный факт — 26 февраля я создал таблицу с ID = 505 xxx, а созданная сегодня таблица data получила ID 596 524 — можно сделать вывод: ~ 91 000 таблиц меньше чем за месяц было добавлено на FT).

И, наконец, функция вывода результатов на странице
function displayData(response) {
 
  //более детальная информация об объекте response доступна здесь
  //http://code.google.com/apis/visualization/documentation/reference.html#QueryResponse
  numRows = response.getDataTable().getNumberOfRows();
  numCols = response.getDataTable().getNumberOfColumns();
  
  //собираем результат в строку
  fusiontabledata = "<b>";
  for(i = 0; i < numCols; i++) {
    fusiontabledata += response.getDataTable().getColumnLabel(i) + ",";
  }
  fusiontabledata += "</b><br />";
  
  for(i = 0; i < numRows; i++) {
    for(j = 0; j < numCols; j++) {
      fusiontabledata += response.getDataTable().getValue(i, j) + ", ";
    }
    fusiontabledata += "<br />";
  }  
  //вывод на странице
   document.getElementById('echoer').innerHTML = fusiontabledata;
}


Пример на jsFiddle

Теперь ложка дегтя. FT использует язык запросов SQL. Подробнее можно почитать здесь. Но! Язык этот имеет несколько неожиданных ограничений — например нет оператора OR в условиях команды SELECT (появился, смотри UPD2) (и даже нет возможности заменить его чем-то вроде (a | b) = !( !a & !b ), поскольку нет оператора NOT), на эту тему даже создали proposal). Хотя есть встроенные функции для работы с координатами. FT вообще направлен на поддержку карт от Google.

Во избежание кеширования запросов (я не разбирался где именно они кешируются — в браузере, visualization или сам FT) на реальном проекте имеет смысл добавить в условие запроса что-то вроде «AND name not equal to 234567», где 234567 — случайное число (см. Math.getRandomInt()).

По-хорошему конечно нужно было бы написать свой собственный класс для отправки запросов и обработки ответов. Если присмотреться — нет ничего сложного. SELECT — отправляется GET-запросом по специальному адресу, ответ от сервера получаем в формате JSON. Я так и поступил — получил довольно сырой код, который здесь выкладывать стыдно. А буквально сегодня наткнулся на замечательный топик от trurl123 и понял, что нужно еще поизучать MVC в js и, возможно, отказаться от своих наработок.

Шаг 3 — добавление данных — PHP + Zend Framework


Когда я взялся за эту часть работы — ничего не предвещало беды. Zend Framework содержит целый API для работы с сервисами от Google. Но не все оказалось так радужно. В общем, класс для работы с FT пришлось писать самому, взяв за основу Zend_Gdata_Base и воспользовавшись нерабочим решением под Drupal.

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

Для того чтобы его использовать, необходимо подключить собственно сам файл, а также Zend/Gdata/ClientLogin.php.

Пример:
//авторизация (по крайней мере данные для авторизации указываются здесь)
$client = Zend_Gdata_ClientLogin::getHttpClient('your_login_here@gmail.com', 'your_pass_here', 'fusiontables');
//создание экземпляра класса
$base = new Zend_Gdata_Fusion($client);

//запрос на выборку
$sql = "SELECT ROWID FROM 596524 WHERE id = 1;";
$rowdata =  $base->query($sql)->get_array();
print_r($rowdata);
//вставка строк - согласно API необходимо перечислить все столбцы таблицы
$newRowId = $base->insertRow('596524',array(
	'id' => time(),
	'name' => 'trird row',
	'added' => date('n/j/y'),
) );
//обновление строки
$base->updateRow(
    '596524', //ID таблицы
    array('name' => 'new first row'), //ассоциативный массив значений
    $rowdata[1][0] //ROWID полученный в запросе на выборку
);


Если дойдут руки, доведу файл до ума и залью на Zend Proposal.

Еще один момент: если нет нужды выполнять INSERT/UPDATE команды, можно использовать Zend_Base, например как описано здесь. Дело в том что по спецификации через GET запрос можно выполнить только SELECT ( http://code.google.com/intl/ru/apis/fusiontables/docs/developers_guide.html#Updating "… To update a row, send an authenticated POST request..." )

На этом все. Любые камни замечания приветствуются. Спасибо.

PS: и конечно, я понимаю, что нет смысла держать коммерческий проект на шаровых услугах, но, если мое приложение станет коммерческим (т.е. если у него появится достаточное количество пользователей и я придумаю как на них заработать), я немедленно мигрирую на собственный сервер. А на данный момент я имею:
— достаточно красивое и быстро работающее приложение при небольших начальных затратах;
— веру что все будет работать так же быстро и под нагрузкой.

PPS: Да, я надеюсь что code highlighter глючит только при предпросмотре

Ссылки:
Fusion Tables API — http://code.google.com/intl/ru/apis/fusiontables/docs/developers_guide.html
Fusion Tables SQL syntax — http://code.google.com/intl/ru/apis/fusiontables/docs/developers_reference.html
Fusion Tables UI — http://tables.googlelabs.com/
Zend Gdata — http://framework.zend.com/manual/en/zend.gdata.html
PHP-класс для работы с FT — http://barahlo.semero.com/description/Zend_Gdata_Fusion.zip
UPD2: это просто праздник какой-то! сегодня ночью обновили proposal и теперь появился аналог OR! Сам еще не тестировал, но надеюсь все будет ок.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 28

    +3
    Повторюсь — я просто не знаю, как в js можно скрыть логин/пароль.
    А никак нельзя. Даже если Вы все жестко обфусцируете, это не защитит от банального сниффинга трафика.
      0
      А как насчёт использования md5(password + salt) на стороне JavaScript? Насколько мне известно для JavaScript md5 давно реализован.
        +2
        Это же всё на клиенте выполняется. Открою фаербаг и посмотрю какой пароль передается гуглу.
          –2
          Ну свой пароль то вы посмотрите, а чужой как? По RDP подключитесь к удалённой машине и на ней тоже в FireBug быстренько-незаметненько подсмотрите? :)
            +3
            В статье речь про логин/пароль автора к FT. Он был бы он для всех.
            0
            Ой, я извиняюсь. Не совсем понял суть задачи. В таком случае вы правы, упрятать пароль доступа к таблице вряд ли получится.
        0
        Для того, чтобы не сперли пароль, можно использовать серверный прокси-скрипт для запросов, но некрасивое это решение, т.к. лишний трафик через сервер будет прогоняться.
          +1
          OR и NOT не делают, потому что запросы с ними нельзя быстро по индексам искать — только фуллсканом таблицы. Не знаю, зачем вам они понадобились, но часто можно создать вспомогательную колонку в таблице и туда сохранять значение этого предвычисленного выражения. search_col = (price >= 100) or (queries < 10). Потом искать по search_col=1 уже проще простого средствами базы.

          Зачем вы кеширование средствами базы отключаете — тоже непонятно. Вы же создаёте лишнюю нагрузку на серверы гугла, хотя можно было без этого обойтись.

          И ещё. В документации GFT написано: «Applications using the Google Fusion Tables API can send a maximum of 5 requests per second to the Google Fusion Tables server». На какую посещаемость вы рассчитываете?
            0
            «Зачем вы кеширование средствами базы отключаете» — мне нужно real-time отображение вновь добаленных элементов. Без отключения кеширования они появятся втечение 1 часа.
            "...can send a maximum of 5 requests per second..." — В документации не указано — 5 запросов в секунду с одного IP или всего? При геокодировании используется ограничение именно по IP
              0
              > Без отключения кеширования они появятся втечение 1 часа.
              О как. Это документировано? Я что-то ничего такого не видел.
                0
                Это документировано? Я что-то ничего такого не видел.
                Я тоже. Это вылезло во время тестирвки. Я даже не стал разбираться где именно кешируются ответы. Возможно даже это браузерный кеш.
                У меня запрос из JS отправляется через GET
                var reqUri = 'http://www.google.com/fusiontables/gvizdata?tq='  + encodeURIComponent(queryText);

              0
              OR и NOT не делают, потому что запросы с ними нельзя быстро по индексам искать — только фуллсканом таблицы. Не знаю, зачем вам они понадобились...

              Я планировал сделать кнопку «Объявления друзей» для посетителя. В данном случае в условии запроса появится что-то вроде «user_id = 25 OR user_id = 26 OR user_id = 27», где 25,26,27 — id друзей посетителя. В принципе что-то вроде user_id IN (25, 26, 27) тоже подошло бы, но в FT API я не нашел ничего похожего.
              +1
              Есть еще PHP Fusion Tables Client Library для доступа к FT через PHP.

              Содержит классы для подключения к FT. Помогает создавать и править таблицы (Insert, Update, Delete). И есть класс для загрузки данных в FT из CSV файла. Для простых задач вполне достаточно.
                +1
                Спасибо, я эту библиотеку так и не нашел — видимо слишком сильно надеялся на Zend.
                +1
                ...91 000 таблиц меньше чем за месяц было добавлено на FT.

                Это не факт. У меня при создании таблиц номера шли то больше, то меньше (сначала 60хххх, потом 59хххх)
                  +2
                  Действительно интересно что там с ограничением количества запросов в секунду? Кто то проверял?
                    +1
                    Если (когда) проапрувят приложение — дам отчет.
                    0
                    gmaps-samples.googlecode.com/svn/trunk/fusiontables/gviz_sample.html Opera 11.01 — не работает. Печально.
                      0
                      Хм. Странно. Специально поставил Opera
                      Version 11.01
                      Build 1190
                      И sample, и jsFiddle и приложение отработали нормально.
                      0
                      Хм…
                      > создать высоконагруженное веб-приложение при крайне ограниченных ресурсах сервера.
                      и Zend в моем понимании плохо сочетаются.
                        0
                        Кстати, недавно сам начал использовать FusionTables и нашел 2 бага:

                        1. Выборка по Marged-таблицам с кол-вом записей более 0.5 млн. происходит оооочень медленно. Уже отписал Google, они даже ответили и занесли в баг-треккер. А вот выборка по обычным таблицам происходит довольно шустро.

                        2. При использовании JS Visualization Api из IE вместо русских букв возвращаются знаки вопроса ?????????? ????.. Оказывается в заголовке запроса должен быть Accept-Charset: utf-8. В IE его почему-то нету… У вас русского текста в выдаче нет? Если есть русский текст — проверьте открыть в IE. Я сначала был доволен — потом облом. Не работает в IE. Эту проблему до сих пор не решил. Вы не сталкивались с этой проблемой?
                          0
                          Да, действительно, проверил в explorer-е (8-й), эта проблема присутствует.
                          Придется переписывать строки
                                  var query = new google.visualization.Query(reqUri);
                                  query.send(displayData);
                          

                          своими
                            0
                            Я разобрался почему это. Гугл выдает JSON при отправке запроса на адрес www.google.com/fusiontables/gvizdata?tq= Так вот, если в заголовке запроса не указан Accept-Charset — то возвращается белиберда. А IE почему-то не указывает Accept-Charset. Может и есть способ заставить его указать этот Accept-Charset?

                            А вы как хотите делать? Свой JSON-прокси на сервере поднимать?
                              0
                              Не, не так жестоко.
                              Просто не использовать vizualization, а сделать GET-запрос (например с помощью jQuey.ajax) и руками установить заголовки (в случае с jQuery например так)
                                0
                                Насколько я понимаю, кросс-доменные запросы поддерживают далеко не все браузеры. А вот включение JSON — поддерживают все.

                                Т.е. с помощью jQuery получиться обратиться только к своему домену, а вот к домену Google только через JSON-прокси (либо их либо свой).
                                  +1
                                  Можно сделать вот так вот:
                                  		$.ajax({
                                  			url:'http://tables.googlelabs.com/api/query?sql='+sql+'&jsonCallback=?',
                                  			type: 'GET',
                                  			success:function(resp){
                                  				console.log(resp);
                                  			},
                                  			dataType:'json'
                                  		});
                                  

                                  У меня работает на отлично!
                                    0
                                    Подтверждаю, работает без проблем с IE адрес tables.googlelabs.com/api/query?sql=. Я уже было свой прокси начал юзать.

                                    Вообще довольно неплохо и полнотекстовый поиск работает. Главное теперь чтобы сервис не прикрыли.
                          0
                          Интересно — буквально сутки назад делал аналогичную вешь, только не через Google Fusion Tables, а через Google Spreadsheets. А теперь вот совершенно случайно на вашу статью наткнулся :)
                          В принципе там всё аналогично, только проще (ну и ограничений больше, соответственно)
                          cr-it.livejournal.com/24039.html

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое