前回の「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」にしておきます。
次の処理をシェルスクリプトで実現してみます。
1. アプリB(コピー先)から最大KeyCode(アプリAのレコードID)を取得する
2. 最大KeyCodeより大きいレコードIDを持ったアプリAのデータを抽出してアプリBに登録する
3. アプリB(コピー先)のキー日時(レコードごとの処理した日時)の最大を取得する
4. その最大キー日時がアプリ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コマンドラインツールとシェルスクリプトを使うと 定期的に、別会社(ドメイン)へデータを同期して活用 することができますね。お試しください。