ドキュメントを検索する

始める前に

サンプル ドキュメントを Document AI Warehouse に取り込むには、クイック スタートガイドをご覧ください。

ドキュメント スキーマを定義してドキュメントを作成する際は、定義するプロパティと、それらが検索でどのように使用されるかを検討することが重要です。

検索でドキュメントの一部を含めるか除外するためにプロパティを使用する場合は、そのプロパティをフィルタ可能としてマークします。たとえば、ユーザーが特定のベンダーの請求書を検索できるように、「ベンダー」を表すプロパティをフィルタ可能にすることができます。

プロパティでヒストグラムを作成する場合(このトピックの後半の例を参照)、プロパティはフィルタ可能である必要があります。

キーワード検索でユーザーがクエリを実行するデータがある場合は、プロパティを検索可能としてマークします。

全文検索とは、検索可能なテキスト内の検索キーワードに一致するすべてのドキュメントを取得するプロセスです。ユーザーは、キーワードのリスト(空白で区切られた単語)を入力します。これは、UI の検索フィールドに入力されたものと想定されます。Document AI ウェアハウスでは、キーワードが処理され、適切なクエリに変換されます。このような処理では、ストップワード(「the」、「in」、「an」など)が削除され、残りの単語が語幹に変換されます。ステミングは、単語を共通の表現に変換し、単語のバリエーションを一致させます。例: 「work」、「working」、「worked」。

検索対象となるデータ

  • ドキュメントの plain_text
  • Document AI オブジェクトをインポートする場合は、埋め込み cloud_ai_document.text を使用します。
  • ドキュメントの display_name。
  • 検索可能なすべてのプロパティ。

このクエリは、Google AIP スタイル構文を部分的にサポートしています。具体的には、クエリはリテラル、論理演算子、否定演算子、比較演算子、関数をサポートしています。

  • リテラル: リテラル値(例: 「42」、「Hugo」)は、照合する値です。ドキュメントの全文と検索可能なプロパティを検索します。
  • 論理演算子: 「AND」、「and」、「OR」、「or」は 2 項論理演算子です(例: 「engineer OR developer」)。
  • 否定演算子: 「NOT」と「!」は否定演算子です(例: 「NOT software」)。
  • 比較演算子: 文字列、数値、列挙型、ブール値に対して、バイナリ比較演算子 =!=<><=>= をサポートします。文字列の like 演算子 ~~ もサポートします。入力クエリに対して解析、ステミング、同義語拡張を行うことで、セマンティック検索機能を提供します。

    クエリでプロパティを指定するには、比較の左辺の式が親を含むプロパティ ID である必要があります。右辺はリテラルにする必要があります。たとえば、\"projects/123/locations/us\".property_a < 1 は、プロジェクト 123us ロケーションで property_a が 1 未満の結果と一致します。リテラルと比較式は、1 つのクエリで接続できます(例: software engineer \"projects/123/locations/us\".salary > 100)。

  • 関数: サポートされている関数は、大文字と小文字を区別せずに照合する LOWER([property_name]) と、キーの存在をフィルタする EMPTY([property_name]) です。

  • かっこや論理演算子を使用して接続されたネストされた式をサポートします。式と式の間に演算子がない場合、デフォルトの論理演算子は AND です。

このクエリは、time_filtersfolder_name_filter などの他のフィルタと組み合わせて使用できます。これらは、内部で AND 演算子と接続されています。

検索クエリは、propertytimeschemafoldercreator などの追加パラメータでフィルタできます。

検索リクエストの呼び出し

検索サービスを呼び出すには、次のように定義された検索リクエストを使用する必要があります。

{
  "requestMetadata": {
    object (RequestMetadata)
  },
  "documentQuery": {
    object (DocumentQuery)
  },
  "offset": integer,
  "pageSize": integer,
  "pageToken": string,
  "orderBy": string,
  "histogramQueries": [
    {
      object (HistogramQuery)
    }
  ],
  "requireTotalSize": boolean,
  "totalResultSize": enum (TotalResultSize),
  "qaSizeLimit": integer
}

parent フィールドは、次の形式で入力する必要があります。

/projects/PROJECT_ID/locations/LOCATION

検索リクエストへのレスポンス

検索レスポンスは次のように定義されます。

{
  "matchingDocuments": [
    {
      object (MatchingDocument)
    }
  ],
  "nextPageToken": string,
  "totalSize": integer,
  "metadata": {
    object (ResponseMetadata)
  },
  "histogramQueryResults": [
    {
      object (HistogramQueryResult)
    }
  ]
}

