.NET Framework

.NET 向けエンタープライズ検索のビルド

Damian Zapart

コード サンプルのダウンロード

近年登場したクラウド コンピューティングからは、企業もユーザーも同様に恩恵を受けています。企業は顧客についての情報をかつてないほど多く把握し、顧客に合わせた対応を取れるようになりました。ユーザーはほぼどこからでもデータを入手できるため、アクセスが容易かつ便利になります。このようなデータをすべて保管するために、巨大なデータセンターが世界各地に建築されています。ところが、ビッグ データには大きな課題もあります。

John Naisbitt の著書『メガトレンド』(三笠書房、1983 年) の有名な一節「We are drowning in data but starving for information (私たちはデータの海におぼれているが、情報には飢えている)」は、ビッグ データ市場の現状を的確に表現しています。企業はペタバイト規模のデータを保管できても、そのデータを解明して、検索できるようにすることに苦労しています。というのも、ほとんどのデータ ウェアハウスでは、特定のビッグ データ ストア内の複数のコレクション間に、構造化されていないかたち (NoSQL) でデータが保管されているためです。そのうえ、複数のウェアハウスに分散して保管されている場合もあります。さらに、JSON ドキュメント、Microsoft Office など、データの形式もさまざまです。構造化されていないコレクションの中を検索するとしても、コレクションが 1 つであれば問題ありません。しかし、所在がわからないデータの一部を求めて、構造化されていないデータを複数のコレクションからすべて検索するのはかなり難しくなります。ここで重要な役割を果たすのがエンタープライズ検索です。

エンタープライズ検索

多くのデータ ソースを持つ大企業が、企業のパブリック データ ソースをすべて検索できるインターフェイスを 1 つ構築し、社内外のユーザーに提供するとしたらどうすればよいでしょう。これがエンタープライズ検索の基本的な課題です。その単一インターフェイスとしては、API 、企業の Web サイト、内部にオートコンプリート機能を実装した簡単なテキスト ボックスなどが考えられます。どのようなインターフェイスであったとしても、構造化されたデータベースと構造化されていないデータベース、形式がさまざまなイントラネット ドキュメント、その他の API やデータ ソースなど、データが存在する世界全体を検索できなくてはなりません。

複数のデータセット内の検索は非常に複雑になるため、エンタープライズ検索ソリューションとして広く認識されているものは少なく、高いハードルが課せられます。エンタープライズ検索ソリューションは、次の機能を含む必要があります。

  • コンテンツ認識: 特定種類のデータの所在を認識します。
  • リアルタイムのインデックス付け: すべてのデータにインデックスを付けられるようにします。
  • コンテンツ処理: さまざまなデータ ソースにアクセスできます。

最も有名なエンタープライズ検索ソリューションの 1 つに、オープンソースの Elasticsearch (elasticsearch.org、英語) があります。これは Apache Lucene (lucene.apache.org、英語) 上に構築された Java ベースのサーバーで、複数のデータ ソース間で全文検索を実行します。JSON をサポートし、REST Web インターフェイスを備えているだけでなく、可用性が高く、競合の管理やリアルタイム分析を実行します。完全な機能については bit.ly/1vzoUrR (英語) を参照してください。

大まかに言うと、Elasticsearch のデータ保管方法は非常に簡単です。サーバー内の構造の最上位要素がインデックス (index) で、複数のインデックスが同じデータ ストア内に存在することができます。インデックス自体はドキュメント (document) のコンテナーにすぎません (ドキュメントは 1 つでも複数でもかまいません)。各ドキュメントは、1 つ以上の (構造が定義されていない) フィールド (field) のコレクションです。各インデックスには、タイプ (type) という単位に集約されるデータが含まれることがあります。タイプは、特定のインデックス内のデータの論理グループを表します。

Elasticsearch は、リレーショナル データベースにおけるテーブルと同じように考えるとわかりやすくなります。テーブルの行と列と、インデックスのドキュメントとフィールドとの間には同じ相関関係があります。ドキュメントが行に、フィールドが列に相当します。ただし Elasticsearch の場合、固定のデータ構造やデータベース スキーマはありません。

