
kintone: Search Records
This item searches records in a Kintone App using a query.
Basic Configs
- Step Name
- Note
Configs for this Auto Step
- conf_auth
- C1: Authorization Setting in which API Token is set *
- conf_basic
- C2: Basic Auth Setting (required if enabled on Kintone)
- conf_domain
- C3: Domain (such as xxxxx.kintone.com or xxxxx.cybozu.com) *
- conf_guestSpaceId
- C4: Guest Space ID (required if the App is in a Guest Space)
- conf_appId
- C5: App ID *
- conf_query
- C6: Search Query#{EL}
- conf_recordIds
- C7: Data item to save Record IDs
- conf_idField
- C8F: Field Code 1
- conf_ids
- C8V: Data item to save Values of Field Code 1
- conf_labelField
- C9F: Field Code 2
- conf_labels
- C9V: Data item to save Values of Field Code 2
Notes
- To get the API Token, open the App Settings and click “API Token” in the App Settings tab on Kintone.
Click “Generate”, select the Permissions (“View records” permission is required), and click “Save”.
Do not forget to click “Update App” to apply the update. - Guest Space ID (only when the Kintone App is in a guest space) and App ID can be confirmed in the API Token settings on Kintone.
- Supported field types are: Record Number, Record ID, Revision, Status, Created datetime, Updated datetime, Text, Number, Calculated, Link, Date, Time, Date and time, Lookup, Radio button, Drop-down.
- See the Kintone Reference for the operators and functions that can be used in Search Query. Query options (order by, limit, offset) are not supported.
Capture

