Pesquisar documentos

Antes de começar

Para ingerir documentos de amostra no warehouse da Document AI, consulte o guia de início rápido.

Ao definir os esquemas de documentos e criar os documentos, é importante considerar quais propriedades você quer definir e como elas serão usadas com a pesquisa, se for o caso.

Marque uma propriedade como filtrável se quiser usá-la para incluir ou excluir uma parte dos documentos em uma pesquisa. Por exemplo, você pode criar uma propriedade que represente um filtro "Fornecedor" porque seus usuários querem pesquisar faturas de um fornecedor específico.

Se você quiser criar um histograma (consulte o exemplo mais adiante neste tópico) em uma propriedade, ela precisa ser filtrável.

Marque uma propriedade como pesquisável se ela tiver dados que os usuários vão querer consultar durante uma pesquisa de palavra-chave.

A pesquisa de texto completo é o processo de recuperação de todos os documentos que correspondem às palavras-chave de pesquisa no texto pesquisável. O usuário fornece uma lista de palavras-chave (palavras separadas por um espaço em branco), presumivelmente digitadas em um campo de pesquisa na UI. No Document AI Warehouse, as palavras-chave são processadas e convertidas em uma consulta adequada. Esse processamento remove as palavras irrelevantes ("o", "em" e "um") e radicaliza as palavras restantes. A derivação reduz a palavra a uma versão comum da redação, para que a variação de palavras corresponda. Por exemplo: "trabalho", "trabalhando", "trabalhou".

Quais dados são pesquisados?

  • O plain_text do documento.
  • Se você estiver importando um objeto da Document AI, use o cloud_ai_document.text incorporado.
  • O display_name do documento.
  • Todas as propriedades pesquisáveis.

A consulta oferece suporte parcial à sintaxe de estilo da AIP do Google. Especificamente, a consulta oferece suporte a literais, operadores lógicos, operadores de negação, operadores de comparação e funções.

  • Literais: um valor literal simples (exemplos: "42", "Hugo") é um valor a ser correspondido. Ela pesquisa o texto completo do documento e as propriedades pesquisáveis.
  • Operadores lógicos: "AND", "and", "OR" e "or" são operadores lógicos binários (exemplo: "engineer OR developer").
  • Operadores de negação: "NOT" e "!" são operadores de negação (exemplo: "NOT software").
  • Operadores de comparação: compatíveis com os operadores de comparação binária =, !=, <, >, <= e >= para string, numérico, enum e booleano. Também há suporte para o operador ~~ para strings. Ele oferece funcionalidade de pesquisa semântica analisando, derivando e expandindo sinônimos em relação à consulta de entrada.

    Para especificar uma propriedade na consulta, a expressão do lado esquerdo na comparação precisa ser o ID da propriedade, incluindo o pai. O lado direito precisa ser literal. Por exemplo: \"projects/123/locations/us\".property_a < 1 corresponde a resultados cujo property_a é menor que 1 no projeto 123 e no local us. Os literais e a expressão de comparação podem ser conectados em uma única consulta (exemplo: software engineer \"projects/123/locations/us\".salary > 100).

  • Funções: as funções compatíveis são LOWER([property_name]) para realizar uma correspondência indiferente a maiúsculas e EMPTY([property_name]) para filtrar a existência de uma chave.

  • Oferece suporte a expressões aninhadas conectadas usando parênteses e operadores lógicos. O operador lógico padrão é AND se não houver operadores entre as expressões.

A consulta pode ser usada com outros filtros, como time_filters e folder_name_filter. Eles são conectados ao operador AND internamente.

As consultas de pesquisa podem ser filtradas por outros parâmetros, como property, time, schema, folder e creator.

Chamada para uma solicitação de pesquisa

Para chamar o serviço de pesquisa, use uma solicitação de pesquisa, que é definida da seguinte maneira:

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

O campo parent precisa ser preenchido com o formato:

/projects/PROJECT_ID/locations/LOCATION

Resposta a uma solicitação de pesquisa

A resposta da pesquisa é definida da seguinte maneira:

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

Consulta de documento

O campo document_query é definido da seguinte maneira:

{
  "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)
  }
}

O campo query é para as palavras da consulta de pesquisa do usuário solicitante. Normalmente, eles vêm do campo de pesquisa na UI.

Filtros

O Document AI Warehouse oferece vários filtros.

Filtro de período do documento

O filtro de hora de criação e atualização é exatamente o que você espera: ele encontra documentos que correspondem às palavras-chave em um período especificado.

Um objeto TimeFilter é usado para especificar o período e é definido da seguinte maneira:

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

No campo time_field, você especifica se o período especificado em time_range é para o horário de criação ou de última atualização do documento.

