2016年8月2日火曜日

「履歴情報つきinteger試作」をMySQLで

nuko_yokohamaさんの記事「履歴情報つきinteger試作」が面白い.PostgreSQLのJSON機能を用いてSQLだけで履歴情報を管理している.こうした機能を考えていたので試してみたくなった.JSON機能についてはMySQLでもバージョン5.7からサポートしているらしい.せっかくなのでMySQLのJSON機能の勉強もかねてMySQLでの実装を試みてみた.

関数定義

「履歴情報つきinteger試作」では色々と関数を定義しているが,ここでは最低限の「create_tt_int」「add_tt_int」「value(実際にはgetValueByUnixTimeとgetValueByNum)」の実装を試みた.

履歴つきinteger作成

履歴つきinteger作成関数.時間はUNIX_TIMESTAMPで表現する.

DROP FUNCTION IF EXISTS create_tt_int;
delimiter //
CREATE FUNCTION create_tt_int(value integer)
 RETURNS JSON DETERMINISTIC
BEGIN
  DECLARE t INT UNSIGNED;
  set t = UNIX_TIMESTAMP();
 RETURN (SELECT JSON_OBJECT('header',JSON_OBJECT('nums',1,'newest',t,'oldest',t),'values',JSON_ARRAY(JSON_OBJECT('value',value,'timestamp',t,'num',1))));
END//
delimiter ;

履歴つきinteger更新

履歴つきinteger更新関数.JSON_ARRAYのマージに手間取った.JSON_MERGEでよかった.

DROP FUNCTION IF EXISTS add_tt_int; delimiter // CREATE FUNCTION add_tt_int(src json, value integer) RETURNS JSON DETERMINISTIC BEGIN DECLARE t INT UNSIGNED; SET t = UNIX_TIMESTAMP(); RETURN (SELECT JSON_OBJECT("header",JSON_OBJECT("nums",JSON_EXTRACT(src,"$.header.nums")+1,"oldest",JSON_EXTRACT(src,"$.header.oldest"),"newest",t),"values",JSON_MERGE(JSON_ARRAY(JSON_OBJECT("value",value,"num",JSON_EXTRACT(src,"$.header.nums")+1,"timestamp",t)),JSON_EXTRACT(src,"$.values")))); END// delimiter ;


UnixTimeを指定して値を取得

UnixTimeを指定して値を取得.

DROP FUNCTION IF EXISTS getValueByUnixTime;
delimiter //
CREATE FUNCTION getValueByUnixTime(src json,t INT UNSIGNED)
 RETURNS JSON DETERMINISTIC
BEGIN
 DECLARE i int;
 DECLARE x int;
 DECLARE t2 JSON;
 DECLARE value JSON;
 IF src->'$.header.oldest' > t THEN RETURN JSON_OBJECT();
 ELSEIF src->'$.header.newest' < t THEN RETURN JSON_EXTRACT(src,CONCAT("$.values[0].value"));
 END IF;
 SET x = src->'$.header.nums';
 SET i = 0;
 set value = JSON_OBJECT();
 WHILE i < x DO
  SET t2 = JSON_EXTRACT(src,CONCAT("$.values[",i,"].timestamp"));
  IF t2<=t THEN RETURN JSON_EXTRACT(src,CONCAT("$.values[",i,"].value"));
  END IF;
  SET i = i + 1;
 END WHILE;
 RETURN value;
END//
delimiter ;


Numを指定して値を取得

履歴番号Numを指定して値を取得.

DROP FUNCTION IF EXISTS getValueByNum;
delimiter //
CREATE FUNCTION getValueByNum(src json,num INT UNSIGNED)
 RETURNS JSON DETERMINISTIC