ドキュメント クエリ

document_query フィールドは次のように定義されます。

{
  "query": string,
  "isNlQuery": boolean,
  "customPropertyFilter": string,
  "timeFilters": [
    {
      object (TimeFilter)
    }
  ],
  "documentSchemaNames": [
    string
  ],
  "propertyFilter": [
    {
      object (PropertyFilter)
    }
  ],
  "fileTypeFilter": {
    object (FileTypeFilter)
  },
  "folderNameFilter": string,
  "queryContext": [
    string
  ],
  "documentCreatorFilter": [
    string
  ],
  "customWeightsMetadata": {
    object (CustomWeightsMetadata)
  }
}

query フィールドは、リクエストしているユーザーの検索クエリの単語用です。通常、これらは UI の検索フィールドから取得されます。

フィルタ

Document AI ウェアハウスには、さまざまなフィルタが用意されています。

ドキュメントの期間フィルタ

作成時間と更新時間のフィルタは、指定した期間内にキーワードに一致するドキュメントを検索します。

TimeFilter オブジェクトは、時間範囲を指定するために使用され、次のように定義されます。

{
  "timeRange": {
    object (Interval)
  },
  "timeField": enum (TimeField)
}

time_field フィールドでは、time_range で指定された期間がドキュメントの作成時間とドキュメントの最終更新時間のどちらを対象とするかを指定します。

time_range フィールドは、期間を Interval として指定します。Interval は次のように定義されます。

{
  "startTime": string,
  "endTime": string
}

クリエイター フィルタ

特定のユーザーが作成したドキュメントを検索するには、作成者フィルタを使用します。次に例を示します。

  {
    document_query {
      query: "videogames director",
      documentCreatorFilter: [
        "diane@some_company.com",
        "frank@some_company.com",
      ],
    },
  }

プロパティ フィルタ

プロパティ フィルタを使用すると、スキーマで指定したプロパティのうち、フィルタ可能に構成されているプロパティに対してフィルタを指定できます。

たとえば、法律業界でプロパティ フィルタを使用すると、COURT というプロパティでフィルタして、特定の裁判所のドキュメントのみを検索できます。

プロパティ フィルタでは PropertyFilter オブジェクトを使用します。プロパティ フィルタは複数指定できます。複数のプロパティ フィルタを使用すると、それらは OR 演算子を使用して結合されます。プロパティ フィルタは次のように定義されます。

  {
    "documentSchemaName": string,
    "condition": string
  }

プロパティはスキーマで定義されます。したがって、documentSchemaName フィールドは、フィルタリングに使用するプロパティのスキーマを指定する場所です。condition フィールドで、目的のロジックを指定します。documentSchemaName フィールドと condition フィールドの使用例については、このページの前の例をご覧ください。

一致するドキュメント

一致するドキュメントには、Document とスニペット(後述)が含まれます。MatchingDocument で返されたドキュメントが、完全に記入されたドキュメントではありません。リクエストしたユーザーに検索結果リストを表示するための最小限のデータが含まれています。ドキュメント全体が必要な場合(ユーザーが検索結果をクリックした場合など)、GetDocument API を介してドキュメント全体を取得する必要があります。

Document フィールドの Project numberDocument idDocument schema idCreate timeUpdate timeDisplay nameRaw document file typeReference idFilterable properties が入力されます。

一致するドキュメントは次のようになります。

{
  "document": {
    object (Document)
  },
  "searchTextSnippet": string,
  "qaResult": {
    object (QAResult)
  }
}
の完全なリファレンス

ランキング/並べ替え

検索リクエストでは、結果の並べ替え方法を指定できます。並べ替えるには、検索リクエストで order_by フィールドを使用します。このフィールドで指定可能な値は次のとおりです。

  • relevance desc - 関連性の降順(最も一致するものが上位に表示されます)。
  • upload_date desc - ドキュメントが作成された日付(降順、新しい順)。
  • upload_date - ドキュメントが作成された日付(昇順、古い順)。
  • update_date desc - ドキュメントが最後に更新された日付の降順(最新のものが一番上)。
  • Update_date - ドキュメントが最後に更新された日付(昇順、最も古いものが上)。

並べ替えを指定せずに検索キーワードを指定すると、関連性の降順(最も関連性の高いものが上位)で並べ替えられます。並べ替えもキーワードも指定されていない場合、デフォルトの並べ替えは更新時間の降順(最新のドキュメントが上)になります。

ページネーション