前述のように、開発者は REST Web インターフェイスを通じて Elasticsearch サーバーと通信します。つまり、インデックス、タイプ、データなどのシステム情報は、ブラウザーやその他の Web クライアントから REST Web 要求を送信するだけで照会できます。以下に GET 要求の例をいくつか示します。

  • 全インデックスのクエリ:
           http://localhost:9200/_cat/indices/?v
  • インデックス メタデータのクエリ:
           http://localhost:9200/clients/_stats
  • 全インデックス データのクエリ:
           http://localhost:9200/clients/_search?q=*:*
  • インデックス内の特定フィールド値の検索:
           http://localhost:9200/clients/_search?q=field:value
  • インデックスのマッピング タイプ内の全データの取得:
           http://localhost:9200/clients/orders/_search?q=*:*

検索の作成

ここからは、複数のソースを持つシンプルなソリューションの作成方法をデモします。デモでは、Elasticsearch 1.3.4、JSON ドキュメント、PDF ドキュメント、および SQL Server データベースを使用します。まず、Elasticsearch のセットアップについて簡単に説明し、各データ ソースに接続してデータを検索できるようにします。わかりやすいように、身近な例として恒例の Contoso 社のデータ ソースを使用します。

複数のテーブルを持つ SQL Server 2014 データベースを使用しますが、使うのは dbo.Orders だけです。これは名前のとおり、Contoso 社の顧客からの注文レコードを保存します。レコードの量は膨大ですが、次のように管理は簡単です。

CREATE TABLE [dbo].[Orders]
(
  [Id] [int] IDENTITY(1,1) NOT NULL primary key,
  [Date] [datetime] NOT NULL,
  [ProductName] [nvarchar](100) NOT NULL,
  [Amount] [int] NOT NULL,
  [UnitPrice] [money] NOT NULL
);

また、フォルダー階層にまとめられた複数の社内ドキュメントとのネットワーク共有もあります。これらは、同社が過去に企画したさまざまな製品マーケティング キャンペーンのドキュメントで、PDF や Microsoft Office Word など、複数の形式で保存されています。ドキュメントの平均サイズは約 1 MB です。

さらに、同社のクライアント情報を JSON 形式で公開する社内 API もあります。応答の構造がわかっているため、それをタイプ クライアントのオブジェクトに簡単に逆シリアル化することができます。目標は、Elasticsearch エンジンを使用してすべてのデータ ソースを検索できるようにすることです。さらに、Elasticsearch サーバーを 1 回呼び出すだけですべてのインデックスに対しエンタープライズ クエリを実行する、Web API 2 ベースの Web サービスも作成します (Web API 2 の詳細については bit.ly/1ae6uya (英語) を参照してください)。この Web サービスは、潜在的なヒントを含む候補リストとして結果をエンド ユーザーに返します。このリストは、ASP.NET MVC アプリケーションに埋め込まれているオートコンプリート コントロールや、その他すべての種類の Web サイトから使用することができます。

セットアップ

まず Elasticsearch サーバーをインストールします。Windows の場合、自動と手動のどちらでインストールしても、Elasticsearch サーバーをホストする Windows サービスが実行されるようになります。自動インストールは非常に簡単で、すぐに終わります。Elasticsearch MSI インストーラー (bit.ly/12RkHDz、英語) をダウンロードして実行するだけです。ただし、残念ながら Java バージョンを選択することはできません。さらに問題なのが Elasticsearch バージョンも選択できないことです。対照的に、手動インストール プロセスは少し手間がかかりますが、コンポーネントをより細かく制御できるため、今回はこちらの方が適しています。

Elasticsearch を手動で Windows サービスとしてセットアップする手順は以下のとおりです。

  1. 最新の Java SE Runtime Environment (www.oracle.com/technetwork/java/javase/downloads/index.html、英語) をダウンロードしてインストールします。
  2. JAVA_HOME という環境変数を追加します。これは "C:\Program Files\Java\jre7" など、Java をインストールしたフォルダー パスにします (図 1 参照)。
  3. Elasticsearch のファイルを bit.ly/1upadla (英語) からダウンロードして、解凍します。
  4. 解凍したソースを [Program Files] 下の [Elasticsearch] フォルダーに移動します (省略可能)。
  5. コマンド プロンプトを管理者として起動し、以下のように install パラメーターを指定して service.bat を実行します。
           C:\Program Files\Elasticsearch\elasticsearch-1.3.4\bin>service.bat install

