We’ve migrated our documentation to a new site, which means some URLs have changed.
Audience

Persistedクエリチュートリアル

イントロダクション

Piano Insight ユーザアカウントと対応するAPIキーを利用すればバックエンドのCGIスクリプトからPiano InsightのAPIを介して殆どのデータを照会できます。しかし、同じアプローチはフロントエンドのWebページから行うことはできません。WebページにユーザアカウントとAPIキーがある状態で誰でもこれを見れるようになってしまうと、そのユーザの権限で利用できる全ての操作が出来てしまいますし、悪用されることも懸念されます。

これを防ぐためにPiano InsightのAPIを登録し、その登録したIDを利用してWebページからAPIを実行することができます。これをPersistedクエリIDと呼びます。このIDは予め登録されたAPI以外の機能を実行させることはできません。下記の図はAPIキーとPersistedクエリIDでできる事の違いを表しています。APIキーはPersistedクエリ含めた、全ての機能に対してアクセス可能です。それに対してPersistedクエリIDは特定のAPIのみ実行可能となります。

image-20220118-052523.png

このページに利用されているコードはHTML/Javascript と Python 3.xでの動作を想定しています。なお、Pythonが利用できない場合でもGUIを使用して全ての例を実行可能です(最終セクションの「Piano Admin GUIからの作成と管理」を参照ください)。

PersistedクエリIDはユーザーIDに紐づくものです。異動や退職によってその作成を行なったユーザーが削除された場合、設定されたイベントが送れなくなってしまいます。Persistedクエリを使用する場合は運用上のリスクを考慮して、できる限りPiano Insightの個人ユーザーIDで作成し利用するのではなく、グループアドレスやメールエイリアスなどのシステム用ユーザーを作成いただき、そのユーザーにて設定作業を行なってください。

Persistedクエリをコードで作成する方法

以下のスクリプトを使用して、任意のAPIコマンドをPersistedクエリとして登録できます。
※persistedクエリ作成のための権限付与が必要です(過去に作成されたことのあるユーザは既にこの権限をお持ちです)。詳しくは /persisted/create をご覧ください。
※具体的な作成方法は DMP eventのヘルパー関数 - sendEventDMP eventのヘルパー関数 - getUserSegmentIds() が参考になります。

クエリを作成するにあたって重要なパラメータは次の三つです。

  • コマンドパス (/some/command)

  • リクエストオブジェクト ({ "param1":"value1", "param2", "value2", "param3":"value3" })

  • Webページからの変更を指定できる可変パラメータ ({ "param1":True, "param3", True })

以下のスクリプトは、登録した機能/操作にアクセス可能なWebページで使用する為のpersistedクエリIDを返します。APIキーを参照するには APIキー確認方法 をご覧ください。

# 認証情報
_username     = "<Piano Insight のユーザーID>"
_secret       = "<Piano Insight ユーザーIDに紐付く APIキー>"

# 登録するクエリ情報
_path         = "< API コマンドパス>" 
_request      = {<リクエストオブジェクト>}
_mutable      = {<可変パラメータ>}
  
import json, datetime, hmac, hashlib, http.client
   
