문서 검색

시작하기 전에

샘플 문서를 Document AI Warehouse에 수집하려면 빠른 시작 가이드를 참고하세요.

문서 스키마를 정의하고 문서를 만들 때 정의할 속성과 검색에 사용할 방법을 고려하는 것이 중요합니다.

검색에서 일부 문서를 포함하거나 제외하는 데 속성을 사용하려면 속성을 필터링 가능으로 표시합니다. 예를 들어 사용자가 특정 공급업체의 인보이스를 검색하기를 원하므로 '공급업체'를 나타내는 속성을 필터링 가능하게 만들 수 있습니다.

속성에 대한 히스토그램을 구성하려면 (이 주제의 뒷부분에 나오는 예 참고) 속성을 필터링할 수 있어야 합니다.

사용자가 키워드 검색 중에 쿼리할 데이터가 있는 경우 속성을 검색 가능으로 표시합니다.

전체 텍스트 검색은 검색 가능한 텍스트에서 검색 키워드와 일치하는 모든 문서를 검색하는 프로세스입니다. 사용자가 키워드 목록 (공백으로 구분된 단어)을 제공합니다. 이는 UI의 검색 필드에 입력된 것으로 추정됩니다. Document AI Warehouse에서 키워드가 처리되고 적절한 쿼리로 변환됩니다. 이러한 처리에서는 불용어 ('the', 'in', 'an')를 삭제하고 나머지 단어의 어간을 추출합니다. 어간 추출은 단어를 단어 변형이 일치하도록 단어의 일반적인 버전으로 줄입니다. 예: 'work', 'working', 'worked'

어떤 데이터가 검색되나요?

  • 문서의 plain_text입니다.
  • Document AI 객체를 가져오는 경우 내장된 cloud_ai_document.text를 사용합니다.
  • 문서의 display_name입니다.
  • 검색 가능한 모든 속성입니다.

쿼리는 Google AIP 스타일 구문을 부분적으로 지원합니다. 특히 쿼리는 리터럴, 논리 연산자, 부정 연산자, 비교 연산자, 함수를 지원합니다.

  • 리터럴: 일반 리터럴 값('42', 'Hugo' 등)은 일치시킬 값입니다. 문서의 전체 텍스트와 검색 가능한 속성을 검색합니다.
  • 논리 연산자: 'AND', 'and', 'OR', 'or'는 이진 논리 연산자입니다 (예: 'engineer OR developer').
  • 부정 연산자: 'NOT' 및 '!'은 부정 연산자입니다 (예: 'NOT software').
  • 비교 연산자: 문자열, 숫자, enum, 불리언에 대해 이진 비교 연산자 =, !=, <, >, <=, >=를 지원합니다. 문자열의 like 연산자 ~~도 지원합니다. 입력된 질문을 파싱하고, 어간을 추출하고, 동의어를 확장하여 의미 검색 기능을 제공합니다.

    쿼리에서 속성을 지정하려면 비교의 왼쪽 표현식이 상위 항목을 포함한 속성 ID여야 합니다. 오른쪽은 리터럴이어야 합니다. 예를 들어 \"projects/123/locations/us\".property_a < 1123 프로젝트 및 us 위치에서 property_a이 1 미만인 결과와 일치합니다. 리터럴과 비교 표현식은 단일 쿼리에서 연결할 수 있습니다 (예: software engineer \"projects/123/locations/us\".salary > 100).

  • 함수: 지원되는 함수는 대소문자를 구분하지 않는 일치를 수행하는 LOWER([property_name])와 키의 존재를 기준으로 필터링하는 EMPTY([property_name])입니다.

  • 괄호와 논리 연산자를 사용하여 연결된 중첩된 표현식을 지원합니다. 표현식 사이에 연산자가 없으면 기본 논리 연산자는 AND입니다.

