前回の「kintoneコマンドラインツールでデータ登録してみた」では、kintoneの異なるアプリのデータを定期的に別のアプリにコピーする方法を紹介しました。今回はコピー(追加)ではなく、UPSERT(キーとなる値があったら更新、なかったら追加)する方法を紹介したいと思います。
イメージ
kintoneにはUPSERTするAPIがないのですが、2016年5月にアップデートされた kintone API(レコード番号以外のキー項目でレコードの更新)を利用して次のようにINSERTとUPDATEを使い分けることにしました。
INSERT:
アプリB(コピー先)の最大KeyCode(アプリAのレコードID)より大きいレコードIDを持ったアプリAのデータを抽出してアプリBに登録する。
※アプリAからアプリBにコピーする想定で、他のツールや人の操作によるレコード追加は想定していません。
UPDATE:
アプリB(コピー先)のキー日時(レコードごとの処理した日時)の最大が、アプリAの更新日時以下のレコードIDを持ったアプリAのデータを抽出してアプリBに更新する。
※INSERTの後にUPDATEするため、INSERTするデータがUPDATEするデータにも含まれますが、kintone APIでは変更のないデータは更新しない仕様です。
kintoneコマンドラインツールを用意する
「kintoneコマンドラインツール」を使います。このツールを使った過去の記事を紹介します。
2つのアプリを用意する
異なるドメインでkintoneアプリをそれぞれ 1つずつ用意します。
※同じドメイン内の異なるアプリ間でも動作します。
kintoneの環境を持っていない!という方、cybozu.com developer networkで無償の開発者ライセンスを取得しておきましょう。
アプリの構成
アプリA
| フィールドコード | フィールドタイプ |
|:-----------|:------------|
| TitleA | 文字列(1行)|
| NumberA | 数値 |
| UpdateDatetime | 更新日時(組み込み)|
アプリB
| フィールドコード | フィールドタイプ | 備考 |
|:-----------------|:------------|:------------|
| KeyCode | 数値 | 重複なし(コピー元のレコードIDを保存) |
| KeyUpdateDatetime| 日時 | 処理した日時を保存 |
| TitleB | 文字列(1行)| |
| NumberB | 数値 | |
レコード登録画面
(フィールド名とフィールドコードを同じにしています)
アプリA
アプリB
kintoneコマンドラインツールに必要な情報
アプリA
| パラメータ | 値 |ここで実験する値 |
|:----------|:----------------|:------------|
| -a | アプリID |85 ※IDはURLに表示されています。 |
| -d | サブドメイン名 |sample1.cybozu.com ※サブドメイン名は各自異なります。
| -t | APIトークン |De5Xb3TM1TDccdqEM20eP1dpesjuBLckOx6AyM4n ※アプリの管理画面から発行できます。|
アプリB
| パラメータ | 値 |ここで実験する値 |
|:----------|:----------------|:------------|
| -a | アプリID |822 ※IDはURLに表示されています。 |
| -d | サブドメイン名 |sample2.cybozu.com ※サブドメイン名は各自異なります。
| -t | APIトークン |BoNFRmX0oDoKQMSrTiZC7hbEmb9QaZa1vYVfnyD4 ※アプリの管理画面から発行できます。|
それぞれの値は環境によって異なります。
いよいよ本題!シェルスクリプトを実行してみよう
Linux環境の場合、次のシェルスクリプトをUTF-8で保存しましょう。
ここでのファイル名は「upsert.sh」にしておきます。
次の処理をシェルスクリプトで実現してみます。
- アプリB(コピー先)から最大KeyCode(アプリAのレコードID)を取得する
- 最大KeyCodeより大きいレコードIDを持ったアプリAのデータを抽出してアプリBに登録する
- アプリB(コピー先)のキー日時(レコードごとの処理した日時)の最大を取得する
- その最大キー日時がアプリAの更新日時以下のレコードIDを持ったアプリAのデータを抽出してアプリBに更新する
説明
--- self setting ---
で囲まれた部分のみ変更しましょう。
-
### APP_A
内の変数にアプリAの情報を設定 -
### APP_B
内の変数にアプリBの情報を設定 -
### Mapping custom
内の変数にコピー元のフィールド:コピー先のフィールドを設定- TitleAからTitleBにコピーする際には、
TitleA:TitleB
と書きます -
$id
など「$
」を使う際にはエスケープしましょう - コピーするフィールドに応じて増やしましょう
- TitleAからTitleBにコピーする際には、
プログラム
#!/bin/bash
# --------------- self setting ------------------
### character encoding
OUTPUT_TYPE="sjis"
#OUTPUT_TYPE="utf-8"
### APP_A
APP_ID_A="85"
SUB_DOMAIN_A="sample1"
API_TOKEN_A="De5Xb3TM1TDccdqEM20eP1dpesjuBLckOx6AyM4n"
KEY_DATETIME_A="UpdateDatetime"
### APP_B
APP_ID_B="822"
SUB_DOMAIN_B="sample2"
API_TOKEN_B="BoNFRmX0oDoKQMSrTiZC7hbEmb9QaZa1vYVfnyD4"
UNIQUE_B="KeyCode"
KEY_DATETIME_B="KeyUpdateDatetime"
QUERY_B_INSERT="$UNIQUE_B != \"\" order by $UNIQUE_B desc limit 1"
QUERY_B_UPDATE="$UNIQUE_B != \"\" order by $KEY_DATETIME_B desc limit 1"
# Mapping
declare -a ARR_MAPPING=()
# Mapping define
ARR_MAPPING+=("\$id:KeyCode")
ARR_MAPPING+=("UpdateDatetime:KeyUpdateDatetime")
### Mapping custom
ARR_MAPPING+=("TitleA:TitleB") #sample:\$id,Title,Number,...
ARR_MAPPING+=("NumberA:NumberB")
# --------------- self setting ------------------
# Set cli-kintone files
CLI_FILES="cli-kintone"
BASE_DIR=$(cd $(dirname $0); pwd)
if [ ! -f "$BASE_DIR/$CLI_FILES" ]; then
echo "Not found $CLI_FILES files."
exit 1
fi
# Create files dirctory
OUTPUT_DIR="files"
BASE_DIR=$(cd $(dirname $0); pwd)
if [ ! -e "$BASE_DIR/$OUTPUT_DIR" ]; then
mkdir -p "$BASE_DIR/$OUTPUT_DIR"
fi
# Set header fields
declare -a ARR_FIELDS_A=()
declare -a ARR_FIELDS_B=()
for i in "${ARR_MAPPING[@]}"
do
ARR_FIELDS_A+=(`echo $i | cut -d":" -f1 | cut -d":" -f1`)
strB=\"`echo $i | cut -d":" -f2 | cut -d":" -f1`\"
ARR_FIELDS_B+=($strB)
done
OUTPUT_FIELDS="$(IFS=,; echo "${ARR_FIELDS_A[*]}")"
INSERT_FIELDS="$(IFS=,; echo "${ARR_FIELDS_B[*]}")"
UPDATE_FIELDS="$(IFS=,; echo "${ARR_FIELDS_B[*]}")"
# Set update field flg
UPDATE_FIELDS=`echo $UPDATE_FIELDS | sed -e "s/\"$UNIQUE_B\"/\"*$UNIQUE_B\"/"`
# Set datetime file
DATETIME=`date +%Y%m%d%H%M%S.%2N`
OUTPUT_FILE="output$DATETIME.csv"
OUTPUT_PATH=$BASE_DIR/$OUTPUT_FILE
# ---Get Update datetime Script----------------------
# Get max data
MAX_LIST=(`./$CLI_FILES -a $APP_ID_B -d $SUB_DOMAIN_B -t $API_TOKEN_B -e $OUTPUT_TYPE -c "$KEY_DATETIME_B" -q "$QUERY_B_UPDATE"`)
MAX_DATA=$(echo ${MAX_LIST[1]} | sed s/\"//g)
if [ -n "$MAX_DATA" ] ; then
QUERY_A_DATE="$KEY_DATETIME_A > ${MAX_LIST[1]}"
fi
echo $QUERY_A_DATE
# ---Insert Script------------------------------------
# Get max data
MAX_LIST=(`./$CLI_FILES -a $APP_ID_B -d $SUB_DOMAIN_B -t $API_TOKEN_B -e $OUTPUT_TYPE -c "$UNIQUE_B" -q "$QUERY_B_INSERT"`)
MAX_DATA=${MAX_LIST[1]}
if [ -z "$MAX_DATA" ] ; then
MAX_DATA="0"
echo "Changed ID."
fi
MAX_DATA=`echo $MAX_DATA | sed s/\"//g`
QUERY_A="\$id > $MAX_DATA"
echo $QUERY_A
# Excute output file
./$CLI_FILES -a $APP_ID_A -d $SUB_DOMAIN_A -t $API_TOKEN_A -e $OUTPUT_TYPE -c "$OUTPUT_FIELDS" -q "$QUERY_A" >> $OUTPUT_FILE
# Change csv header
INSERT_FILE="Insert$DATETIME.csv"
find . -name "$OUTPUT_FILE" | sed -i -e "1,1d" $OUTPUT_FILE
find . -name "$OUTPUT_FILE" | sed -i -e "1i $INSERT_FIELDS" $OUTPUT_FILE
find . -name "$OUTPUT_FILE" | mv "$OUTPUT_FILE" "$INSERT_FILE"
echo "Insert file: $INSERT_FILE"
# Excute import file
./$CLI_FILES -a $APP_ID_B -d $SUB_DOMAIN_B -t $API_TOKEN_B -e $OUTPUT_TYPE -f "$INSERT_FILE"
mv "$INSERT_FILE" "$OUTPUT_DIR"
echo "Finished insert data."
# ---Update Script------------------------------------
# Excute output file
./$CLI_FILES -a $APP_ID_A -d $SUB_DOMAIN_A -t $API_TOKEN_A -e $OUTPUT_TYPE -c "$OUTPUT_FIELDS" -q "$QUERY_A_DATE" >> $OUTPUT_FILE
# Change csv header
UPDATE_FILE="Update$DATETIME.csv"
find . -name "$OUTPUT_FILE" | sed -i -e "1,1d" $OUTPUT_FILE
find . -name "$OUTPUT_FILE" | sed -i -e "1i $UPDATE_FIELDS" $OUTPUT_FILE
find . -name "$OUTPUT_FILE" | mv "$OUTPUT_FILE" "$UPDATE_FILE"
echo "Update file: $UPDATE_FILE"
# Excute import file
./$CLI_FILES -a $APP_ID_B -d $SUB_DOMAIN_B -t $API_TOKEN_B -e $OUTPUT_TYPE -f "$UPDATE_FILE"
mv "$UPDATE_FILE" "$OUTPUT_DIR"
echo "Finished update data."
シェルスクリプトを実行してみる
$ ./upsert.sh
できた!!
処理したファイルは、filesフォルダにそれぞれ Insert~.csv、Update~.csv というファイルで、保存しています。
さいごに
いかがでしたでしょうか。kintoneコマンドラインツールとシェルスクリプトを使うと 定期的に、別会社(ドメイン)へデータを同期して活用 することができますね。お試しください。