MovableType Data APIでコンテンツデータ検索を試す

らら
らら

はじめに

Data APIを使うと、MovableType管理画面本体と切り離しが難しくなるので・・最悪公開サーバから編集サーバをみるようにとか・・

いままで使用していなかったが、とりあえず試すことに・・・

コンテンツデータを使って、1000件以上のデータを検索することの想定です。

1000件以下であれば、javascript用のデータをMovableTypeから作ったほうが・・・

javascript用のデータでも、月別アーカイブだったり、カテゴリアーカイブ化でjavascript用のデータをつくって

ごまかしてきましたが・・・

準備

MovableTypeをインストールしたディレクトリ内で、mt-data-api.cgiを探しパーミッションが設定されているか確認する。

mt-data-api.cgiのパーミッションを755など、サーバーに合わせて設定する。

筆者は、なるべく、最小でパーミッションを設定しているので、下記に実行権限与えていません。


mt-search.cgi
mt-comments.cgi
mt-cdsearch.cgi
mt-data-api.cgi
MovableType7は下記も・・
mt-atom.cgi
mt-feed.cgi
mt-ftsearch.cgi
mt-tb.cgi
mt-xmlrpc.cgi

MovableType管理画面設定のWebサービス設定から下記にチェックをする・・・

MovableType Data API

あとセキュリティ対策で・・

今回は、外部と連携しないので自サーバーからのみ許可する感じ・・

ただ、デバック中は、offのがよいです。

この場合は、公開、編集サーバーが同一の場合です。別々の場合は、2つドメインを許可するとか・・



SetEnvIf Referer "^https://www\.xxx\.xxx\.jp/" ref_ok
order deny,allow
deny from all
allow from env=ref_ok

Data APIの仕様・・を

基本、RESTful API(REST API)ぽいので・・・なんとかなりそう・・

MTのJavaScript SDKもあるようですが・・今回はじか書きで・・

下記のSearch

https://movabletype.github.io/mt-docs-data-api-reference/v6.html#tag/Search

下記で動作確認・・


/mt-data-api.cgi/v6/search?IncludeBlogs=ブログID&cdSearch=1&search=検索したいワード

うう。。指定したサイトのコンテンツタイプ全部でるじゃんか・・・コンテンツタイプの指定が・・見当たらない・・

ContentData.pmのソース眺めて、SearchContentTypes、SearchContentTypeIDってあったから・・・

下記で動作確認、うううごいた


/mt-data-api.cgi/v6/search?IncludeBlogs=ブログID&cdSearch=1&SearchContentTypes=コンテンツタイプのID&search=検索したいワード

これで何となく、手入力で確認できたので・・・

javascript書いてみる・・

下記は、MTで再構築するHTMLテンプレートに入れ込む感じ?

バージョンを固定したい場合直接数字を・・・


	var searchkey		= "";
	var DATA_API_PATH	= "<mt:CGIPath><mt:DataAPIScript>";
	var DATA_API_VER	= "<mt:DataAPIVersion>";
	var BLOG_ID			= "<mt:BlogID>";
	var CONTENTTYPE_ID	= "23";		//目標のID

下記は、jsファイル化して呼び出す共通

json.totalResultsとかで最大件数が入ってくるので、limitとかoffsetでページ制御もできるかと・・

	function xxxxSearch(){
		$('#content_data').empty();
		if(searchkey) {
			$.ajax({
				url: DATA_API_PATH + "/v"+DATA_API_VER+"/search?IncludeBlogs="+BLOG_ID +"&cdSearch=1&SearchContentTypes="+CONTENTTYPE_ID+"&search="+searchkey,
				type: "GET",
				dataType: "json",
			}).done(function(json){
				recs = json.items;
				$.each(recs,function(key,item){
					var li_tag = 
					'<div>' +
						'<p>'+item.data[0].data+'</p>' +
						'<p>'+item.data[1].data+'</p>' +
						'<p>'+item.data[2].data+'</p>' +
					'</div>';
					$('#content_data').append(li_tag);
				});
			});
		}
	}
	xxxxSearch();
	$('#search_btn').on("click", function(){
		searchkey = $('#searchkey').val();
		xxxxSearch();
	});

下記はMTのテンプレートに入れる部分


	<form>
		<input type="text" name="searchkey" id="searchkey">
		<button type="button" id="search_btn">検索</button>
	</form>
	<ul id="content_data">
	</ul>

ページャー付けた感じ・・

あと、記事書きながら・・limitとか調べてたから・・下記にSearchContentTypesってあった・・・

https://www.movabletype.jp/documentation/mt8/designers-guide/search/contenttype/contenttype-search-page/

SearchContentTypesは、AND ORが使えるので・・

あくまでも全文検索かなぁ・・ページャーとかなしでいいのであれば、全文検索と複合検索はできそう・・

MTの検索機能のlimitとoffsetを使って・・このページだったら、このoffsetからlimit件数までという処理

1個目のajaxは入力されたキーワードで最大何件あるかをページャー用に調べて、展開する形・・・

このままだと、idが重なる場合があるので配慮がいります。。


