2016年10月30日日曜日
サントリーホール音響設計──豊田泰久|WIRED.jp
http://wired.jp/series/wired-audi-innovation-award/031-yasuhisa_toyota/
2016年10月29日土曜日
2016年10月28日金曜日
2016年10月27日木曜日
2016年10月26日水曜日
2016年10月25日火曜日
Electronでファイルやフォルダの選択
InDesignの自動化に役立つ知識とツールのまとめ - Qiita
2016年10月24日月曜日
Linux 特定のファイル内の特定の文字列だけを置換する
さくらインターネットのメールがGmailやガラケーで迷惑フォルダ・スパム扱いになる
2016年10月22日土曜日
2016年10月21日金曜日
WordPressのアップローダープラグインを作る方法
WordPressの安全なファイルアップロードをAJAXで実現
2016/08/23
Firdaus Zahari
Articles in this issue reproduced from SitePoint
Copyright © 2016, All rights reserved. SitePoint Pty Ltd. www.sitepoint.com. Translation copyright © 2016, KADOKAWA CorporationJapanese syndication rights arranged with SitePoint Pty Ltd, Collingwood, Victoria,Australia through Tuttle-Mori Agency, Inc., Tokyo
プラグインでのアップロード機能の実現は、いつも一筋縄ではいきません。アップロードにおける優れたユーザー体験(UX)の実現が必要な一方、セキュリティ問題からも目が離せません。セキュリティ対策が適切でないと、サイトをセキュリティ上のさまざまな脆弱性発生の潜在的な危険にさらすことになりかねません。
ソリューション全体をスクラッチで構築せず、WordPressのコアコード、特にasync-upload.phpファイルをwp-adminディレクトリに置いて活用すれば、開発をスピードアップできるでしょう。
async-upload.phpファイルの使用にはいくつかのメリットがあります。このファイルはWordPressコア自体によってメディアライブラリーでの非同期アップロード用に使われるので、コードを確実に標準化できます。次に、バリデーションと権限のチェックをすべてしてくれるので、自分でする必要がなくなります。
必要条件
async-upload.phpファイルを使用するには、従うべき規則がいくつかあります。以下にそれぞれの規則を説明します。
- 使用されるfileインプット(ファイル入力欄)はasync-uploadに設定されたname属性を有していなければならない
ひとたびasync-upload.phpファイル内部でバリデーションが通ると、次いでwp_ajax_upload_attachmentが、第1引数としてasync-uploadを使用するmedia_handle_upload関数を呼び出すためです。ほかの値の使用は無効です。
- AJAXリクエストと共に送信されるノンスは、wp_create_nonce('media-form')関数で生成された値とともにデフォルトの_wpnonceキーを使用しなければならない
wp_ajax_upload_attachment関数内部で発生するcheck_ajax_referer形式でのバリデーションのためです。
- AJAXリクエストを介して送信されるデータも、upload-attachmentの値とともにactionキーを有する必要がある
async-upload.phpファイル内部で検証され、値が正しく設定された場合のみwp_ajax_upload_attachment関数をトリガーします。
プラグインについて
プラグインにAJAXファイルアップロードのカスタム機能を構築するアイデアをよりよく説明するため、簡単なプラグインを作成して実際に試します。
この記事では、登録済みのユーザーがちょっとしたコンテストに画像を投稿できるプラグインを作成します。フロントエンドの投稿フォーム、つまりユーザーが直接画像をアップロードできる特定のページに表示されるアップロードフォームです。まさにAJAXアップロード機能の実装にぴったりです。
記事の長さの関係で話をシンプルにしておくため、このプラグインで実現することとしないことを決めておきます。
このプラグインでできることは次のとおりです。
- ショートコードでの任意のページへのフォームの追加を管理者に許可する
- AJAXアップロード機能を備えた投稿フォームをユーザーに表示する
- 投稿後ただちにサイト管理者に通知するEメールを送信する
この記事の範囲では、プラグインに次の機能は付けません。
- 投稿内容をデータベースに格納する
- バックエンドで投稿内容を一覧表示する
- 匿名のユーザーにファイルのアップロードを許可する
プラグインのブートストラッピング
wp-content/pluginsフォルダーに移動して、すべてのプラグインコードを置く新規フォルダーを作成します。ここから先、記事ではこのフォルダー名をsitepoint-uploadとし、すべての関数、フック、コールバックにsu_プリフィックスを付けます。
次に、メインのプラグインファイルを作成し、分かりやすくするためにフォルダーと同じ名前にします。プラグインフォルダー内にjsフォルダーも作成し、とりあえず空のscript.jsファイルを入れておきます。
ここまでで、プラグインのディレクトリ構造は次のようになります。
wp-content/ |-- plugins/ |-- sitepoint-upload/ |-- js/ | |-- script.js |--sitepoint-upload.php
プラグインのメインファイルsitepoint-upload.phpに簡単なプラグインヘッダーを付け、その後プラグインのページに進んで有効化します。以下が作成例です。
<?php /* Plugin Name: Simple Uploader Plugin URI: http://sitepoint.com Description: Simple plugin to demonstrate AJAX upload with WordPress Version: 0.1.0 Author: Firdaus Zahari Author URI: http://www.sitepoint.com/author/fzahari/ */
スクリプトのエンキュー
ここで空のscript.jsファイルをフロントエンドにエンキューできます。これをAJAXアップロード機能の処理にも、投稿フォームの拡張にも使います。
function su_load_scripts() { wp_enqueue_script('image-form-js', plugin_dir_url( __FILE__ ) . 'js/script.js', array('jquery'), '0.1.0', true); } add_action('wp_enqueue_scripts', 'su_load_scripts');
wp_localize_script関数を使って、script.js内部で使用されるデータもローカライズします。必要事項が3つあります。1つ目はadmin-ajax.phpファイルへの正確なURL(AJAX経由でもフォームに投稿するため)、2つ目はasync-upload.phpファイルへの正確なURL、3つ目はwp_create_nonce関数を使って生成するノンスのローカライズです。
ここまで説明してきたwp_enqueue_scriptsフック用のコールバック関数は次のようになります。
function su_load_scripts() { wp_enqueue_script('image-form-js', plugin_dir_url( __FILE__ ) . 'js/script.js', array('jquery'), '0.1.0', true); $data = array( 'upload_url' => admin_url('async-upload.php'), 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('media-form') ); wp_localize_script( 'image-form-js', 'su_config', $data ); } add_action('wp_enqueue_scripts', 'su_load_scripts');
投稿フォーム用のショートコードを登録する
次に、同じことを繰り返しマークアップしなくても希望のページに登録フォームを簡単に追加できるよう、投稿フォーム用のショートコードを登録する必要があります。フォームに実装する内容は次のとおりです。
- ユーザー名用のテキスト入力フィールド
- ユーザーのEメールアドレス用の別のEメール入力フィールド
- AJAXアップロード用のファイル入力欄「async-upload」
- Eメールのプレビュー、エラーメッセージ、その他のアイテム用の多数のプレースホルダーdiv
また、ユーザーが現在ログインしていない場合は投稿フォームを完全に無効にし、代わりにログインリンクを表示します。
function su_image_form_html(){ ob_start(); ?> <?php if ( is_user_logged_in() ): ?> <p class="form-notice"></p> <form action="" method="post" class="image-form"> <?php wp_nonce_field('image-submission'); ?> <p><input type="text" name="user_name" placeholder="Your Name" required></p> <p><input type="email" name="user_email" placeholder="Your Email Address" required></p> <p class="image-notice"></p> <p><input type="file" name="async-upload" class="image-file" accept="image/*" required></p> <input type="hidden" name="image_id"> <input type="hidden" name="action" value="image_submission"> <div class="image-preview"></div> <hr> <p><input type="submit" value="Submit"></p> </form> <?php else: ?> <p>Please <a href="<?php echo esc_url( wp_login_url( get_permalink() ) ); ?>">login</a> first to submit your image.</p> <?php endif; ?> <?php $output = ob_get_clean(); return $output; } add_shortcode('image_form', 'su_image_form_html');
上記のショートコードのコールバック関数について説明します。
- 登録するショートコードはimage_form
- ショートコードのコールバック関数内部でexposeする部分をよりフレキシブルにするため、出力バッファリングを使用
- ファイル入力欄での画像用のファイルの選択をaccept属性経由のみに制限する。ちなみにこれは実質上のファイルバリデーションに代わるものではない(詳しくはこちら)
- ログインURLに関して、ログイン成功後ただちにユーザーが投稿ページにリダイレクトできるように、wp_login_urlへの現在のページのパーマリンクを供給
特定のユーザー権限にupload_files機能を追加する
プラグインを確実に機能させるために、subscriber(購読者)権限の機能を変更する必要があります。なぜならsubscriber権限のユーザーは、デフォルトでファイルのアップロード機能を持たないからです。
function su_allow_subscriber_to_uploads() { $subscriber = get_role('subscriber'); if ( ! $subscriber->has_cap('upload_files') ) { $subscriber->add_cap('upload_files'); } } add_action('admin_init', 'su_allow_subscriber_to_uploads');
ちなみに、subscriber権限はその時点でupload_files機能を持っていない場合のみ変更されます。
これでプラグインの基礎部分ができました。これから、投稿フォームを表示する新規ページを作成します。
ここではtwentysixteenテーマを有効にしたデフォルトのWordPress実装において、フロントエンドでフォームがどのように見えるかを示しています。
サイトからログアウトすると、代わりに通知が表示されます。
プラグインが、いい感じになってきましたね!
AJAXアップロードを実装する
プラグインの基礎部分が正しく構成されたところで、必要なコア機能、AJAXアップロードに集中できます。
進めるにあたって、フォルダー内にあるscript.jsファイルを開き、最初にすべてのコードを即時呼び出し関数式(immediately-invoked function expression:IIFE)内にラップします。
次に、コードをスピードアップするためにいくつかのセレクターをキャッシュします。画像プレビューのdiv、入力されたファイル、アップロード通知の表示に使用されるdivへの参照も含まれます。
(function($) { $(document).ready(function() { var $formNotice = $('.form-notice'); var $imgForm = $('.image-form'); var $imgNotice = $imgForm.find('.image-notice'); var $imgPreview = $imgForm.find('.image-preview'); var $imgFile = $imgForm.find('.image-file'); var $imgId = $imgForm.find('[name="image_id"]'); }); })(jQuery);
キャッシュされたセレクターは、あとで役立ちます。先に述べたとおり、aync-upload.phpファイルでのバリデーションが通るために従うべきルールがいくつかあります。指定された正しいキーまたは値のペアで、AJAXを介してaync-upload.phpファイルへのPOSTリクエストの作成は、FormData APIを使って実行できます。
最初にファイル入力欄にchangeイベントをフックし、入力が変更される場合にかぎり、アップロードをトリガーします。
$imgFile.on('change', function(e) { e.preventDefault(); var formData = new FormData(); formData.append('action', 'upload-attachment'); formData.append('async-upload', $imgFile[0].files[0]); formData.append('name', $imgFile[0].files[0].name); formData.append('_wpnonce', su_config.nonce); $.ajax({ url: su_config.upload_url, data: formData, processData: false, contentType: false, dataType: 'json', type: 'POST', success: function(resp) { console.log(resp); } }); });
コードを上記のようにしてアップロード機能をテストし、うまくいくことを確認します。開発者コンソール(使用しているブラウザに応じた)を使って、出力用のコンソールタブをチェックしてください。アップロード成功後のaync-upload.phpファイルからのレスポンスのサンプルは次のようになります。
wp-content/uploadsディレクトリに移動してファイルがあるかどうかもチェックできます。アップロード機能がきちんと動作していることを確かめたら、アップロードのスクリプトに少しばかり改良を加えます。考えられるいくつかの改良点は次のとおりです。
- アップロード処理中にプログレスバーまたはテキストを表示する
- アップロード成功時にアップロードされた画像のプレビューを表示する
- アップロードが失敗した場合、エラーを表示する
- ユーザーが新規画像をアップロードして現在の画像と差し替える方法を提供する
これらの実装方法を1つずつ説明します。
アップロード処理中にプログレスバーまたはテキストを表示する
これはとても簡単です。jQuery AJAXのbeforeSend用にコールバックを定義するだけで大丈夫です。AJAXアップロード用のコードのある場所に、次のコードブロックを置きます。
beforeSend: function() { $imgFile.hide(); $imgNotice.html('Uploading…').show(); },
ユーザーに進捗状況をテキスト表示するようにあらかじめ定義されたimage-noticeクラスで空のdivを使用します。また、アップロード処理中はファイル入力欄を隠します。
対応しているブラウザーでは、オリジナルのjQuery「xhr」オブジェクトを自分でオーバーライドして、アップロードのパーセンテージも表示できます。$.ajax構成にこれを追加すると次のようになります。
xhr: function() { var myXhr = $.ajaxSettings.xhr(); if ( myXhr.upload ) { myXhr.upload.addEventListener( 'progress', function(e) { if ( e.lengthComputable ) { var perc = ( e.loaded / e.total ) * 100; perc = perc.toFixed(2); $imgNotice.html('Uploading…(' + perc + '%)'); } }, false ); } return myXhr; }
対応しているブラウザーでは、コードのUploadingテキストのあとにアップロードのパーセンテージを簡単に追加できます。なかなか素敵な拡張ですね。対応していないブラウザーでは、この優れたグレースフル・デグラデーションは実装できません。
アップロード成功時にアップロードされた画像のプレビューを表示し、失敗時にはエラーを表示する
async-upload.phpスクリプトからのレスポンスによって、ユーザーに別々のメッセージを表示します。successキーがtrueに設定された場合、アップロードされた画像をユーザーに表示し、ファイル入力欄を隠せます。アップロードが失敗した場合、div内のテキストを前述のimage-noticeに置きかえます。
success: function(resp) { if ( resp.success ) { $imgNotice.html('Successfully uploaded.'); var img = $('<img>', { src: resp.data.url }); $imgId.val( resp.data.id ); $imgPreview.html( img ).show(); } else { $imgNotice.html('Fail to upload image. Please try again.'); $imgFile.show(); $imgId.val(''); } }
$imgIdは、アップロードされた画像のIDの参照に使用する隠しデータです。この値はあとでフォーム投稿に使用しますので、いまは気にしなくて大丈夫です。
ユーザーが新規画像をアップロードして現在の画像と差し替える方法を実現する
ユーザーが現在アップロードされている画像を新規画像と差し替えるための方法として、リンクを供給します。アップロード成功時に表示される通知について、下のコードを、
$imgNotice.html('Successfully uploaded.');
次のコードに変更します。
$imgNotice.html('Successfully uploaded. <a href="#" class="btn-change-image">Change?</a>');
btn-change-imageクラスへのアンカーを設定していますが、メリットがあります。アンカーにイベントリスナーを追加して、クリックされたときに現在の画像のプレビューを消去できるからです。また、通知メッセージを隠し、リセット済みの値でファイル入力欄を再び表示します。
$imgForm.on( 'click', '.btn-change-image', function(e) { e.preventDefault(); $imgNotice.empty().hide(); $imgFile.val('').show(); $imgId.val(''); $imgPreview.empty().hide(); });
さらにchangeイベントを再びトリガーできるように、クリックされたときにfile inputの値をリセットする必要があります。
$imgFile.on('click', function() { $(this).val(''); $imgId.val(''); });
次のセクションに進む前に、もう一度アップロード機能全体を実行して、すべてが目的どおりに動くか確認してください。
プラグインの完成
AJAX経由でフォーム投稿を処理するにあたり、フォームのsubmitイベントにイベントリスナーをバインドします。
$imgForm.on('submit', function(e) { e.preventDefault(); var data = $(this).serialize(); $.post( su_config.ajax_url, data, function(resp) { if ( resp.success ) { $formNotice.css('color', 'green'); $imgForm[0].reset(); $imgNotice.empty().hide(); $imgPreview.empty().hide(); $imgId.val(''); $imgFile.val('').show(); } else { $formNotice.css('color', 'red'); } $formNotice.html( resp.data.msg ); }); });
上記のコードをベースに、WordPress AJAXアクションのビルトインを使ってバックエンドで投稿を処理します。投稿が成功したら、ただちにフォームをリセットし、画像のプレビューを消去してフォームの通知メッセージを緑色に設定します。
投稿が失敗した場合は、フォームの通知メッセージを赤に設定するだけです。こうしておけば、ユーザーは再試行の前にデータから画像を再確認できます。
ここで再びプラグインのメインファイルを開いてAJAXコールバックに追加します。image_submissionにaction値を設定しているので、wp_ajax_image_submissionアクションに有効なコールバックを追加することが必要です。
add_action('wp_ajax_image_submission', 'su_image_submission_cb');
コールバック関数において、最初にしておくべきことがいくつかあります。有効なAJAXノンスのチェックと、ユーザー入力の検証も必要です。この記事の範囲では、新規投稿があったことをサイト管理者にEメールで通知するのみとします。
AJAXコールバック関数のコード全体を示します。
function su_image_submission_cb() { check_ajax_referer('image-submission'); $user_name = filter_var( $_POST['user_name'],FILTER_SANITIZE_STRING ); $user_email = filter_var( $_POST['user_email'], FILTER_VALIDATE_EMAIL ); $image_id = filter_var( $_POST['image_id'], FILTER_VALIDATE_INT ); if ( ! ( $user_name && $user_email && $image_id ) ) { wp_send_json_error( array('msg' => 'Validation failed. Please try again later.') ); } $to = get_option('admin_email'); $subject = 'New image submission!'; $message = sprintf( 'New image submission from %s (%s). Link: %s', $user_name, $user_email, wp_get_attachment_url( $image_id ) ); $result = wp_mail( $to, $subject, $message ); if ( $result ) { wp_send_json_error( array('msg' => 'Email failed to send. Please try again later.') ); } else { wp_send_json_success( array('msg' => 'Your submission successfully sent.') ); } }
今回の目的は、シンプルなcheck_ajax_refererでのチェックとネイティブなPHP関数filter_varでユースケースに事足ります。wp_send_json_errorとwp_send_json_success関数を使ってレスポンスを返すこともできます。
これでプラグインが完成し、すべての機能が使えるようになります。検証のため、フォームをきちんと完成して、アップロードされた画像へのリンクを記載したEメールが受信されるか確認してください。
さらなる改良
記事では、内部async-upload.phpファイルを介したAJAXアップロード方法を示すことに焦点を当ててきたため、実際ところどころで説明を端折ってきました。参考までに、プラグイン全体を拡張できるシンプルな点をいくつか紹介します。
- 投稿に関する付加的な値をキャプチャするフィールドをさらに追加する
- 通知とアップロード進捗のフォームをより良いスタイルにするため、分離したCSSファイルをエンキューする
- 投稿されたデータを再確認できるように、データベースに保存する
- セキュリティ強化のため、アップロード処理のバリデーションをさらに追加する
完成したプラグインのソースコードをGitHubから利用できます。
最後に
結論としては、目のつけどころが分かっていると、プラグインでのAJAXアップロードの実装をスピードアップできるということです。async-upload.phpファイルを使うことで、機能を実装する開発時間を削減できます。また、このファイルはWordPressコアによって管理画面のダッシュボードでのユーザーアップロードの処理に使われているため、信頼性も得られます。
(原文:Enabling AJAX File Uploads in Your WordPress Plugin)
[翻訳:新岡祐佳子]
[編集:Livit]
全文検索エンジン Elasticsearch 使い方
Articles in this issue reproduced from SitePoint
Copyright © 2016, All rights reserved. SitePoint Pty Ltd. www.sitepoint.com. Translation copyright © 2016, KADOKAWA CorporationJapanese syndication rights arranged with SitePoint Pty Ltd, Collingwood, Victoria,Australia through Tuttle-Mori Agency, Inc., Tokyo
Elasticsearchは、高い性能と分散型アーキテクチャで人気のオープンソース検索エンジンです。本記事では、主要な機能およびNode.js検索エンジン作成に使用するプロセスについて順を追って説明します。
Elasticsearch入門
Elasticsearchは、Apache Luceneをベースにした高性能テキスト検索エンジンライブラリーです。Elasticsearchではデータの格納と検索ができますが、主な用途はデータベースではなく、インデックス作成、検索、データに関するリアルタイム統計情報の提供を目標とする検索エンジン(サーバー)です。
Elasticsearchは、複数のノードを追加し別のハードウェアを利用して水平方向のスケーリングを可能にする、分散型アーキテクチャを採用しています。ペタバイトのデータを処理する数千のノードをサポートし、水平方向のスケーリングはノードに障害が発生してもデータをリバランスすることで高可用性を実現します。
インポートされたデータは、検索ですぐに利用できます。Elasticsearchはスキーマフリーで、JSON文書にデータを格納し、データのタイプと構造を自動的に検出します。
Elasticsearchは完全なAPI駆動型で、HTTP経由でJSONデータを使うシンプルなRESTful APIを介し、ほぼすべての操作ができます。Node.js.を含むほぼすべてのプログラミング言語のクライアントライブラリーがあります。本記事では、公式クライアントライブラリーを使用します。
Elasticsearchのハードウェアとソフトウェアの要件はとても柔軟です。推奨されるプロダクション環境は64ギガバイトのメモリと、できるだけ多くのCPUコアです。リソースが制約されたシステムでも実行でき、十分なパフォーマンスを得られます(データセットが巨大ではない場合を仮定しています)。本記事の例なら、2GBのメモリーと単一のCPUコアのシステムで十分です。
Elasticsearchはすべての主要なオペレーティングシステム(Linux、OS X、Windows)で実行できます。実行にはJava Runtime Environmentの最新バージョンが必要です(「Elasticsearchのインストール」を参照)。本記事の例を実行するにはNode.js(v0.11.0以降)とnpmのインストールも必要です。
Elasticsearchの用語
Elasticsearchでは、一般的なデータベースシステムとは異なる独自用語を使用することがあります。Elasticsearchの用語と意味を説明します。
- インデックス(Index):インデックスはElasticsearchコンテキストでは2つの意味がある。1つはデータを追加する操作のことで、データが追加されるとテキストがトークン(例えば単語)に分解され、すべてのトークンが索引付けされる。一方で、インデックスは索引付けされたデータが格納される場所を指す。基本的にはデータをインポートするとインデックスに索引付けされる。データ上で操作を実行したいときは毎回インデックス名を指定する必要がある
- タイプ(Type):Elasticsearchにはインデックス内の文書に詳細分類があり、タイプと呼ばれている。インデックス内のすべての文書にはタイプも必要だ。たとえば、libraryインデックスを定義し、article、book、report、presentationなど数種のデータに索引を付ける。インデックスはほぼオーバーヘッドが固定されているので、インデックスを少なく、タイプを多くすることを勧める
- サーチ(Search):この用語は多くの人が考えるとおりの意味だ。さまざまなインデックスとタイプのデータを検索できる。Elasticsearchには用語、フレーズ、ターム、ファジー、さらには地理データなど、たくさんの種類のクエリがある
- フィルター(Filter):Elasticsearchでは、結果をさらに絞り込むために、異なる基準で検索結果をフィルターできる。文書セットに新しい検索クエリを追加する場合は関連性に基づいて順序が変わることがあるが、フィルターとして同じクエリを追加する場合は順序の変更はない
- 集計(Aggregations):集計データ上で最小値、最大値、平均値、合計、ヒストグラムなど異なる種類の統計を提供する
- 補完(Suggestions):Elasticsearchは入力テキストに、用語やフレーズをベースにしたさまざまな補完を用意している
Elasticsearchのインストール
ElasticsearchはApache2ライセンスで提供されており、無料でダウンロード、使用、編集ができます。Elasticsearchをインストールする前に、コンピューターにJava Runtime Environment(JRE)がインストールされているか確認してください。ElasticsearchはJavaで記述されており、実行はJavaライブラリーに依存しています。システムにJavaがインストールされているか確認するには、コマンドラインに次のように入力します。
java -version
Javaの最新安定版の使用をお勧めします(本記事の作成時点で1.8です)。システムへのJavaインストールのガイドは、ここにあります。
Elasticsearchの最新バージョン(本記事の作成時点で2.4.0)をダウンロードするには、ダウンロードページでZIPファイルをダウンロードしてください。Elasticsearchはインストール不要で、サポートされているすべてのOS用の実行ファイルの完全なセットが単一のzipファイルにまとめられています。ダウンロードしたファイルを解凍すれば完了です。TARファイルや異なるLinuxディストリビューションのパッケージの取得など、ほかにもElasticsearchを実行する方法がいくつかあります(こちらを参照してください)。
OS XにHomebrewをインストールしている場合はbrew install elasticsearchを使用してElasticsearchをインストールできます。Homebrewは実行ファイルをパスへ自動的に追加し、必要なサービスをインストールします。コマンドbrew upgrade elasticsearchだけでアプリケーションの更新もできます。
WindowsでElasticsearchを実行するには、解凍したディレクトリでコマンドラインのbin\elasticsearch.batを実行します。その他のOSの場合はターミナルで./bin/elasticsearchを実行します。この時点ではシステム上で実行します。
前述のとおり、Elasticsearchで実行できるほとんどすべての操作はRESTful APIを介します。Elasticsearchはポート9200をデフォルトで使用しています。正しく実行していることを確認するには、ブラウザーでhttp://localhost:9200/を開くと、実行中のインスタンスについて基本的な情報が表示されます。
インストールおよびトラブルシューティングについての詳細はドキュメントを参照してください。
グラフィカルユーザインタフェース
Elasticsearchは、ほとんどの機能をREST APIを介して提供しており、グラフィカルユーザインタフェース(GUI)は付属していません。APIとNode.jsを使いすべての必要な操作を実行する方法で記事は進めますが、インデックスやデータ、さらには高レベル解析の視覚的情報を提供するGUIツールもあります。
Elastic社が開発したKibanaには、データのリアルタイムサマリーに加え、カスタマイズされた可視化および分析オプションがあります。Kibanaは無料で、詳しいドキュメントが用意されています。
このほかにも、elasticsearch-head、Elasticsearch GUI、さらにはChrome拡張機能のElasticSearch Toolboxなど、コミュニティが開発したツールがあります。いずれも、インデックスやデータをブラウザーで探索したり別の検索や集計クエリを試すにも役立ちます。すべてのツールにはインストールおよび使用方法のチュートリアルが用意されています。
Node.js環境の構築
ElasticsearchはNode.jsの公式モジュールelasticsearchを提供しています。プロジェクトフォルダーにモジュールを追加し、あとで使用するときのために依存オブジェクトを保存します。
npm install elasticsearch --save
そして、以下のようにスクリプトにモジュールをインポートします。
const elasticsearch = require('elasticsearch');
最後に、Elasticsearchとの通信を処理するクライアントを設定します。この記事ではIPアドレス127.0.0.1とポート9200(デフォルト設定)を使用しているローカルマシンでElasticsearchを実行していることを前提とします。
const esClient = new elasticsearch.Client({ host: '127.0.0.1:9200', log: 'error' });
logオプションはすべてのエラーを記録します。本記事の後半では、Elasticsearchと通信するために同じesClientオブジェクトを使用します。ノードモジュールの完全なドキュメントはここにあります。
注意:本記事のソースコードはすべてGitHubにあります。簡単に理解したいなら、使用しているPCにRepoをインストールして、ソースコードを実行してください。
git clone https://github.com:sitepoint-editors/node-elasticsearch-tutorial.git cd node-elasticsearch-tutorial npm install
データのインポート
本記事では、ランダムに生成されたコンテンツを持つ学術論文のデータセットを使用します。データはJSON形式で、データセット内の記事数は1000です。データがどのように表示されるかは、次に紹介するデータセットの1項目を参照してください。
{ "_id": "57508457f482c3a68c0a8ab3", "title": "Nostrud anim proident cillum non.", "journal": "qui ea", "volume": 54, "number": 11, "pages": "109-117", "year": 2014, "authors": [ { "firstname": "Allyson", "lastname": "Ellison", "institution": "Ronbert", "email": "Allyson@Ronbert.tv" }, ... ], "abstract": "Do occaecat reprehenderit dolore ...", "link": "http://mollit.us/57508457f482c3a68c0a8ab3.pdf", "keywords": [ "sunt", "fugiat", ... ], "body": "removed to save space" }
フィールド名は一目で分かります。注意すべき唯一のポイントは、完全かつランダムに生成された記事(段落数100~200)が含まれるため、ここではbodyフィールドが表示されないことです。完全なデータセットはこちらにあります。
Elasticsearchには、単一データポイントの索引付け、更新、削除の方法がありますが、データのインポートには、大規模データセットの操作をより効果的に実行するのに使用される、Elasticserchのbulkメソッドを使います。
// index.js const bulkIndex = function bulkIndex(index, type, data) { let bulkBody = []; data.forEach(item => { bulkBody.push({ index: { _index: index, _type: type, _id: item.id } }); bulkBody.push(item); }); esClient.bulk({body: bulkBody}) .then(response => { console.log('here'); let errorCount = 0; response.items.forEach(item => { if (item.index && item.index.error) { console.log(++errorCount, item.index.error); } }); console.log( `Successfully indexed ${data.length - errorCount} out of ${data.length} items` ); }) .catch(console.err); }; const test = function test() { const articlesRaw = fs.readFileSync('data.json'); bulkIndex('library', 'article', articles); };
例では、インデックス名をlibrary、タイプをarticleとし、インデックスしたいJSONデータに渡すbulkIndex関数を呼び出します。bulkIndex関数は、esClientオブジェクト上のbulkメソッドを順に呼び出します。このメソッドは、bodyプロパティを持つオブジェクトを引数として受け取ります。
bodyプロパティに指定された値は、各操作に2つのエントリーがある配列です。最初のエントリーの操作タイプはJSONオブジェクトと指定されています。このオブジェクト内では、indexプロパティは実行する操作(今回はドキュメントのインデックス作成)だけでなく、インデックス名、タイプ名、ドキュメントIDを決定します。次のエントリーはドキュメント自体に相当します。
将来的には、この方法で同じインデックスに(本やレポートなど)ほかのタイプのドキュメントを追加する可能性に注意してください。また、各ドキュメントには任意で固有のIDを割り当てられます。固有のIDを割り当てない場合は、Elasticsearchがランダム生成したIDを割り当てます。
リポジトリーをクローン化している場合、プロジェクトルートから次のコマンドを実行して、Elasticsearchにデータをインポートできます。
$ node index.js 1000 items parsed from data file Successfully indexed 1000 out of 1000 items
データが正しくインデックスされたか確認する
Elasticsearchの大きな特徴の1つは、リアルタイムに近い検索です。ドキュメントがインデックスされると、1秒以内で検索できるようになります(こちらを参照)。データがインデックスされたあと、indices.jsを実行するとインデックス情報を確認できます(ソースはここ)。
// indices.js const indices = function indices() { return esClient.cat.indices({v: true}) .then(console.log) .catch(err => console.error(`Error connecting to the es client: ${err}`)); };
クライアントのcatオブジェクトのメソッドは、実行中のインスタンスのさまざまな情報を提供します。indicesメソッドは、すべてのインデックス、ステータス、ドキュメント数、ディスク上でのサイズを一覧表示します。vオプションはcatメソッドからのレスポンスにヘッダーを追加します。
上のコードを実行すると、クラスタのステータスを示すカラーコードの出力が分かります。赤色は、クラスタになにか間違いがあり実行されていないことを示しています。黄色は、クラスタは実行中であるものの警告が出ていることを意味し、緑色は、すべてが順調に動作していることを意味します。
ローカルマシンで実行しているほとんどの場合、(設定に応じて)黄色のステータスが表示されます。これはデフォルトの設定にクラスタ用の5つのノードが含まれていることが理由ですが、ローカルマシンはインスタンスを1つだけ実行しているからです。実稼働環境では常に緑色のステータスを目指すべきですが、本記事では黄色のステータスのままでElasticsearchの使用を継続します。
$ node indices.js elasticsearch indices information: health status index pri rep docs.count docs.deleted store.size pri.store.size yellow open library 5 1 1000 0 41.2mb 41.2mb
ダイナミックマッピングとカスタムマッピング
Elasticsearchはスキーマフリーです。これは(SQLデータベースでのテーブル定義のように)データ構造を定義する必要がなく、インポートする前にElasticsearchが自動的に検出することです。ただし、スキーマフリーとされてはいるものの、データ構造上の制限がいくつかあります。
Elasticsearchはマッピングとしてデータ構造を参照します。マッピングが存在しない場合は、データがインデックスされたときにElasticsearchがJSONデータの各フィールドを調べ、タイプに基づいて自動的にマッピングを定義します。フィールドのマッピングエントリーがすでに存在する場合は、同じフォーマットで新規データが追加されているか確認します。それ以外の場合はエラーとなります。
たとえば、{"key1": 12}がすでにインデックスされている場合はElasticsearchが自動的にフィールドkey1をlongとしてマッピングします。するとlongになるのはフィールドタイプkey1なので、{"key1": "value1", "key2": "value2"}をインデックスしようとするとエラーが出ます。同時に、オブジェクト{"key1": 13, "key2": "value2"}はタイプstringのkey2が追加されることで問題なくマッピングされます。
マッピングに関してこれ以上詳細に説明はしませんが、ほとんどの部分で自動マッピングがうまく動作することは伝えておきます。マッピングの詳しい解説は、Elasticsearchのドキュメントを読んでください。
(後編に続く)
※本記事はMark Brown、Vildan Softic、Moritz Krögerが査読を担当しています。最高のコンテンツに仕上げるために尽力してくれたSitePointの査読担当者のみなさんに感謝します。
(原文:Build a Search Engine with Node.js and Elasticsearch)
[翻訳:柴田理恵/編集:Livit]
Copyright © 2016, Behrooz Kamali All Rights Reserved.
Behrooz Kamali
MEANスタックに特化したフルスタック開発者です。オペレーションズリサーチ、データ分析、アルゴリズム設計、効率の分野の専門知識を習得して、産業およびシステムエンジニアリングの博士号も取得しました。開発をしていないときは、教えることや、新しいことを学ぶのを楽しんでいます。
https://www.webprofessional.jp/search-engine-node-elasticsearch-1/