BEGIN
 DECLARE i int;
 DECLARE x int;
 DECLARE num2 INT UNSIGNED;
 DECLARE value JSON;
 SET x = src->'$.header.nums';
 IF (num < 0 ) THEN RETURN JSON_OBJECT();
 ELSEIF (num > x) THEN RETURN JSON_OBJECT();
 END IF; 
 SET i = 0;
 set value = JSON_OBJECT();
 WHILE i < x DO
  SET num2 = JSON_EXTRACT(src,CONCAT("$.values[",i,"].num"));
  IF (num=num2) THEN RETURN JSON_EXTRACT(src,CONCAT("$.values[",i,"].value"));
  END IF;
  SET i = i + 1;
 END WHILE;
 RETURN value;
END//
delimiter ;

使い方

テーブルを作成,履歴つきintegerを作成.2回ほど更新.

DROP TABLE test2;
CREATE TABLE test2 (id INT UNSIGNED, data JSON);
INSERT INTO test2 VALUES (1, create_tt_int(100));
INSERT INTO test2 VALUES (2, create_tt_int(300));
SELECT SLEEP(60);
UPDATE test2 SET data = add_tt_int(data, 200) WHERE id = 1;
UPDATE test2 SET data = add_tt_int(data, 500) WHERE id = 2;
SELECT SLEEP(60);
UPDATE test2 SET data = add_tt_int(data, 300) WHERE id = 1;
UPDATE test2 SET data = add_tt_int(data, 600) WHERE id = 2;
SELECT * FROM test2;

中身は以下のとおり.履歴情報がJSON形式で記録されている.

mysql> select * from test2\G;
*************************** 1. row ***************************
  id: 1
data: {"header": {"nums": 3, "newest": 1470061025, "oldest": 1470060905}, "values": [{"num": 3, "value": 300, "timestamp": 1470061025}, {"num": 2, "value": 200, "timestamp": 1470060965}, {"num": 1, "value": 100, "timestamp": 1470060905}]}
*************************** 2. row ***************************
  id: 2
data: {"header": {"nums": 3, "newest": 1470061025, "oldest": 1470060905}, "values": [{"num": 3, "value": 600, "timestamp": 1470061025}, {"num": 2, "value": 500, "timestamp": 1470060965}, {"num": 1, "value": 300, "timestamp": 1470060905}]}
2 rows in set (0.00 sec)

ERROR: 
No query specified

ここから値を取り出すにはgetValueByUnixTimeを使う.

mysql> SELECT getValueByUnixTime(data,UNIX_TIMESTAMP('2016-08-01 23:15:00')) AS value FROM test2;
+-------+
| value |
+-------+
| {}    |
| {}    |
+-------+
2 rows in set (0.00 sec)

mysql> SELECT getValueByUnixTime(data,UNIX_TIMESTAMP('2016-08-01 23:15:05')) AS value FROM test2;
+-------+
| value |
+-------+
| 100   |
| 300   |
+-------+
2 rows in set (0.00 sec)
mysql> SELECT getValueByUnixTime(data,UNIX_TIMESTAMP('2016-08-01 23:50:00')) AS value FROM test2;
+-------+
| value |
+-------+
| 300   |
| 600   |
+-------+
2 rows in set (0.00 sec)

ちゃんと履歴から値をとってこれている.

2016年8月1日月曜日

Mirador Viewerを試す

Mirador Viewer は International Image Interoperability Framework (IIIF) プロトコルへの完全対応を謳ったオープンソースのイメージビューワ.アノテーション機能も実装されている.

  • http://projectmirador.org/
  • https://github.com/IIIF/mirador  


Mirador Viewer にはサンプルファイルへのリンクが含まれているので,インストール後すぐにIIIF Viewer としての機能を試すことができる.

インストール


動作に必要なパッケージをインストールする.


sudo yum install nodejs
sudo yum install npm
sudo npm install -g grunt-cli
sudo npm install bower -g

mirador リポジトリを clone する.

git clone https://github.com/IIIF/mirador.git

依存ライブラリをインストールする.

cd mirador/
npm install
bower install

ビルドする.

grunt

エラーがでなければビルド完了.

簡易実行


手軽に試したいのであれば grunt server を実行する.