Java_Home 環境変数の設定
図 1 Java_Home 環境変数の設定

これで Windows サービスが稼働するようになります。Elasticsearch サーバーには localhost のポート 9200 からアクセスできます。どの Web ブラウザーからも http://localhost:9200/ という URL に Web 要求を送信でき、以下のような応答が返ります。

{
  "status" : 200,
  "name" : "Washout",
  "version" : {
    "number" : "1.3.4",
    "build_hash" : "a70f3ccb52200f8f2c87e9c370c6597448eb3e45",
    "build_timestamp" : "2014-09-30T09:07:17Z",
    "build_snapshot" : false,
    "lucene_version" : "4.9"
  },
  "tagline" : "You Know, for Search"
}

これで Elasticsearch のローカル インスタンスの準備はできましたが、手を加えなければまだ SQL Server に接続したり、データ ファイルを全文検索することはできません。これらの機能を有効にするには、いくつかプラグインもインストールする必要があります。

Elasticsearch の拡張

前述のように、手を加えないバージョンの Elasticsearch では SQL Server、Office、PDF などの外部データ ソースにインデックスを付けることができません。これらのデータ ソースをすべて検索可能にするにはいくつかプラグインをインストールする必要があります。インストールは実に簡単です。

まず、attachment を全文検索できるようにします。ここでいう attachment とは、Elasticsearch データ ストアに JSON ドキュメントとしてアップロードされているソース ファイルが base64 エンコードで表現されたものです (attachment タイプの詳細については bit.ly/12RGmvg (英語) を参照してください)。このために必要なプラグインは "Mapper Attachments Type for Elasticsearch" バージョン 2.3.2 です (bit.ly/1Alj8sy、英語)。これは、ドキュメントの全文検索を有効にする Elasticsearch の拡張機能で、Apache Tika プロジェクト (tika.apache.org、英語) をベースとしています。Apache Tika は、さまざまな種類のドキュメントからメタデータとテキスト コンテンツを検出および抽出するほか、bit.ly/1qEyVmr (英語) にリストアップされているファイル形式のサポートを提供します。

Elasticsearch のほとんどのプラグインと同様、インストールはきわめて簡単で、コマンド プロンプトを管理者として起動して以下のコマンドを実行するだけです。

bin>plugin --install elasticsearch/elasticsearch-mapper-attachments/2.3.2

プラグインをダウンロードおよび展開したら、Elasticsearch Windows サービスを再起動します。

次に、SQL Server のサポートを構成します。もちろん、このためのプラグインもあります。JDBC River (bit.ly/12CK8Zu、英語) というプラグインは、SQL Server などの JDBC ソースからデータを取得して、Elasticsearch でのインデックス付けを可能にします。このプラグインは簡単にインストールおよび構成できますが、インストールでは 3 段階のプロセスを踏む必要があります。

  1. まず、SQL Server 用の Java ベースのデータ プロバイダー、Microsoft JDBC Driver 4.0 をインストールします (www.microsoft.com/ja-JP/download/details.aspx?id=11774 からダウンロードできます)。重要なのが、ダウンロード ファイルのコンテンツを [Program Files] フォルダー直下の [Microsoft JDBC Driver 4.0 for SQL Server] というフォルダー (存在しない場合は作成する必要があります) に展開することです。パスは C:\Program Files\Microsoft JDBC Driver 4.0 for SQL Server のようになります。
  2. 以下のコマンドを実行してプラグインをインストールします。
           bin> plugin --install
           jdbc --url "http://xbib.org/repository/org/xbib/elasticsearch/plugin/elasticsearch-river-jdbc/1.3.4.4/elasticsearch-river-jdbc-1.3.4.4-plugin.zip"
  3. 最初の手順で展開した SQLJDBC4.jar ファイル (C:\Program Files\Microsoft JDBC DRIVER 4.0 for SQL Server\sqljdbc_4.0\enu\SQLJDBC4.jar) を、[Elasticsearch] フォルダー直下の [lib] フォルダー (C:\Program Files\Elasticsearch\lib) にコピーします。その後 Windows サービスを忘れずに再起動します。