O campo time_range especifica o período como um Interval. Um Interval é definido como:

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

Filtro de criador de conteúdo

Para pesquisar documentos criados por um ou mais usuários específicos, use o filtro de criador. Exemplo:

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

Filtro de propriedade

Com o filtro de propriedade, é possível especificar filtros em qualquer uma das propriedades definidas em um esquema, desde que ela tenha sido configurada para ser filtrável.

Por exemplo, usar filtros de propriedade no setor jurídico pode filtrar uma propriedade chamada COURT para pesquisar apenas documentos de um tribunal específico.

Os filtros de propriedade usam um objeto PropertyFilter. É possível ter mais de um filtro de propriedade. Quando você usa vários filtros de propriedade, eles são combinados usando o operador OR. Um filtro de propriedade é definido da seguinte maneira:

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

As propriedades são definidas em esquemas. Assim, o campo documentSchemaName é onde você especifica o esquema da propriedade usada para filtragem. No campo condition, especifique a lógica desejada. Para exemplos de uso dos campos documentSchemaName e condition, consulte os exemplos anteriores nesta página.

Documento correspondente

Um documento correspondente contém um Document e um snippet (abordado mais adiante). O documento retornado em MatchingDocument não está totalmente preenchido. Ele contém dados mínimos para mostrar uma lista de resultados da pesquisa ao usuário solicitante. Se o documento completo for desejado (por exemplo, se o usuário clicou em um resultado da pesquisa), ele deverá ser recuperado pela API GetDocument.

Os seguintes campos Document são preenchidos: Project number, Document id, Document schema id, Create time, Update time, Display name, Raw document file type, Reference id e Filterable properties.

Um documento correspondente seria assim:

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

Classificação/ordenação

A solicitação de pesquisa permite especificar como você quer que os resultados sejam classificados. Para classificar, use o campo order_by na solicitação de pesquisa. Os valores possíveis para esse campo incluem:

  • relevance desc: relevância decrescente, ou seja, as melhores correspondências ficam na parte de cima.
  • upload_date desc: a data em que o documento foi criado em ordem decrescente (mais recente na parte de cima).
  • upload_date: a data em que o documento foi criado em ordem crescente (o mais antigo no topo).
  • update_date desc: a data da última atualização do documento em ordem decrescente (mais recente no topo).
  • Update_date: a data em que o documento foi atualizado pela última vez em ordem crescente (o mais antigo no topo).

Se você não especificar uma classificação, mas fornecer palavras-chave de pesquisa, a classificação será por relevância em ordem decrescente (as melhores correspondências no topo). Se nem a classificação nem as palavras-chave forem fornecidas, a classificação padrão será por tempo de atualização em ordem decrescente (os documentos mais recentes na parte superior).

Paginação

A paginação é útil para mostrar uma página de dados ao usuário final. Aqui, você pode especificar o tamanho da página e receber uma contagem total do tamanho do resultado para mostrar ao usuário (por exemplo, "Mostrando 50 documentos de 300").

Defina o campo page_size com o número de resultados que você quer receber com a solicitação de pesquisa. Isso pode corresponder aos requisitos de tamanho da exibição dos resultados da pesquisa UI.

Há dois mecanismos: o de ajuste e o token de página.

Um deslocamento é o índice na lista de documentos retornáveis que você quer receber. Por exemplo, um valor de 5 significa que você quer o sexto documento em diante. Presumivelmente, você incrementaria o deslocamento pelo tamanho da página para a próxima página de resultados.

Como alternativa, use um token de página e não se preocupe em calcular o próximo ajuste. Depois de fazer sua primeira solicitação de pesquisa, você recebe uma resposta que contém o campo next_page_token. Se esse campo estiver vazio, não haverá mais resultados. Se o campo não estiver vazio, use esse token na próxima solicitação de pesquisa definindo o campo page_token.

Algumas interfaces mostram a contagem de documentos encontrados pela pesquisa. Por exemplo, you are viewing 10 documents of 120 Para receber uma contagem de documentos, defina o campo require_total_size boolean da solicitação como True. Dica: require_total_size=True tem uma penalidade de desempenho. Defina isso na primeira consulta de página e, em seguida, defina como false em todas as solicitações subsequentes, mantendo a contagem total em uma variável local.

Exemplos de código

Python

Para mais informações, consulte a documentação de referência da API Python da Document AI Warehouse.

Para autenticar no Document AI Warehouse, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.


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

Para mais informações, consulte a documentação de referência da API Java da Document AI Warehouse.

Para autenticar no Document AI Warehouse, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

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);
    } 
  }
}

Próximas etapas