Страницы

среда, 10 апреля 2013 г.

ContentService vs Fusion Tables API. Работаем с данными.

Использовать Google Cloud SQL или Google Cloud Storage для хранения данных нет ни малейшего желания - Pricing. Остается работать с тем, что доступно (пока) - таблицы. Для визуализации хотелось бы использовать HtmlService, но скорость сервиса, да и реализация в целом, оставляют желать лучшего (на все мои вопросы ребята из Google ответили однозначно - Caja...). Перманентное напоминание "This application was created by another user, not by Google" меня тоже не особенно впечатляет. Не проблема спрятать в каком-нибудь iframe-e, но скорость HtmlService... Поэтому предлагаю кушать данные непосредственно из HTML. И здесь у меня два варианта: ContentService и Fusion Tables API.

Для начала.

Открываем Google Drive, создаем новую таблицу. Переходим к редактору скриптов, наполняем нашу таблицу данными.
function populate() {
  var sh = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].activate();
  var id = 'id';
  var name = 'name';
  var max = 1000;
  sh.getRange(1, 1, 1, 2).setValues([[id, name]]);
  
  var data = [];
  for(var i=1; i<max; i++) {
    data.push([i, name + i]);
  }
  
  sh.getRange(2, 1, max - 1, 2).setValues(data);
}

Создаем сводную таблицу. В качестве источника данных используем нашу свежесозданную таблицу.

Открываем консоль API, создаем новый проект.

Активируем Fusion Tables API.

Are you ready? Are you ready for the good time...

SELECT *.

Напишем скрипт, который будет раздавать все данные таблицы всем (авторизация в контексте текущего повествования нас не интересует).
function doGet(e) {
  var data = SpreadsheetApp.openById('ID_Вашей_Таблицы')
    .getSheets()[0].getDataRange().getValues();  
  return ContentService.createTextOutput(
    e.parameters.prefix + '(' + JSON.stringify(data) + ')')
    .setMimeType(ContentService.MimeType.JAVASCRIPT);
}

Открываем блокнот, пишем код HTML-страницы. С XMLHttpRequest не прокатит, т.к. ContentService не поддерживает CORS заголовки, поэтому в обоих случаях (ContentService и Fusion Tables API) будем использовать метод JSONP.
<html>
  <head>
    <script>
   var d = {}; 
   d.diff = function() { return new Date() - this.d0};
   
   function gogogo() {     
     var url = 'URL_Вашего_Скрипта;
  var callback = 'gotit';  
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = url + '?prefix=' + callback;  
  document.getElementsByTagName("head")[0].appendChild(script);
  d.d0 = new Date();
      }
   
   function gotit(data) {
     document.getElementById('dd').innerHTML = d.diff() + ' ms';
  var res = '';
  for (var i=1; i<data.length; i++) {
    res += data[i] + '<br/>';  
  }
  document.getElementById('content').innerHTML = res;
      }   
    </script>    
  </head>
  <body>    
    <input type="button" value="Go" onclick="gogogo()"/>
 <span id="dd"></span>
 <div id="content"></div>
  </body>
</html>

Тестируем.

Теперь попробуем через Fusion Tables API.
<html>
  <head>
    <script>
   var d = {};
   d.diff = function() { return new Date() - this.d0};
   
   function gogogo() {     
  var API_KEY = 'Ключ_Вашего_Проекта'     
  var tableId = 'ID_Вашей_Сводной_Таблицы';
  var url = 'https://www.googleapis.com/fusiontables/v1/query?sql=';
  var sql = 'SELECT+*+FROM+' + tableId;  
  var callback = 'gotit';  
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = url + sql + '&key=' + API_KEY + '&callback=' + callback;  
  document.getElementsByTagName("head")[0].appendChild(script);
  d.d0 = new Date();
      }
   
   function gotit(data) {     
  document.getElementById('dd').innerHTML = d.diff() + ' ms';
  var res = '';
  for (var i=0; i<data.rows.length; i++) {
       res += data.rows[i] + '<br/>';  
  }
  document.getElementById('content').innerHTML = res;  
      }   
    </script>    
  </head>
  <body>    
    <input type="button" value="Go" onclick="gogogo()"/>
 <span id="dd"></span>
 <div id="content"></div>
  </body>
</html>

Тестируем.

SELECT WHERE.