これで必要なプラグインがすべてインストールされました。インストールが適切に完了したことを確認するために、以下のコマンドを HTTP GET 要求として送信します。

http://localhost:9200/_nodes/_all/plugins

この応答では、インストールしたプラグインが 2 つとも表示されるはずです (図 2 参照)。

インストールしたプラグイン
図 2 インストールしたプラグイン

SQL Server のセットアップ

JDBC River と共に SQL Server を使用するには、TCP/IP 経由で SQL Server のインスタンスにアクセスできるようにする必要がありますが、既定では無効になっています。有効にするのは簡単で、SQL Server 構成マネージャーを起動して [SQL Server ネットワーク構成] に移動し、接続する SQL Server インスタンスで [TCP/IP] プロトコルの [状態] を [有効] にします (図 3 参照)。次に、SQL Server Management Studio で [サーバー名] を "localhost 1433" (ポート 1433 は TCP/IP 経由で SQL Server にアクセスする場合の既定のポート) に設定すれば、SQL Server インスタンスにログインできるようになります。

SQL Server インスタンスの TCP/IP の有効化
図 3 SQL Server インスタンスの TCP/IP の有効化

ソースを検索可能にする

必要なプラグインをすべてインストールしたので、ここでデータを読み込みます。前述のように、Elasticsearch でインデックスを付けるデータ ソースは 3 種類 (JSON ドキュメント、ファイル、および SQL Server データベースの Order テーブル) です。これらのデータ ソースにインデックスを付ける方法はいくつもありますが、ここでは今回実装した .NET ベースのアプリケーションを使用すればインデックス付けがどのように簡単になるかお見せします。そのため、インデックス付けの前提条件としてプロジェクトに NEST (bit.ly/1vZjtCf、英語) という外部ライブラリをインストールします。NEST は、Elasticsearch Web インターフェイスの単なるマネージ ラッパーです。このライブラリは NuGet から入手できるので、プロジェクトに追加する場合は Package Manager Console で以下のコマンドを実行するだけです。

PM> Install-Package NEST

これで NEST ライブラリがソリューションに追加されるため、ElasticSearchRepository という新しいクラス ライブラリ プロジェクトを作成します。この名前にした理由は、(NEST ライブラリの一部である) ElasticClient クラスからのすべての関数呼び出しを、ソリューションの別の部分と切り離すことにしたためです。こうすれば、今回のプロジェクトが、Entity Framework ベースのアプリケーションに広く採用されているリポジトリの設計パターンと似てくるため、理解しやすくなります。また、このプロジェクトにはクラスが 3 つしかありません。1 つは BaseRepository クラスで、継承されたクラスと ElasticClient のインスタンスを初期化および公開します。その他 2 つのクラスは以下のとおりです。

  • IndexRepository: インデックスの操作、マッピングの設定、ドキュメントのアップロードに使用する読み取り/書き込みクラスです。
  • DiscoveryRepository: API ベースの検索操作中に使用する読み取り専用のクラスです。

ElasticClient 型の保護されているプロパティがある、BaseRepository クラスの構造を図 4 に示します。ElasticClient 型は NEST ライブラリの一部として提供され、Elasticsearch サーバーとクライアント アプリケーションとの間の通信を一元化します。そのインスタンスを作成するには、Elasticsearch サーバーに URL を渡します。ここでは、省略可能な、クラスのコンストラクターのパラメーターとして渡しています。パラメーターが null の場合、既定の値 http://localhost:9200 が使用されます。

図 4 BaseRepository クラス

namespace ElasticSearchRepository
{
  using System;
  using Nest;
  public class BaseRepository
  {
    protected ElasticClient client;
    public BaseRepository(Uri elastiSearchServerUrl = null)
    {
      this.client = elastiSearchServerUrl != null ?
        new ElasticClient(new ConnectionSettings(elastiSearchServerUrl)) :
        : new ElasticClient();
    }
  }
}