See Also
Script (click to open)
- An XML file that contains the code below is available to download
- kintone-record-search.xml (C) Questetra, Inc. (MIT License)
- If you are using Professional, you can modify the contents of this file and use it as your own add-on auto step
main();
function main(){
//// == 工程コンフィグの参照 / Config Retrieving ==
const auth = configs.getObject("conf_auth");
const basic = configs.getObject("conf_basic");
const domain = configs.get("conf_domain");
const guestSpaceId = configs.get("conf_guestSpaceId");
const appId = configs.get("conf_appId");
const query = configs.get("conf_query");
const fieldCodeList = [
"$id",
configs.get("conf_idField"),
configs.get("conf_labelField")
];
const dataDefList = [
configs.getObject("conf_recordIds"),
configs.getObject("conf_ids"),
configs.getObject("conf_labels")
];
//// == ワークフローデータの参照 / Data Retrieving ==
const apiToken = auth.getToken();
//// == 演算 / Calculating ==
const singleLineFlag = checkFieldCodesAndDataDefs( fieldCodeList, dataDefList );
const apiUri = determineApiUri( domain, guestSpaceId );
checkAppId( appId );
const fields = new Set( fieldCodeList );
fields.delete(null);
fields.delete("");
// フィールドの存在とフィールド型をチェック(requestNum は現時点での HTTP リクエスト回数)
const requestNum = checkFields( apiUri, apiToken, basic, appId, fields );
const initialParams = prepareInitialParams( appId, fields, query );
const records = []; // レコードオブジェクトを格納する配列
getRecords( apiUri, apiToken, basic, initialParams, records, requestNum, singleLineFlag );
//// == ワークフローデータへの代入 / Data Updating ==
saveData( records, fieldCodeList, dataDefList );
}
/**
* フィールドコードと保存先データ項目をチェックし、以下の場合はエラーとする
* 1. 保存先データ項目が一つも設定されていない
* 2. フィールドコードが空なのに、保存先データ項目が設定されている
* 3. フィールドコードが設定されているのに、保存先データ項目が設定されていない
* @param {Array<String>} fieldCodeList フィールドコードの配列(1番目は $id)
* @param {Array<ProcessDataDefinitionView>} dataDefList 保存先データ項目の配列
* @return {boolean} singleLineFlag 保存先データ項目のいずれかが単一行であれば true
*/
function checkFieldCodesAndDataDefs( fieldCodeList, dataDefList ) {
// 保存先データ項目が一つも設定されていなければエラー
if ( dataDefList.filter(dataDef => dataDef !== null).length === 0 ) {
throw "No data item to save the result is set.";
}
// フィールドコードと、値を保存するデータ項目をチェック
for (let i = 1; i < fieldCodeList.length; i++) {
if ( fieldCodeList[i] === "" || fieldCodeList[i] === null ) { // フィールドコードが空
if ( dataDefList[i] !== null ) { // 保存先データ項目が設定されている
throw `Field Code ${i} is empty but the data item to save the values is set.`;
}
} else { // フィールドコードが空でない
if ( dataDefList[i] === null ) { // 保存先データ項目が設定されていない
throw `Data item to save the values of Field Code ${i} is not set.`;
}
}
}
return dataDefList.some(dataDef => dataDef !== null && dataDef.matchDataType("STRING_TEXTFIELD"));
}
/**
* kintone REST API のレコード取得の URI を決定する
* ドメインが空、または kintone のドメインとして不正な文字列であればエラーとする
* @param {String} domain ドメイン
* @param {String} guestSpaceId ゲストスペース ID
* @return {String} apiUri API の URI
*/
function determineApiUri( domain, guestSpaceId ) {
checkDomain( domain );
let apiUri;
if ( guestSpaceId === "" || guestSpaceId === null ) {
apiUri = `https://${domain}/k/v1/`;
} else {
if ( !isValidId(guestSpaceId) ) {
throw "Invalid Guest Space ID.";
}
apiUri = `https://${domain}/k/guest/${guestSpaceId}/v1/`;
}
return apiUri;
}
/**
* ドメインが空または不正な文字列であればエラーとする
* @param {String} domain ドメイン
*/
function checkDomain( domain ) {
if ( domain === "" || domain === null ) { // required="true" なので空になることはないが、チェック
throw "Domain is empty.";
}
const reg = new RegExp( '^[0-9a-zA-Z-]{3,32}.(?:kintone.com|cybozu.com)$' );
if ( !reg.test(domain) ) {
throw "Invalid Kintone domain.";
}
}
/**
* アプリ ID が空または不正な文字列であればエラーとする
* @param {String} appId アプリ ID
*/
function checkAppId( appId ) {
if ( appId === "" || appId === null ) { // required="true" なので空になることはないが、チェック
throw "App ID is empty.";
}
if ( !isValidId(appId) ) {
throw "Invalid App ID.";
}
}
/**
* ID が有効か(自然数か)を判定する
* @param {String} idString ID の文字列
* @return {Boolean} 有効な ID かどうか
*/
function isValidId( idString ) {
const idReg = new RegExp( '^[1-9][0-9]*$' );
return idReg.test( idString );
}
/**
* kintone REST API にフィールド一覧取得の GET リクエストを送信し、
* 指定したフィールドの存在とフィールド型をチェックする
* ただし、$id と $revision 以外のフィールドが指定されていない場合はリクエストを送信しない
* サポートするフィールド型:
* レコードID, リビジョン, レコード番号, 文字列(1行), リンク, 数値, 日付, 時刻, 日時, 計算,
* ドロップダウン, ラジオボタン, ステータス, 作成日時, 更新日時
* @param {String} apiUri API の URI
* @param {String} apiToken API トークン
* @param {AuthSettingWrapper} basic Basic 認証設定
* @param {String} appId アプリ ID
* @param {Set<String>} fields フィールドコードの集合
* @return {Number} requestNum HTTP リクエスト回数(リクエストを送信したら 1, 送信しなければ 0)
*/
function checkFields( apiUri, apiToken, basic, appId, fields ) {
const fieldsToCheck = new Set(fields);
fieldsToCheck.delete("$id");
fieldsToCheck.delete("$revision");
if ( fieldsToCheck.size === 0 ) { // チェックすべきフィールドがない
return 0;
}
const getFieldsUri = `${apiUri}app/form/fields.json`;
let request = httpClient.begin()
.queryParam("app", appId)
.header( "X-Cybozu-API-Token", apiToken );
if (basic !== null) {
request = request.authSetting(basic);
}
const response = request.get( getFieldsUri );
//when error thrown
const responseStr = response.getResponseAsString();
const status = response.getStatusCode();
if (status >= 300) {
engine.log(`---GET request--- ${status}\n${responseStr}\n`);
throw `Failed to get form fields. status: ${status}`;
}
const json = JSON.parse(responseStr);
const supportedFieldTypes = new Set([
"RECORD_NUMBER", "SINGLE_LINE_TEXT", "LINK", "NUMBER", "DATE", "TIME", "DATETIME", "CALC",
"DROP_DOWN", "RADIO_BUTTON", "STATUS", "CREATED_TIME", "UPDATED_TIME"
]);
fieldsToCheck.forEach(fieldCode => {
if ( json.properties[fieldCode] === undefined ) {
throw `Field Code: ${fieldCode} does not exist in the app.`;
}
if ( !supportedFieldTypes.has( json.properties[fieldCode].type ) ) { // フィールド型がサポート外であればエラー
throw `Unable to save the values of ${fieldCode}. Field Type ${json.properties[fieldCode].type} is not supported.`;
}
});
return 1;
}
/**
* 初回の GET リクエストのパラメータに使用する情報を準備する
* @param {String} appId アプリ ID
* @param {Set<String>} fields フィールドコードの集合
* @param {String} query 検索クエリ
* @return {Object} initialParams リクエストのパラメータに使用する情報を格納した JSON オブジェクト
* プロパティ: {String} app アプリ ID
* {Set<String>} fields フィールドコードの集合
* {String} query 検索クエリ
* {Number} lastRecordId 検索済みの最後のレコード番号
*/
function prepareInitialParams( appId, fields, query ) {
const initialParams = {
app: appId,
fields: fields,
query: query,
lastRecordId: 0
};
return initialParams;
}
/**
* kintone REST API にレコード取得の GET リクエストを送信する
* 未取得のレコードがなくなるまで再帰的に実行される
* @param {String} apiUri API の URI
* @param {String} apiToken API トークン
* @param {AuthSettingWrapper} basic Basic 認証設定
* @param {Object} params GET リクエストのパラメータに使用する情報が格納されたオブジェクト
* プロパティ: {String} app アプリ ID
* {Set<String>} fields フィールドコードの集合
* {String} query 検索クエリ
* {Number} lastRecordId 検索済みの最後のレコード番号
* @param {Array<Object>} records レコードオブジェクトを格納する配列
* @param {Number} requestNum HTTP リクエスト回数
* @param {boolean} singleLineFlag 保存先データ項目のいずれかが単一行であれば true
*/
function getRecords( apiUri, apiToken, basic, { app, fields, query, lastRecordId }, records, requestNum, singleLineFlag ) {
// リクエスト回数の上限チェック
if ( requestNum + 1 > httpClient.getRequestingLimit() ) {
throw "HTTP requests exceed the limit.";
}
const LIMIT = 500; // 1回の GET リクエストで取得できるレコードの上限件数
const getRecordsUri = `${apiUri}records.json`;
let request = httpClient.begin()
.queryParam("app", app)
.header( "X-Cybozu-API-Token", apiToken );
if (basic !== null) {
request = request.authSetting(basic);
}
// query パラメータの設定
if ( query === "" || query === null ) {
request = request.queryParam("query", `$id > ${lastRecordId} order by $id asc limit ${LIMIT}`);
} else {
request = request.queryParam("query", `( ${query} ) and $id > ${lastRecordId} order by $id asc limit ${LIMIT}`);
}
// fields パラメータの設定
Array.from(fields.values()).forEach((fieldCode, i) => {
request = request.queryParam(`fields[${i}]`, fieldCode);
});
const response = request.get( getRecordsUri );
//when error thrown
const responseStr = response.getResponseAsString();
const status = response.getStatusCode();
if (status >= 300) {
engine.log(`---GET request--- ${status}\n${responseStr}\n`);
throw `Failed to get records. status: ${status}`;
}
const json = JSON.parse(responseStr);
Array.prototype.push.apply(records, json.records); // レコードオブジェクトを配列に追加
requestNum++; // リクエスト回数を加算
// レコード件数のチェック
if ( records.length === 0 ) { // 該当するレコードが一件もなければエラー
throw "No records found.";
}
if ( singleLineFlag && records.length > 1 ) { // 保存先データ項目が単一行なのにレコードが複数件あればエラー
throw "Multiple records were found while the data item to save the result is Single-Line.";
}
// 再帰呼び出し
if ( json.records.length === LIMIT ) {
// 取得レコードの件数が LIMIT と同じ場合は、未取得のレコードが残っている場合があるので、
// lastRecordId を更新し、getRecords を再帰呼び出しする
lastRecordId = json.records[json.records.length - 1].$id.value;
getRecords( apiUri, apiToken, basic, { app, fields, query, lastRecordId }, records, requestNum );
}
}
/**
* レコードからデータを読み出し、データ項目に出力する
* @param {Array<Object>} records レコードオブジェクトが格納された配列
* @param {Array<String>} fieldCodeList フィールドコードの配列
* @param {Array<ProcessDataDefinitionView>} dataDefList 保存先データ項目の配列
*/
function saveData( records, fieldCodeList, dataDefList ) {
dataDefList.forEach((dataDef, i) => {
if ( dataDef === null ) {
return;
}
const dataList = records.map( record => record[fieldCodeList[i]].value );
engine.setData( dataDef, dataList.join("\n") );
});
}
Pingback: kintone Batch Acquisition of Two Columns of Data – Questetra Support
Pingback: Using kintone from Workflow – Questetra Support
Pingback: Starting Processes According to Records Shown in kintone Calendar – Questetra Support