ページネーションは、エンドユーザーに 1 ページ分のデータを表示するのに便利です。ここでページサイズを指定し、結果の合計サイズを取得してユーザーに表示できます(例: 「300 件中 50 件のドキュメントを表示しています」)。

page_size フィールドに、検索リクエストで受け取る結果の数を設定します。これは、UI 検索結果の表示サイズの要件に対応している可能性があります。

オフセットとページトークンの 2 つのメカニズムがあります。

オフセットは、返されるドキュメントのリストのインデックスです。たとえば、オフセットが 5 の場合、6 番目のドキュメント以降を取得します。おそらく、結果の次のページではオフセットをページサイズだけ増やすことになります。

または、ページトークンを使用することもできます。この場合、次のオフセットを計算する必要はありません。最初の検索リクエストを行うと、next_page_token フィールドを含む検索レスポンスが返されます。このフィールドが空の場合、それ以上の結果はありません。フィールドが空でない場合は、page_token フィールドを設定して、次の検索リクエストでこのトークンを使用します。

一部の UI には、検索で見つかったドキュメントの数が表示されます。(例: you are viewing 10 documents of 120)。返されるドキュメントの数を取得するには、リクエストの require_total_size boolean フィールドを True に設定します。ヒント: require_total_size=True にはパフォーマンスのペナルティがあります。最初のページクエリでこれを設定し、後続のすべてのリクエストで false に設定して、合計数をローカル変数に保持します。

コードサンプル

Python

詳細については、Document AI Warehouse Python API リファレンス ドキュメントをご覧ください。

Document AI Warehouse に対する認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証の設定をご覧ください。


from google.cloud import contentwarehouse

# TODO(developer): Uncomment these variables before running the sample.
# project_number = 'YOUR_PROJECT_NUMBER'
# location = 'YOUR_PROJECT_LOCATION' # Format is 'us' or 'eu'
# document_query_text = 'YOUR_DOCUMENT_QUERY'
# user_id = 'user:YOUR_SERVICE_ACCOUNT_ID' # Format is "user:xxxx@example.com"


def search_documents_sample(
    project_number: str, location: str, document_query_text: str, user_id: str
) -> None:
    # Create a client
    client = contentwarehouse.DocumentServiceClient()

    # The full resource name of the location, e.g.:
    # projects/{project_number}/locations/{location}
    parent = client.common_location_path(project=project_number, location=location)

    # File Type Filter
    # Options: DOCUMENT, FOLDER
    file_type_filter = contentwarehouse.FileTypeFilter(
        file_type=contentwarehouse.FileTypeFilter.FileType.DOCUMENT
    )

    # Document Text Query
    document_query = contentwarehouse.DocumentQuery(
        query=document_query_text,
        file_type_filter=file_type_filter,
    )

    # Histogram Query
    histogram_query = contentwarehouse.HistogramQuery(
        histogram_query='count("DocumentSchemaId")'
    )

    request_metadata = contentwarehouse.RequestMetadata(
        user_info=contentwarehouse.UserInfo(id=user_id)
    )

    # Define request
    request = contentwarehouse.SearchDocumentsRequest(
        parent=parent,
        request_metadata=request_metadata,
        document_query=document_query,
        histogram_queries=[histogram_query],
    )

    # Make the request
    response = client.search_documents(request=request)

    # Print search results
    for matching_document in response.matching_documents:
        document = matching_document.document
        # Display name - schema display name.
        # Name.
        # Create date.
        # Snippet - keywords are highlighted with <b> & </b>.
        print(
            f"{document.display_name} - {document.document_schema_name}\n"
            f"{document.name}\n"
            f"{document.create_time}\n"
            f"{matching_document.search_text_snippet}\n"
        )

    # Print histogram
    for histogram_query_result in response.histogram_query_results:
        print(
            f"Histogram Query: {histogram_query_result.histogram_query}\n"
            f"| {'Schema':<70} | {'Count':<15} |"
        )
        for key, value in histogram_query_result.histogram.items():
            print(f"| {key:<70} | {value:<15} |")

Java

詳細については、Document AI Warehouse Java API リファレンス ドキュメントをご覧ください。

Document AI Warehouse に対する認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証の設定をご覧ください。