クライアントの準備ができたので、まずは以下のように Client のデータにインデックスを付けます。これはプラグインの必要がない最も簡単なシナリオになります。

public class Client
{
  public int Id { get; set; }
  public string Name { get; set; }
  public string Surname { get; set; }
  public string Email { get; set; }
}

この種類のデータにインデックスを付けるために、Elasticsearch クライアントのインスタンスと、Index<T> 関数を呼び出します。T は Client クラスの型で、API から返されるシリアル化されたデータを表します。このジェネリック関数は、T オブジェクト クラスのインスタンス、ターゲットとなるインデックスの名前、そのインデックス内のマッピングの名前という 3 つのパラメーターを受け取ります。

public bool IndexData<T>(T data, string indexName =
  null, string mappingType = null)
  where T : class, new()
  {
    if (client == null)
    {
      throw new ArgumentNullException("data");
    }
    var result = this.client.Index<T>(data,
      c => c.Index(indexName).Type(mappingType));
    return result.IsValid;
  }

NEST は、ジェネリック型に基づいてターゲットのインデックス名を作成するのに既定のロジックを適用するので、最後の 2 つのパラメーターは省略可能です。

次に、同社製品に関係するマーケティング関連のドキュメントにインデックスを付けます。これらのファイルはネットワーク共有に保管されているため、各ドキュメントに関する情報を単純な MarketingDocument クラスにラップします。注意すべき点として、Elasticsearch でドキュメントにインデックスを付ける場合は、以下のように、それを Base64 エンコードの文字列としてアップロードする必要があります。

public class MarketingDocument
{
  public int Id { get; set; }
  public string Title { get; set; }
  public string ProductName { get; set; }
  // Base64-encoded file content.
  public string Document { get; set; }
}

クラスの準備ができたので、ElasticClient を使用して、MarketingDocument クラスの特定のフィールドを attachment としてマークすることができます。このためには、以下のように "products" という新しいインデックスを作成して、マーケティングのマッピングを新しく追加します (わかりやすくするために、クラス名とマッピング名は同じにします)。

private void CreateMarketingIndex()
  {
    client.CreateIndex("products", c =>
      c.AddMapping<Marketing>
      (m => m.Properties(ps =>ps.Attachment(a =>
            a.Name(o =>o.Document).TitleField(t =>
            t.Name(x => x.Name)
            TermVector(TermVectorOption.WithPositionsOffsets)
        )))));
  }

これで、.NET 型、マーケティング データのマッピング、attachment の定義を作成できたので、以下のように、顧客データにインデックスを付けたのと同じ方法で、ファイルのインデックス付けを開始します。

var documents = GetMarketingDocumentsMock();
documents.ForEach((document) =>
{
  indexRepository.IndexData<MarketingDocument>(document, "marketing");
});

最後に、Elasticsearch で JDBC River をセットアップします。残念ながら NEST ではまだ JDBC River がサポートされていません。理論上は、Raw 関数を使用して JDBC River マッピングを作成し、未処理の要求を JSON で送信することはできますが、過度に複雑化するのは気が進みません。したがって、以下のパラメーターを指定して、マッピング作成プロセスを完了することにします。

  • SQL Server データベースへの接続文字列
  • データのクエリに使用する SQL クエリ
  • 更新スケジュール
  • ターゲットのインデックス名とタイプ (省略可能)

(構成可能なパラメーターの完全なリストについては bit.ly/12CK8Zu (英語) を参照してください)

JDBC River のマッピングを新たに作成するため、PUT 要求を、内部で指定されている要求本文と共に、以下の URL に送信する必要があります。

http://localhost:9200/_river/{river_name}/_meta

図 5 の例では、JDBC River のマッピングを新しく作成するために要求本文を追加しました。その要求本文は、ローカルの SQL Server インスタンスでホストされている Contoso データベースに接続します。また、TCP/IP を通じてポート 1433 でアクセスすることができます。

図 5 JDBC River マッピングを新しく作成するための HTTP PUT 要求

