2019年1月2日水曜日

Google Apps Script でプロパティをファイルに保存する

GoogleAppsScriptは便利だがいろいろと困ることがある、その代表格は6分制限。スクリプトの実行時間が6分を超えると途中で処理が終了してしまいます。 その対応として実行経過を保存し時間駆動型のトリガーを設定して、細切れに処理をする必要がある。これについてはGoogleで検索すれば山のように情報が出てくるので割愛する。
今回その途中経過を保存する方法について書いてみる。 一般的にはProperties ServicesetProperty(key, value)などを用いると非常に楽である。しかし、9217文字以上の文字列を扱おうと思うとエラーが発生してしまう。

var scriptProperties = PropertiesService.getScriptProperties();
scriptProperties.setProperty(
  'key_string',  // keyにもサイズ制限があるかもしれないが未検証
  'ここに文字数が9217以上の文字列を指定、XXXXXXXXXX');
}

上の例では以下のようにエラーではじかれる。
引数が大きすぎます:value(行 nn,ファイル「コード」)
そこまで大きい値を扱わない場合には問題ないが、DriveAPIで複雑な検索式でsearchFilesを行った場合のContinuationTokenがこの上限を大幅に超えることがある。9216文字以下になるように分割して複数のプロパティに保存することで対処可能ではあるが、今回はプロパティをファイルに保存する方法を考えてみたので忘れないようにメモ。使い方はそのうち別記事で記載します。

初回起動時にプロパティファイルを生成し、そのファイルIDをProperties Serviceで保存しておく。ファイルIDが変わらない限りはプロパティファイルの名前が変更されても参照が継続する。使い勝手が良いのか、悪いのかはよくわからない。
// 使い方はfunction examplePersistentProperty()を参照

// PersistentProperty()
// 概要:クラスのコンストラクタ
// 引数:filename  プロパティ保存する初期ファイル名
// 戻り値:インスタンス
// 使い方:
//   var pp = new PersistentProperty('PersistentPropertyTest');
// 備考:一度生成したファイルを参照し続ける。ファイル名を変えても参照し続ける。
PersistentProperty = function(filename){
  this.file = null;
  var id =  PropertiesService.getUserProperties().getProperty("PropertiesFileID");
  try{
    if( id!=null )
    {
      // ファイル取得を試みる
      this.file = DriveApp.getFileById(id);
      if( this.file.isTrashed() )
      {
      // ユーザープロパティから取得したファイルがゴミ箱にある場合には廃棄する
        this.file=null;;
      }
    }
  }
  catch(e)
  {
    // ファイル取得に失敗した場合は
    this.file=null;
  }
  // プロパティファイルがない場合には新規に作成する。
  if( this.file==null )
  {
    var name = filename;
    this.file =  DriveApp.createFile(name,"{}",MimeType.PLAIN_TEXT);
    var fileId = this.file.getId();
    PropertiesService.getUserProperties().setProperty("PropertiesFileID",fileId);
  }

  // ファイルからテキスト読み込み
  var text =  this.file.getBlob().getDataAsString();
};


// deleteAllProperties()
// 概要:保存されているプロパティをすべて消去
// 引数:なし
// 戻り値:なし
// 使い方:
//   var pp = new PersistentProperty('PersistentPropertyTest');
//   pp.deleteAllProperties();
// 備考:
PersistentProperty.prototype.deleteAllProperties = function(){
  this.file.setContent(JSON.stringify({}));
};

// deleteProperty(key)
// 概要:指定したプロパティを消去
// 引数:key  削除するプロパティを指定するキー
// 戻り値:なし
// 使い方:
//   var pp = new PersistentProperty('PersistentPropertyTest');
//   pp.deleteProperty('PropaertyKeyString');
// 備考:
PersistentProperty.prototype.deleteProperty = function(key){
  var text =  this.file.getBlob().getDataAsString();
  var Properties =  JSON.parse(text);

  try{
    // 指定されたプロパティ削除
    delete Properties[key];
  }
  finally
  {
    // 最終的にオブジェクトをファイルに保存
    this.file.setContent(JSON.stringify(Properties));
  }  
};

// getKeys()
// 概要:保存しているプロパティのキーの一覧を取得
// 引数:なし
// 戻り値:キーの一覧(string[])
// 使い方:
//   var pp = new PersistentProperty('PersistentPropertyTest');
//   var keys = pp.getKeys()
// 備考:
PersistentProperty.prototype.getKeys = function(){
  var text =  this.file.getBlob().getDataAsString();
  var Properties =  JSON.parse(text);

  var keys = [];
  for (key in Properties) {
    keys.push(key);
  }
  return keys;
};

// getProperties()
// 概要:保存しているプロパティリストを取得
// 引数:なし
// 戻り値:保存しているプロパティリスト(連想配列)
// 使い方:
//   var pp = new PersistentProperty('PersistentPropertyTest');
//   var properties = pp.getProperties();
//   var value = properties['PropaertyKeyString'];
// 備考:
PersistentProperty.prototype.getProperties = function(){
  var text =  this.file.getBlob().getDataAsString();
  var Properties =  JSON.parse(text);
  return Properties;
};

// getProperty(key)
// 概要:指定したプロパティを取得
// 引数:key  取得するプロパティを指定するキー
// 戻り値:プロパティの値
// 使い方:
//   var pp = new PersistentProperty('PersistentPropertyTest');
//   var value = pp.getProperty('PropaertyKeyString');
// 備考:
PersistentProperty.prototype.getProperty = function(key){
  var text =  this.file.getBlob().getDataAsString();
  var Properties =  JSON.parse(text);

  if( key in Properties ){
    return Properties[key];
  }else{
    return null;
  }
};

// setProperties(properties,deleteAllOthers)
// 概要:プロパティリストを設定
// 引数:properties  設定するプロパティリスト(連想配列)
//   :deleteAllOthers  trueを指定すると他のプロパティをクリア、書略時はfalseとして動作する
// 戻り値:なし
// 使い方:
//   var pp = new PersistentProperty('PersistentPropertyTest');
//   pp.setProperties({ "key1":"value1","key2":"value2"},true); // 他のプロパティは削除
//   pp.setProperties({ "key3":"value3","key4":"value4"}); // プロパティを追加
// 備考:deleteAllOthersがfalseの場合はプロパティリストの追加
PersistentProperty.prototype.setProperties = function(properties,deleteAllOthers){
  var text =  this.file.getBlob().getDataAsString();
  var Properties =  JSON.parse(text);

  if( deleteAllOthers ){
    Properties = properties;
  }else{
    for( var key in properties ){
      Properties[key] = properties[key];
    }
  }
  var json = JSON.stringify(Properties);
  this.file.setContent(json);
};

// setProperty(key,value)
// 概要:プロパティを設定
// 引数:key  設定するプロパティのキー
//   :value  設定するプロパティの値
// 戻り値:なし
// 使い方:
//   var pp = new PersistentProperty('PersistentPropertyTest');
//   pp.setProperty( "key1", "value1" );
// 備考:
PersistentProperty.prototype.setProperty = function(key,value){
  var text =  this.file.getBlob().getDataAsString();
  var Properties =  JSON.parse(text);

  Properties[key] = value;
  this.file.setContent(JSON.stringify(Properties));
};

//////////////////////////////////////////////////////////////////////////////
// これ以降は使い方サンプル及びDEBUG用
//////////////////////////////////////////////////////////////////////////////
function examplePersistentProperty()
{
  // ファイル名を決める
  var pp = new PersistentProperty('PersistentPropertyTest');
  
  // プロパティのセット
  pp.setProperty( "test1","test1 value" );

  // プロパティを複数追加
  pp.setProperties(
    { "test2":"test2 value","test3":"test3 value"});

  // プロパティを複数追加
  pp.setProperties(
    { "test4":"test4 value","test5":"test5 value"},false );

  // 一つずつ取得してもよい
  var test1 = pp.getProperty("test1");
  
  // 一気に取得してもよい
  var properties = pp.getProperties();
  var test2 = properties["test2"];
 
  // プロパティを一部削除
  pp.deleteProperty("test4");
  
  // プロパティを一部削除や追加し一気に更新
  delete properties["test5"];
  properties["test6"] = "New Property";
  pp.setProperties(properties,true);
  
  // 全プロパティを列挙
  var keys = pp.getKeys();
  for( var i=0; i<keys.length; i++ ){
    var key = keys[i]
    var value = pp.getProperty(key);
  }

  // 全プロパティを削除
  pp.deleteAllProperties();
}

0 件のコメント:

コメントを投稿

質問、要望、指摘など書いていただいてもよいですが、対応できるとは限りませんのでご了承ください。私に対するものも含め他の人を嫌な気分になるようなコメントは避けてください。