이 쿼리는 time_filters, folder_name_filter와 같은 다른 필터와 함께 사용할 수 있습니다. 내부적으로 AND 연산자와 연결됩니다.

검색어는 property, time, schema, folder, creator과 같은 추가 매개변수로 필터링할 수 있습니다.

검색 요청 호출

검색 서비스를 호출하려면 다음과 같이 정의된 검색 요청을 사용해야 합니다.

{
  "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 Warehouse는 다양한 필터를 제공합니다.

문서 시간 필터

생성 및 업데이트 시간 필터는 예상한 대로 작동합니다. 지정된 기간 내에 키워드와 일치하는 문서를 찾습니다.

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 필드에서 원하는 로직을 지정합니다. documentSchemaNamecondition 필드 사용 예는 이 페이지의 앞부분에 있는 예시를 참고하세요.

일치하는 문서

일치하는 문서에는 Document과 스니펫이 포함됩니다 (나중에 설명). MatchingDocument에서 반환된 문서가 완전히 작성된 문서가 아닙니다. 요청 사용자에게 검색 결과 목록을 표시하기 위한 최소한의 데이터가 포함되어 있습니다. 전체 문서가 필요한 경우 (예: 사용자가 검색 결과를 클릭한 경우) GetDocument API를 통해 전체 문서를 가져와야 합니다.

Project number, Document id, Document schema id, Create time, Update time, Display name, Raw document file type, Reference id, Filterable properties과 같은 Document 필드가 채워집니다.

일치하는 문서는 다음과 같습니다.

{
  "document": {
    object (Document)
  },
  "searchTextSnippet": string,
  "qaResult": {
    object (QAResult)
  }
}

순위/정렬

검색 요청을 사용하면 결과를 정렬할 방법을 지정할 수 있습니다. 정렬하려면 검색 요청에서 order_by 필드를 사용합니다. 이 필드에 가능한 값은 다음과 같습니다.

  • relevance desc - 관련성 내림차순, 즉 가장 적합한 항목이 상단에 표시됩니다.
  • upload_date desc - 문서가 생성된 날짜를 내림차순으로 정렬합니다 (최신순).
  • upload_date - 문서가 생성된 날짜를 오름차순으로 정렬합니다 (오래된 순).
  • update_date desc - 문서가 마지막으로 업데이트된 날짜를 내림차순으로 정렬합니다 (최신순).
  • Update_date - 문서가 마지막으로 업데이트된 날짜를 오름차순으로 정렬합니다 (오래된 문서가 맨 위에 있음).

정렬을 지정하지 않았지만 검색 키워드를 제공한 경우 관련성 내림차순 (가장 일치하는 항목이 상단에 표시됨)으로 정렬됩니다. 정렬도 키워드도 제공되지 않으면 기본 정렬은 업데이트 시간 내림차순 (최신 문서가 맨 위에 있음)입니다.

페이지로 나누기

페이지로 나누기는 최종 사용자에게 페이지에 해당하는 데이터를 표시하는 데 유용합니다. 여기에서 페이지 크기를 지정하고 사용자에게 표시할 결과 크기의 총 개수를 가져올 수 있습니다 (예: '300개 중 50개 문서 표시').

page_size 필드를 검색 요청과 함께 수신하려는 원하는 결과 수로 설정합니다. 이는 UI 검색 결과 표시 크기의 요구사항에 해당할 수 있습니다.

오프셋과 페이지 토큰이라는 두 가지 메커니즘이 있습니다.

오프셋은 반환할 수 있는 문서 목록에서 반환하려는 문서의 색인입니다. 예를 들어 오프셋이 5이면 여섯 번째 문서부터 원하는 것입니다. 다음 결과 페이지의 경우 페이지 크기만큼 오프셋을 늘려야 합니다.

또는 페이지 토큰을 사용하면 다음 오프셋을 계산하지 않아도 됩니다. 첫 번째 검색 요청을 하면 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);
    } 
  }
}

다음 단계