$ grunt server
Running "connect:server" (connect) task
Waiting forever...
Started connect web server on http://localhost:8000

web server が起動するので http://localhost:8000 にアクセスする.



Mirador Viewer は javascript で書かれているので,web server から見える適当な場所に置いても動作する.

以上.

CATCH-Aを試す

  • https://github.com/annotationsatharvard/catcha 

インストール


Java 1.7系をインストールし,環境変数 「JAVA_HOME」を設定する.※1.7系でないとgrails関係でこけるので注意


sudo yum install java-1.7.0-openjdk-devel
pathtojava=$(readlink -e $(which javac));export JAVA_HOME=${pathtojava%/*/*}


Java のバージョンおよび環境変数を確認する.

$ java -version
java version "1.7.0_101"
OpenJDK Runtime Environment (rhel-2.6.6.1.el7_2-x86_64 u101-b00)
OpenJDK 64-Bit Server VM (build 24.95-b01, mixed mode)
$ echo $JAVA_HOME
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.101-2.6.6.1.el7_2.x86_64


GVM(sdkman)をインストールする.最新のもので問題ない模様.


  •  http://sdkman.io/


curl -s "https://get.sdkman.io" | bash
source /home/vagrant/.sdkman/bin/sdkman-init.sh 


sdkman のバージョンを確認する.

$ sdk version
SDKMAN 4.0.37


MySQL をインストールする.

sudo yum install mariadb-server

MySQL のバージョンを確認する.
$ mysql --version
mysql  Ver 15.1 Distrib 5.5.47-MariaDB, for Linux (x86_64) using readline 5.1


ディレクトリを作成し,catcha リポジトリを clone する.

mkdir Catcha
cd Catcha
git clone https://github.com/annotationsatharvard/catcha.git


必要なライブラリも clone する.

mkdir annotationframework
cd annotationframework
git clone https://github.com/annotationframework/AfPersistence.git
git clone https://github.com/annotationframework/AfSecurity.git
git clone https://github.com/annotationframework/AfShared.git


CATCH-A 用のデータベースおよびデータベースユーザを作成する.

sudo mysql -u root -e 'create database catch default charset utf8;'
sudo mysql -u root -e 'grant all on catch.* to "catch"@"localhost" identified by "catch";'


設定ファイルをコピーする.

cd ../catcha
cp Catch-config.properties catch-config.properties 


catch-config.properties を編集する.データベースの設定は以下のあたり.

dataSource.url=jdbc:mysql://localhost:3306/catch?useUnicode=yes&characterEncoding=UTF-8&autoReconnect=true
dataSource.username=catch
dataSource.password=catch


CATCH-A が使っている grails のバージョン確認する.

$ cat application.properties | grep grails
app.grails.version=2.2.5


確認したバージョンの grails をインストールする.

gvm install grails 2.2.5


catcha/grails-app/conf/BuildConfig.groovy を編集する.

diff --git a/grails-app/conf/BuildConfig.groovy b/grails-app/conf/BuildConfig.gr
index 0c32ab1..83193e1 100644
--- a/grails-app/conf/BuildConfig.groovy
+++ b/grails-app/conf/BuildConfig.groovy
@@ -74,6 +74,7 @@ grails.project.dependency.resolution = {
         //runtime "org.semweb4j:rdf2go.impl.base:4.6.2"
                //compile "org.openrdf:openrdf-sesame-onejar-osgi:2.1.2"
                //compile "org.openrdf.sesame:sesame-query:2.7.2"
+       compile "com.nimbusds:nimbus-jose-jwt:2.26.1"
     }
 
     plugins {


nimbus-jose-jwt は 2.x 系じゃないと動かない. 準備が整ったらプロジェクトをビルド&実行する.

grails run-app


ビルドが無事終了し,以下メッセージが表示されるまで待つ.

| Server running. Browse to http://localhost:8080/catch


表示されたら http://localhost:8080/catch にアクセスする.

ユーザ名「admin」,パスワード「password」でログインできる.