$(function() {
	var LIST_LIMIT		= 10;
	var NOWPAGE			= "";
	var start,end;
	var searchkey		= "";
	var DATA_API_PATH	= "<mt:CGIPath><mt:DataAPIScript>";
	var DATA_API_VER	= "<mt:DataAPIVersion>";
	var BLOG_ID			= "<mt:BlogID>";
	var CONTENTTYPE_ID	= 23;
	function xxxxSearch(NOWPAGE){
		if(searchkey) {
			$('#pager').empty();
			$('#content_data').empty();
			$.ajax({
				url: DATA_API_PATH + "/v"+DATA_API_VER+"/search?IncludeBlogs="+BLOG_ID +"&cdSearch=1&SearchContentTypes="+CONTENTTYPE_ID+"&limit=1&search="+searchkey,
				type: "GET",
				dataType: "json",
			}).done(function(json){
				var hits = json.totalResults;
				[NOWPAGE,start,end] = listpage_cal(NOWPAGE,LIST_LIMIT);
				var offset	= start;
				var limit	= LIST_LIMIT;
				$.ajax({
					url: DATA_API_PATH + "/v"+DATA_API_VER+"/search?IncludeBlogs="+BLOG_ID +"&cdSearch=1&SearchContentTypes="+CONTENTTYPE_ID+"&offset="+offset+"&limit="+limit+"&search="+searchkey,
					type: "GET",
					dataType: "json",
				}).done(function(json){
					recs = json.items;
					$.each(recs,function(key,item){
						var li_tag = 
						'<div>' +
							'<p>'+item.data[0].data+'</p>' +
							'<p>'+item.data[1].data+'</p>' +
							'<p>'+item.data[2].data+'</p>' +
						'</div>';
						$('#content_data').append(li_tag);
					});
					pager	= listpage_bar(NOWPAGE,hits,LIST_LIMIT);
					$('#pager').append(pager);
				});
			});
		}
	}
	xxxxSearch("");
	$('#search_btn').on("click", function(){
		searchkey = $('#searchkey').val();
		xxxxSearch("");
	});
	function listpage_cal(page_no,limit_max)
	{
		var pi;
		var start,end;
		//ページインデックス
		if(page_no == '') {
			page_no = 1;
		} else if(!Number.isFinite(page_no)) {
			page_no = 1;
		}
		pi 	= (page_no - 1) * limit_max;
		start	= pi;
		end	= pi + limit_max;
		return[page_no,start,end];
	}
	function listpage_bar(page_no,list_hits,limit_max)
	{
		var prev_one,next_one;
		var end_no,st_no;
		var pages_f,pre_page_a,disp_page_st,disp_page_end;
		var pages;
		var i,j;
		var page_barstr;
		page_barstr="";
		prev_one	= page_no - 1;
		next_one	= page_no + 1;
		end_no	= page_no * limit_max;
		st_no	= end_no - limit_max + 1;
		if(end_no >= list_hits){
			end_no = list_hits;
		}
		pages =  Math.floor(list_hits / limit_max);
		if(list_hits % limit_max) {
			pages ++;
		}
		if(list_hits > limit_max) {
			if(page_no > 1){
				page_barstr = page_barstr + "<li><a href=\"javascript:void(0)\" id=\"pager_1\">先頭へ</a></li>";
				page_barstr = page_barstr + "<li><a href=\"javascript:void(0)\" id=\"pager_"+prev_one+"\">&nbsp;&lt;</a></li>";
			}
			pages_f =  Math.floor(pages / 10);
			if(pages  %  10) {
				pages_a = 1;
			} else {
				pages_a = 0;
			}
			pre_page_f = Math.floor(page_no / 10);
			if(page_no  %  10) {
				pre_page_a = 1;
			}else{
				pre_page_a = 0;
			}
			if(pre_page_a){
				disp_page_st = pre_page_f * 10 + 1;
			} else {
				disp_page_st = pre_page_f * 10 - 9;
			}
			disp_page_end = disp_page_st + 9;
			for(pg=1;pg <= pages;pg++) {
				if(disp_page_end < pg){
					break;
				}
				if(disp_page_st <= pg){
					if(pg != page_no){
						page_barstr = page_barstr + "<li><a href=\"javascript:void(0)\" id=\"pager_"+pg+"\">"+pg+"</a></li>";
					} else {
						page_barstr = page_barstr + "<li><b><a href=\"javascript:void(0)\" class=\"active\">"+pg+"</a></b></li>";
					}
				}
			}
			if(page_no < pages) {
				page_barstr = page_barstr + "<li><a href=\"javascript:void(0)\" id=\"pager_"+next_one+"\">&nbsp;&gt</a></li>";
				page_barstr = page_barstr + "<li><a href=\"javascript:void(0)\" id=\"pager_"+pages+"\">最後へ</a></li>";
			}
		}
		return(page_barstr);
	}
	$(document).on("click", "[id^=pager_]", function () {
		var id_str = $(this).attr("id");
		stArray = id_str.split("_");
		xxxxSearch(parseInt(stArray[1]));
	});
});
	<form>
		<input type="text" name="searchkey" id="searchkey">
		<button type="button" id="search_btn">検索</button>
	</form>
	<ul id="content_data">
	</ul>
	<ul id="pager" class="pagination"></ul>

さいごに

まぁ。カテゴリで検索とキーワードで検索わけちゃえばできそうだけど・・・

複合は、データ件数多いと厳しそう・・・

ただ、APIには記述はないが・・下記にcontent_fieldを指定すると絞り込みできるよってあるけど・・試してない・・

この辺り、API仕様書に反映してくれていれば・・時間節約できたかな。。。

https://www.movabletype.jp/documentation/mt8/designers-guide/search/contenttype/contenttype-search-page/

content_fieldは、コンテンツフィールドの名前もしくはユニーク ID と、コンテンツフィールドの値を、コロン(:)で分けて指定します。

content_field=コンテンツフィールドの名前:コンテンツフィールドの値こんな感じ・・だったと思う

関連

Movabletypeコンテンツデータ
https://www.omakase.net/blog/2022/11/movabletypecontent.html

関連記事