PUT http://localhost:9200/_river/orders_river/_meta
{
"type":"jdbc",
"jdbc":
  {
  "driver": "com.microsoft.sqlserver.jdbc.SQLServerDriver",
  "url":"jdbc:sqlserver://127.0.0.1:1433;databaseName=Contoso",
  "user":"elastic",
  "password":"asd",
  "sql":"SELECT *  FROM dbo.Orders",
  "index" : "clients",
  "type" : "orders",
  "schedule": "0/30 0-59 0-23 ? * *"
  }
}

ログイン名に "elastic"、パスワードに "asd" を使用してユーザーを認証し、以下の SQL コマンドを実行します。

SELECT * FROM dbo.Orders

この SQL クエリによって返されるデータの各行は、orders というマッピング タイプのクライアント インデックス下でインデックスが付けられます。このインデックス付けは 30 秒ごとに実行されます (Cron 表記で表現されます。詳細については bit.ly/1hCcmnN (英語) を参照してください)。

このプロセスが完了したら、以下のような Elasticsearch ログ ファイル (/logs/ elasticsearch.log) 情報が生成されます。

[2014-10-2418:39:52,190][INFO][river.jdbc.RiverMetrics]
pipeline org.xbib.elasticsearch.plugin.jdbc.RiverPipeline@70f0a80d
complete: river jdbc/orders_river metrics: 34553 rows, 6.229481683638776 mean, 
  (0.0 0.0 0.0), ingest metrics: elapsed 2 seconds, 
  364432.0 bytes bytes, 1438.0 bytes avg, 0.1 MB/s

JDBC River の構成に何か問題があると、ログにエラー メッセージが表示されます。

データの検索

Elasticsearch エンジンですべてのデータにインデックスを付けたら、クエリを開始します。もちろん、Elasticsearch サーバーに簡単な要求を送信して、1 つまたは複数のインデックスとマッピング タイプを同時にクエリすることはできますが、もっと便利で、より現実的なシナリオを目指します。したがって、プロジェクトを 3 つのコンポーネントに分割します。1 つ目のコンポーネントは既に扱った Elasticsearch で、http://localhost:9200/ からアクセスできます。2 つ目のコンポーネントは、Web API 2 テクノロジを使用して構築する API です。3 つ目は、Elasticsearch でインデックスをセットアップしたり、インデックスにデータを渡すために使用するコンソール アプリケーションです。

新しい Web API 2 プロジェクトを作成するには、まず空の ASP.NET Web アプリケーション プロジェクトを作成して、Package Manager Console で以下の install コマンドを実行します。

Install-Package Microsoft.AspNet.WebApi

プロジェクトが作成されたら、クライアントからのクエリ要求を処理して Elasticsearch に渡すために使用する、新しいコントローラーを追加します。そのコントローラー DiscoveryController を追加するには、Web API の ApiController クラス (v2.1) を追加するだけです。また、Search 関数を実装することも必要です。この関数は http://website/api/discovery/search?searchTerm=user_input という URL で公開されます。以下に例を示します。

[RoutePrefix("api/discovery")]
public class DiscoveryController : ApiController
{
  [HttpGet]
  [ActionName("search")]
  public IHttpActionResult Search(string searchTerm)
  {
    var discoveryRepository = new DiscoveryRepository();
    var result = discoveryRepository.Search(searchTerm);
    return this.Ok(result);
  }
}

Web API 2 エンジンが自己参照ループが原因で応答をシリアル化できない場合、AppStart フォルダーにある WebApiConfig.cs ファイルに以下の部分を追加する必要があります。

GlobalConfiguration.Configuration
.Formatters
.JsonFormatter
.SerializerSettings
.ReferenceLoopHandling =
      ReferenceLoopHandling.Ignore;

図 6 に示すように、作成したコントローラーの本体で、DiscoveryRepository 型のクラスのインスタンスを作成しました。これは、NEST ライブラリの ElasticClient 型の単なるラッパーです。この非ジェネリックの読み取り専用のリポジトリの中に、2 種類の Search 関数を実装しました。どちらの関数も動的な型を返します。両方の関数本体が動的な型を返すようにするのは重要で、1 つのインデックスだけではなくすべてのインデックスとタイプを同時にクエリすることになります。つまり、結果は異なる構造 (およびタイプ) になります。この 2 つの関数の違いはクエリの手法のみです。1 つ目の関数では、完全一致の検索を実行する QueryString メソッド (bit.ly/1mQEEg7、英語) を使用しています。2 つ目の関数で使用しているのは、インデックス全体であいまい検索を実行する Fuzzy メソッド (bit.ly/1uCk7Ba、英語) です。

図 6 2 種類の検索の実装

namespace ElasticSearchRepository
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  public class DiscoveryRepository : BaseRepository
  {
    public DiscoveryRepository(Uri elastiSearchServerUrl = null)
      : base(elastiSearchServerUrl)
    {
    }
    ///<summary>  
    public List<Tuple<string, string>> SearchAll(string queryTerm)
    {
      var queryResult=this.client.Search<dynamic>(d =>
        d.AllIndices()
        .AllTypes()
        .QueryString(queryTerm));
      return queryResult
        .Hits
        .Select(c => new Tuple<string, string>(
          c.Indexc.Source.Name.Value))
        .Distinct()
        .ToList();
     }
     ///<summary>  
     public dynamic FuzzySearch(string queryTerm)
     {
       return this.client.Search<dynamic>(d =>
         d.AllIndices()
         .AllTypes()
         .Query(q => q.Fuzzy(f =>
           f.Value(queryTerm))));
     }
  }
}

API の準備ができたので、GET 要求を http://website:port/api/discovery/search?searchTerm=user_input に送信してテストを実行し、ユーザー入力を searchTerm クエリ パラメーターの値として渡すことができます。したがって、図 7 は API が "scrum" という検索ワードに対して生成した結果になります。スクリーンショットで強調表示しているように、Search 関数がデータ ストア内の全インデックスにクエリを実行すると同時に、複数のインデックスからヒットしたものを返しています。

"scrum" という検索ワードに対する API の検索結果
図 7 "scrum" という検索ワードに対する API の検索結果

API 層を実装することで、複数のクライアント (Web サイトやモバイル アプリなど) を実装する可能性を含めました。それらのクライアントが API 層を使用できるため、エンタープライズ検索機能をエンド ユーザーに提供することが可能になります。ASP.NET MVC 4 ベースの Web クライアントにオートコンプリート コントロールを実装する例については、私のブログ (bit.ly/1yThHiZ、英語) を参照してください。

まとめ

ビッグ データは、テクノロジ市場の大きなチャンスであると同時に、大きな課題でもあります。ペタバイト規模でデータが保管される世界では、所在がはっきりとわからないデータをすばやく検索する機能の実装が課題の 1 つであり、大きなチャンスも秘めています。今回は、まずエンタープライズ検索を実装する方法について説明し、Elasticsearch と NEST ライブラリを組み合わせてエンタープライズ検索を .NET Framework で実行する方法をデモしました。


Damian Zapart は、Citigroup Inc. の開発リードで、主にエンタープライズ ソリューションに特化しています。最先端のテクノロジ、設計パターン、およびビッグ データに関心を持つプログラミングの専門家でもあります。彼のことをもっと知りたい方は、彼のブログ bit.ly/1suoKN0 (英語) をご覧ください。

この記事のレビューに協力してくれた技術スタッフの Evren Onem (D&B) と Bruno Terkaly (マイクロソフト) に心より感謝いたします。
Evren Onem (D&B) は D&B のプリンシパル ソフトウェア エンジニアです。非常にスケーラブルな REST API を設計および構築することに従事しています。また、コグニティブ無線のアドホック ネットワークの夜間調査員でもあります。

Bruno Terkaly は、デバイスに依存しない、業界をリードするアプリケーションやサービスを開発できるようにすることを目標にするマイクロソフトのプリンシパル ソフトウェア エンジニアです。テクノロジが実現可能かどうかという視点を通り越して、米国で最高のクラウド商談やモバイル商談を進めることを担当しています。ISV が評価、開発、配置を行う際に、アーキテクチャに関するガイダンス提供したり、技術的な細かいサポート作業を行うことによって、パートナーがアプリケーションを市場に投入できるようにサポートしています。また、フィードバックを提供したり、ロードマップに影響を与えて、クラウドやモバイルのエンジニアリング グループと密接に連携することも行っています。