Попробуем получить данные, используя SQL-like синтаксис AND и OR. Для ContentService пишем новый скрипт.
function doGet(e) {
  var sh = SpreadsheetApp.openById('ID_Вашей_Таблицы').getSheets()[0];
  var data = sh.getDataRange().getValues();
  
  var request = JSON.parse(e.parameters.request);  
  var res = [];
  var s = String(e.parameters.statement);
  switch (s) {
    case '*':
      res = data;
      break;
    case 'or':     
      for(var i=0; i<request.length; i++) {
        var j=0;
         for(; j<data[0].length; j++) {
          if(data[0][j] == request[i].col)
            break;
         }
        for(var k=0; k<data.length; k++) {
          if(data[k][j] == request[i].val) {
            res.push(sh.getRange(k+1, 1, 1, sh.getLastColumn()).getValues());        
            //break;
          }
        }
      }
      break;
    case 'and':
      var arr = new Array(request.length);     
      var k = 0;
      for(var i=0; i<request.length; i++) {
        var j=0;
         for(; j<data[0].length; j++) {
          if(data[0][j] == request[i].col)
            break;
         }
        for(; k<data.length; k++) {
          if(data[k][j] == request[i].val) {
            arr[i] = true;
            break;
          }
        }
      }
      var b = false;
      for(var i=0; i<arr.length; i++) {
        b += arr[i];        
      }   
      if(!!(b)) res.push(sh.getRange(k+1, 1, 1, sh.getLastColumn()).getValues());
      break;
  }
  
  return ContentService.createTextOutput(
    e.parameters.prefix + '(' + JSON.stringify(res) + ')')
    .setMimeType(ContentService.MimeType.JAVASCRIPT); 
}

Пробуем OR.
<html>
  <head>
    <script>
   var d = {};
   d.diff = function() { return new Date() - this.d0};
   
   function gogogo() {     
     var url = 'URL_Вашего_Скрипта';
  var callback = 'gotit';  
  var script = document.createElement('script');
  // OR statement
  var statement = 'or';
  var request = JSON.stringify([{col:'id', val:'933'},{col:'name', val:'name966'}]);
  script.type = 'text/javascript';
  script.src = url + '?prefix=' + callback + '&request=' + request + '&statement=' + statement;  
  document.getElementsByTagName("head")[0].appendChild(script);
  d.d0 = new Date();
      }
   
   function gotit(data) {
     document.getElementById('dd').innerHTML = d.diff() + ' ms';
  var res = '';
  for (var i=0; i<data.length; i++) {
    res += data[i] + '<br/>';  
  }
  document.getElementById('content').innerHTML = res;
      }   
    </script>    
  </head>
  <body>    
    <input type="button" value="Go" onclick="gogogo()"/>
 <span id="dd"></span>
 <div id="content"></div>
  </body>
</html>
Тестируем.

Пробуем AND.
<html>
  <head>
    <script>
   var d = {}; 
   d.diff = function() { return new Date() - this.d0};
   
   function gogogo() {     
     var url = 'URL_Вашего_Скрипта';
  var callback = 'gotit';  
  var script = document.createElement('script');
  // AND statement
  var statement = 'and';
  var request = JSON.stringify([{col:'id', val:'933'},{col:'name', val:'name933'}]);
  script.type = 'text/javascript';
  script.src = url + '?prefix=' + callback + '&request=' + request + '&statement=' + statement;  
  document.getElementsByTagName("head")[0].appendChild(script);
  d.d0 = new Date();
      }
   
   function gotit(data) {
     document.getElementById('dd').innerHTML = d.diff() + ' ms';
  var res = '';
  for (var i=0; i<data.length; i++) {
    res += data[i] + '<br/>';  
  }
  document.getElementById('content').innerHTML = res;
      }   
    </script>    
  </head>
  <body>    
    <input type="button" value="Go" onclick="gogogo()"/>
 <span id="dd"></span>
 <div id="content"></div>
  </body>
</html>

Тестируем.

Пробуем AND используя Fusion Tables API.
<html>
  <head>
    <script>
   var d = {}; 
   d.diff = function() { return new Date() - this.d0};
   
   function gogogo() {     
  var API_KEY = 'Ключ_Вашего_Проекта'     
  var tableId = 'ID_Вашей_Таблицы';
  var url = 'https://www.googleapis.com/fusiontables/v1/query?sql=';
  var sql = 'SELECT+*+FROM+' + tableId + "+WHERE+id%3D933+AND+name%3D'name933'";
  var callback = 'gotit';  
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = url + sql + '&key=' + API_KEY + '&callback=' + callback;
  //console.log(script.src);
  document.getElementsByTagName("head")[0].appendChild(script);
  d.d0 = new Date();
      }
   
   function gotit(data) {     
  document.getElementById('dd').innerHTML = d.diff() + ' ms';
  var res = '';
  for (var i=0; i<data.rows.length; i++) {
       res += data.rows[i] + '<br/>';  
  }
  document.getElementById('content').innerHTML = res;  
      }   
    </script>    
  </head>
  <body>    
    <input type="button" value="Go" onclick="gogogo()"/>
 <span id="dd"></span>
 <div id="content"></div>
  </body>
</html>

Тестируем.

Попробовать OR не получится, т.к. "OR is not supported. (If you are looking for OR, consider the IN operator. It will do the trick in certain queries)." Я не понял через что нужно перекувыркнуться, чтобы выполнить запрос OR.

Заключение.

Обновлять данные (INSERT, UPDATE, DELETE и пр.) через скрипт настоятельно не рекомендую. Вот где проявляются плюсы Fusion Tables API. Но это уже совсем другая история...