import com.google.cloud.contentwarehouse.v1.DocumentQuery;
import com.google.cloud.contentwarehouse.v1.DocumentServiceClient;
import com.google.cloud.contentwarehouse.v1.DocumentServiceClient.SearchDocumentsPagedResponse;
import com.google.cloud.contentwarehouse.v1.DocumentServiceSettings;
import com.google.cloud.contentwarehouse.v1.FileTypeFilter;
import com.google.cloud.contentwarehouse.v1.FileTypeFilter.FileType;
import com.google.cloud.contentwarehouse.v1.LocationName;
import com.google.cloud.contentwarehouse.v1.RequestMetadata;
import com.google.cloud.contentwarehouse.v1.SearchDocumentsRequest;
import com.google.cloud.contentwarehouse.v1.SearchDocumentsResponse.MatchingDocument;
import com.google.cloud.contentwarehouse.v1.UserInfo;
import com.google.cloud.resourcemanager.v3.Project;
import com.google.cloud.resourcemanager.v3.ProjectName;
import com.google.cloud.resourcemanager.v3.ProjectsClient;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

public class SearchDocuments {
  public static void main(String[] args) throws IOException, 
        InterruptedException, ExecutionException, TimeoutException { 
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "your-project-id";
    String location = "your-region"; // Format is "us" or "eu".
    String documentQuery = "your-document-query";
    String userId = "your-user-id"; // Format is user:<user-id>

    searchDocuments(projectId, location, documentQuery, userId);
  }

  // Searches all documents for a given Document Query
  public static void searchDocuments(String projectId, String location,
        String documentQuery, String userId) throws IOException, InterruptedException,
          ExecutionException, TimeoutException { 
    String projectNumber = getProjectNumber(projectId);

    String endpoint = "contentwarehouse.googleapis.com:443";
    if (!"us".equals(location)) {
      endpoint = String.format("%s-%s", location, endpoint);
    }

    DocumentServiceSettings documentServiceSettings = 
             DocumentServiceSettings.newBuilder().setEndpoint(endpoint)
             .build(); 

    /*
     * Create the Document Service Client 
     * Initialize client that will be used to send requests. 
     * This client only needs to be created once, and can be reused for multiple requests. 
     */
    try (DocumentServiceClient documentServiceClient = 
            DocumentServiceClient.create(documentServiceSettings)) {  

      /*
       * The full resource name of the location, e.g.:
       * projects/{project_number}/locations/{location} 
       */
      String parent = LocationName.format(projectNumber, location);

      // Define RequestMetadata object for context of the user making the API call
      RequestMetadata requestMetadata = RequestMetadata.newBuilder()
          .setUserInfo(
          UserInfo.newBuilder()
            .setId(userId)
            .build())
            .build();

      // Set file type for filter to 'DOCUMENT'
      FileType documentFileType = FileType.DOCUMENT;

      // Create a file type filter for documents 
      FileTypeFilter fileTypeFilter = FileTypeFilter.newBuilder()
          .setFileType(documentFileType)
          .build();

      // Create document query to search all documents for text given at input
      DocumentQuery query = DocumentQuery.newBuilder()
          .setQuery(documentQuery)
          .setFileTypeFilter(fileTypeFilter)
          .build();

      /*
       * Create the request to search all documents for specified query. 
       * Please note the offset in this request is to only return the specified number of results 
       * to avoid hitting the API quota. 
       */
      SearchDocumentsRequest searchDocumentsRequest = SearchDocumentsRequest.newBuilder()
          .setParent(parent)
          .setRequestMetadata(requestMetadata)
          .setOffset(5)
          .setDocumentQuery(query)
          .build();

      // Make the call to search documents with document service client and store the response
      SearchDocumentsPagedResponse searchDocumentsPagedResponse = 
          documentServiceClient.searchDocuments(searchDocumentsRequest);

      // Iterate through response and print search results for documents matching the search query
      for (MatchingDocument matchingDocument :
          searchDocumentsPagedResponse.iterateAll()) {
        System.out.println(
            "Display Name: " + matchingDocument.getDocument().getDisplayName()
            + "Document Name: " + matchingDocument.getDocument().getName()
            + "Document Creation Time: " + matchingDocument.getDocument().getCreateTime().toString()
            + "Search Text Snippet: " + matchingDocument.getSearchTextSnippet());
      }
    }
  }

  private static String getProjectNumber(String projectId) throws IOException { 
    /*
     * Initialize client that will be used to send requests. 
     * This client only needs to be created once, and can be reused for multiple requests.
     */
    try (ProjectsClient projectsClient = ProjectsClient.create()) { 
      ProjectName projectName = ProjectName.of(projectId); 
      Project project = projectsClient.getProject(projectName);
      String projectNumber = project.getName(); // Format returned is projects/xxxxxx
      return projectNumber.substring(projectNumber.lastIndexOf("/") + 1);
    } 
  }
}

次のステップ