def cxApi(path, obj):
    date = datetime.datetime.utcnow().isoformat() + "Z"
    signature = hmac.new(_secret.encode('utf-8'), date.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
    headers = {"X-cXense-Authentication": "username=%s date=%s hmac-sha256-hex=%s" % (_username, date, signature)}
    connection = http.client.HTTPSConnection("api.cxense.com", 443)
    connection.request("POST", path, json.dumps(obj), headers)
    response = connection.getresponse()
    status = response.status
    responseObj = json.loads(response.read().decode('utf-8'))
    connection.close()
    return status, responseObj
 
def errorHandling(status, response, message):
    if status != 200:
        raise Exception("%s (http status = %s, error details: '%s')" % (message, status, response['error']))
 
def createPersitedQuery(path, request={}, mutable={}):
    status, response = cxApi('/persisted/create', {"path":path, "request":request, "mutable":mutable})
    errorHandling(status, response, "Unable to create persisted query")
    return response['id']
 
persistedQueryId = createPersitedQuery(_path, _request, _mutable)
print("Created persisted query id '%s'" % persistedQueryId)

上記のスクリプトはこの先のセクションでも使用します。

Webページ上からPersistedクエリIDを利用する方法

上記のスクリプトで、下記例のpersistedクエリの設定を登録します。このクエリではAPI /siteを使用してアクセス権限のある全てのサイトの情報をリクエストします。

# Persistedクエリを登録
_path         = "/site"
_request      = {}
_mutable      = {}

このクエリを登録するとPersistedクエリIDが発行されます。
例: bda7639f305555555c981f86ff46ab7e6014fb97

ここでは、アクセス権のあるすべてのサイトのデータが返されるように、発行されたIDをPiano InsightのJSONP呼び出しで使用します。JSONPのテンプレートは以下のようになります。

<script src='https://api.cxense.com/コマンドパス?persisted=PersistedクエリID&callback=コールバック関数名'></script>

登録したコマンドパスとpersistedクエリIDは既に登録/発行済みです。以下のWebページのソースコードではコールバック関数listSitesを呼び出し、Piano Insightから受け取るJSONデータが表示されます。ブラウザにsrcで指定するurlを挿入して、ページを実装する前にJSONPがどのように呼び出されているか確認が出来ます。

<!DOCTYPE HTML>
<html>
	<body>
		<script>
			function listSites(data) {
				if (data.hasOwnProperty('httpStatus') && data['httpStatus'] == 200) {
					var names = '';
					var sites = data['response']['sites'];
					for (var i in sites) {
						names += sites[i]['name'] + '<br>';
					}
					document.getElementsByTagName('body')[0].innerHTML = names;
				} else {
					alert('No or bad http status');
				}
			}
		</script>
		<script src='https://api.cxense.com/site?persisted=bda7639f305555555c981f86ff46ab7e6014fb97&callback=listSites'></script>
	</body>
</html>

コールバック関数は、下記の画像のようにアクセス可能な全てのサイト名を抽出してWebページに表示します。

https://wiki.cxense.com/download/attachments/30378688/site%20list.png?version=1&modificationDate=1451328329665&api=v2

この場合は全てのサイトがレスポンスに含まれています。特定のサイトに限定したい場合は、Persistedクエリを作る際にsiteIdまたはsiteIdsパラメータをリクエストオブジェクトに追加できます。

# The persisted query to be registered
_path         = "/site"
_request      = {"siteId":"9222364973424511877"}
_mutable      = {}

cX.jsonpRequest() 関数の利用

cx.jsライブラリ内のcX.jsonpRequest() 関数はJSONPリクエストより便利です。 
固定された文字列ではなく、変数を使う事ができるようになります。

<!DOCTYPE HTML>
<html>
	<body>
		<script type="text/javascript" src="http://cdn.cxense.com/cx.js"></script>
		<script>
		    var path = '/site';
		    var requestObj = {};
            var persistedQueryId = "bda7639f305555555c981f86ff46ab7e6014fb97";
			var apiUrl = 'https://api.cxense.com' + path + '?callback={{callback}}'
                                                         + '&persisted=' + encodeURIComponent(persistedQueryId) 
                                                         + '&json=' + encodeURIComponent(cX.JSON.stringify(requestObj));

    		cX.jsonpRequest(apiUrl, function(data) {
                if (data.hasOwnProperty('httpStatus') && data['httpStatus'] == 200) {
        			var names = '';
					var sites = data['response']['sites'];
					for (var i in sites) {
						names += sites[i]['name'] + '<br>';
					}
					document.getElementsByTagName('body')[0].innerHTML = names;
                } else {
					alert('No or bad http status');
                }                    
			});
		</script>
	</body>
</html>

このWebページでは、JSONPの処理の時と同じ出力が表示されます。
異なる点は、cX.jsonpRequest()関数がリクエストから出力まで処理することです。

Webページからのリクエスト可能なオブジェクトパラメータを指定する

これまでの例ではWebページ(ユーザ)が何を返すかに対して影響を与える方法はありませんでした。persistedクエリは予め固定されている為に、リクエストを受け取るもしくは残すだけです。 次の例では、Webページ(ユーザー)がデータを取得するサイトを決定できるようにします。新しいPersistedクエリを作成する際にsiteIdをWebページからmutable (変更可能な)パラメータとして登録します。 

# The persisted query to be registered
_path         = "/site"
_request      = {}
_mutable      = {"siteId":True}

このクエリをPython スクリプトで登録するとPersistedクエリIDが発行されます。
例: 31caec61462f60555556107384ad246dfe00d127

追加のパラメータを即座に提供するためのJSONPテンプレートは次のとおりです。

<script src='https://api.cxense.com/コマンドパス?persisted=PersistedクエリID&callback=コールバック関数名&json={"パラメータ1"="値1","パラメータ2"="値2"}'></script>

 jsonデータはURLエンコードが必要です。 
したがって、Webページの実際のJSONP呼び出し(この場合、サイトIDは9222302702321341959)は
次のようになります。

<script src='https://api.cxense.com/site?persisted=31caec61462f60555556107384ad246dfe00d127&callback=listSites&json=%7B"siteId":"9222302702321341959"%7D'></script>

実際に利用されるWebページの例

これまでの例はポイントを解説するために最小限の解説のみとしていました。ここからはもう少し実際に利用されるような例を取り上げていきます。jqueryui.com のページではPiano Searchのサジェスト検索に近いクエリコンプリション(検索ワードの自動補完)の例があります。

実際のサンプルは ここからダウンロードできます。 下記にはサンプルコードの一部を記載します。ここではjQueryを使ってリクエストとレスポンスを処理します。

function( request, response ) {
	$.ajax({
		url: "https://api.cxense.com/processing/dictionary/search",
		dataType: "jsonp",
		data: {
			persisted: 'aee984854ddfeecd16e074333f1d3bb0bd67f849',
			json: JSON.stringify({
				input: request.term
			})
		},
		success: function( data ) {
			data = $.map(data['response']['matches'], function(value, i) { return value['key'] + ' :: ' + value['value'].split('|')[0]; })
			response( data );
		}
	});
}

このコードが機能するには、2つの前提条件があります。

  • クエリコンプリーション辞書を作成してアップロードする必要があります。

  • 辞書内検索はpersistedクエリを介して行う必要があります。

先ほど紹介したサンプルには辞書管理チュートリアル の通りアップロード可能な映画タイトルのクエリコンプリートの辞書ファイル(afi.json)が同梱されています(ファイル内のカスタマプリフィクスはご自身のサイトグループのものに変更してください)。persistedクエリの設定は次のようになります。 dictionaryIdは、辞書を作成した際に取得します。

# The persisted query to be registered
_path         = "/processing/dictionary/search"  
_request      = {"dictionaryId": 1234567890123456789} 
_mutable      = {"input": True} 

下記の画像では入力した文字列を補完して候補のリストが表示されます。

https://wiki.cxense.com/download/attachments/30378688/movie%20titles.png?version=2&modificationDate=1451433316967&api=v2

特殊な例:DMP オーディエンスセグメント

DMPオーディエンスセグメントの抽出は、他のpersistedクエリより若干特殊なケースになります。 作成プロセスはまったく同じですが、Webページの使用方法はgetUserSegtmentId()というcx.jsライブラリ関数でサポートされています。以下はpersistedクエリの設定になります。

# The persisted query to be registered
_siteGroupIds = [指定したいサイトグループIDをカンマ区切りで指定]
_path         = "/profile/user/segment" 
_request      = {"identities":[{"id":"0","type":"cx"}],"siteGroupIds":_siteGroupIds}
_mutable      = {"identities":True}

このクエリをPython スクリプトで登録するとPersistedクエリIDが発行されます。
例: 5d2c2ebfdbc41cc555551ae63fdd0b5516d812de 

<!DOCTYPE HTML>
<html>
	<body>
		<script type="text/javascript" src="http://cdn.cxense.com/cx.js"></script>
		<script>
    		var segments = cX.getUserSegmentIds({persistedQueryId:'5d2c2ebfdbc41cc555551ae63fdd0b5516d812de'});
			alert("The user is member of the following DMP audience segments: " + segments);
		</script>
	</body>
</html>

Piano Admin GUIからの作成と管理

Pythonでコードの作成を行わなくても、今までのセクションを実行可能です。 以下では、Persisted Query管理ページにアクセスする方法と、persistedクエリの作成/更新を作成します。

https://wiki.cxense.com/download/attachments/30378688/persisted%20GUI.png?version=1&modificationDate=1451362204364&api=v2

以下の画像は、これまでのセクションでPythonスクリプトを使用して作成したsiteIdを可変パラメータにしてサイト情報を取得するpersistedクエリの設定方法です。

https://wiki.cxense.com/download/attachments/30378688/persisted%20data%20gui.png?version=3&modificationDate=1451366738495&api=v2

Last updated: