Documentos e índices

Na API Search, há um modelo para a indexação de documentos contendo dados estruturados. É possível pesquisar um índice, organizar e apresentar os resultados da pesquisa. A API é compatível com correspondência de texto completa em campos de string. Documentos e índices são salvos em um armazenamento permanente separado e otimizado para operações de pesquisa. Qualquer número de documentos pode ser indexado na API Search. O App Engine Datastore pode ser mais apropriado para aplicativos que precisam recuperar grupos de resultados muito grandes.

Visão geral

A API Search é baseada em quatro conceitos principais: documentos, índices, consultas e resultados.

Documentos

Um documento é um objeto com um código exclusivo e uma lista de campos contendo dados do usuário. Cada campo tem um nome e um tipo. Existem vários tipos de campos, identificados pelos tipos de valores que contêm:

  • Campo atômico: uma string de caracteres indivisível.
  • Campo de texto: uma string de texto simples que pode ser pesquisada por palavra.
  • Campo HTML: uma string de caracteres que contém tags de marcação HTML. Somente o texto fora das tags de marcação pode ser pesquisado.
  • Campo numérico: um número de ponto flutuante.
  • Campo de data: um objeto de data.
  • Campo de Geopoint: um objeto de dados com coordenadas de latitude e longitude.

O tamanho máximo de um documento é 1 MB.

Índices

Um índice armazena documentos para recuperação. É possível recuperar um documento pelo código, uma série de documentos com códigos consecutivos ou todos os documentos em um índice. Também é possível pesquisar um índice para recuperar documentos que atendam a determinados critérios nos campos e valores correspondentes, especificados como uma string de consulta. Também é possível gerenciar grupos de documentos colocando-os em índices separados.

Não há limite para a quantidade de documentos em um índice ou o número de índices em uso. O tamanho total de todos os documentos em um índice é limitado a 10 GB por padrão, mas pode ser aumentado para até 200 GB por meio de uma solicitação na página do App Engine Search no Console do Google Cloud Platform.

Consultas

A fim de pesquisar um índice, você constrói uma consulta contendo uma string de consulta e talvez opções adicionais. Na string de consulta, são especificadas as condições para os valores de um ou mais campos do documento. Ao pesquisar um índice, você recupera apenas os documentos com os campos que atendem à consulta.

A consulta mais simples, às vezes chamada de "pesquisa global", é uma string que contém apenas valores de campo. Nessa pesquisa, uma string é usada para localizar documentos que contenham as palavras "rose" e "water":

A string a seguir é usada para pesquisar documentos com campos de data que contenham "4 de julho de 1776" ou campos de texto que incluam a string "1776-07-04":

Uma string de consulta também pode ser mais específica. Ela pode conter um ou mais termos, cada um nomeando um campo e uma restrição sobre o valor dele. A forma exata de um termo depende do tipo de campo. Por exemplo, supondo que exista um campo de texto chamado "Produto" e um campo numérico chamado "Preço", aqui está uma string de consulta com dois termos:

Java 8

// search for documents with pianos that cost less than $5000
index.search("product = piano AND price < 5000");

Java 7

// search for documents with pianos that cost less than $5000
index.search("product = piano AND price < 5000");

As opções de consulta, como o nome indica, não são necessárias. Com elas, é possível usar diversos recursos:

  • Controlar quantos documentos são retornados nos resultados da pesquisa.
  • Especificar quais campos de documento incluir nos resultados. O padrão é incluir todos os campos do documento original. É possível definir que os resultados incluam apenas um subgrupo de campos, sem que o documento original seja afetado.
  • Classificar os resultados.
  • Criar "campos calculados" para documentos usando FieldExpressions e campos de texto abreviados usando snippets.
  • Dar suporte à paginação por meio dos resultados da pesquisa ao retornar apenas uma parte dos documentos correspondentes em cada consulta, usando deslocamentos e cursores.

Resultados da pesquisa

Uma chamada para search() só pode retornar um número limitado de documentos correspondentes. A pesquisa pode encontrar mais documentos do que podem ser retornados em uma única chamada. Cada chamada de pesquisa retorna uma instância da classe Results, que contém informações sobre quantos documentos foram encontrados e quantos foram retornados, além da lista de documentos retornados. Repita a mesma pesquisa usando cursores ou deslocamentos para recuperar o conjunto completo de documentos correspondentes.

Material de treinamento adicional

Além desta documentação, leia a aula de treinamento em duas partes sobre a API Search na Google Developer's Academy. A API Python é usada na aula, mas talvez você considere útil discutir sobre os conceitos de pesquisa.

Documentos e campos

A classe Document representa documentos. Cada documento tem um identificador e uma lista de campos.

Identificador do documento

Todo documento em um índice precisa ter um identificador exclusivo ou doc_id. O identificador pode ser usado para recuperar um documento de um índice sem realizar uma pesquisa. Por padrão, a API Search gera automaticamente um doc_id quando um documento é criado. Você também pode especificar o doc_id ao criar um documento. Um doc_id precisa conter apenas caracteres ASCII visíveis e imprimíveis (códigos ASCII 33 a 126 inclusos) e não pode ter mais de 500 caracteres. Um identificador de documento não pode começar com um ponto de exclamação ('!"), e não pode começar e terminar com sublinhados duplos ("__").

É conveniente criar identificadores de documentos exclusivos, legíveis e significativos, mas não é possível incluir o doc_id em uma pesquisa. Considere esta situação: você tem um índice com documentos que representam peças, usando o número de série da peça como doc_id. Será muito eficiente recuperar o documento de uma peça única, mas será impossível pesquisar por uma faixa de vários números de série junto com outros valores de campo, como data de compra. Armazenar o número de série em um campo atômico resolve o problema.

Campos do documento

Um documento contém campos com um nome, um tipo e um único valor desse tipo. Dois ou mais campos podem ter o mesmo nome, mas tipos diferentes. Por exemplo, é possível definir dois campos com o nome "idade": um com tipo texto (o valor "vinte e dois") e outro com o tipo número (valor 22).

Nomes de campos

Os nomes dos campos diferenciam maiúsculas de minúsculas e só podem conter caracteres ASCII. Eles precisam começar com uma letra e podem conter letras, números ou sublinhados. O nome do campo não pode ter mais de 500 caracteres.

Campos com vários valores

Um campo pode conter apenas um valor, que precisa corresponder ao tipo do campo. Os nomes dos campos não precisam ser exclusivos. Um documento pode ter vários campos com o mesmo nome e o mesmo tipo. Essa é uma maneira de representar um campo com vários valores. No entanto, os campos de data e número com o mesmo nome não podem ser repetidos. Um documento também pode conter vários campos com o mesmo nome e diferentes tipos.

Tipos de campos

Há três tipos de campos que armazenam strings de caracteres java.lang.String. Eles são conhecidos coletivamente como campos de string:

  • Campo de texto: uma string com comprimento máximo de 1024**2 caracteres.
  • Campo HTML: uma string formatada em HTML com comprimento máximo de 1024**2 caracteres.
  • Campo atômico: uma string com comprimento máximo de 500 caracteres.

Existem também três tipos de campos para armazenar dados não-textuais:

  • Campo numérico: uma pontuação flutuante de dupla precisão entre -2.147.483.647 e 2.147.483.647.
  • Campo de data: um java.util.Date.
  • Campo de Geopoint: um ponto na Terra descrito em coordenadas de latitude e longitude.

Os tipos de campo são especificados com os enums Field.FieldType TEXT, HTML, ATOM, NUMBER, DATE e GEO_POINT.

Tratamento especial de campos de string e data

Quando um documento com campos de data, texto ou HTML é adicionado a um índice, ocorre um tratamento especial. É útil entender o que está acontecendo nos bastidores para usar a API Search de forma eficaz.

Tokenização de campos de string

Quando um campo de HTML ou texto é indexado, o conteúdo é tokenizado. A string é dividida em tokens onde houver espaços em branco ou caracteres especiais, como sinais de pontuação, barra invertida etc. O índice inclui uma entrada para cada token. Isso permite pesquisar palavras-chave e frases que compõem apenas parte do valor de um campo. Por exemplo, em uma pesquisa por "noite", um documento com um campo de texto contendo a string "era uma noite escura e tormentosa" será retornado. Em uma pesquisa por "tempo", um documento com um campo de texto contendo a string "isto é um sistema em tempo real" será retornado.

Em campos HTML, o texto dentro das tags de marcação não é tokenizado. Portanto, um documento com um campo HTML que contém it was a <strong>dark</strong> night será retornado para "night", mas não para "strong". Para pesquisar um texto de marcação, armazene-o em um campo de texto.

Os campos atômicos não são tokenizados. Um documento com um campo atômico que contenha o valor "tempo ruim" só será retornado em uma pesquisa para toda a string "tempo ruim". Ele não será retornado em uma pesquisa pelos termos "tempo" ou "ruim" separados.

Regras de tokenização
  • As palavras não são divididas em tokens pelos caracteres e comercial (&) e sublinhado (_).

  • As palavras são sempre divididas em tokens pelos seguintes caracteres de espaço em branco: espaço, retorno de carro, avanço de linha, tabulação horizontal, tabulação vertical, avanço de formulário e NULL.

  • As palavras são divididas em tokens pelos caracteres a seguir, que são tratados como sinais de pontuação:

    !"%()
    *,-|/
    []]^`
    :=>?@
    {}~$
  • Geralmente, as palavras são divididas em tokens pelos caracteres a seguir, mas eles podem ser manipulados de forma diferente dependendo do contexto em que aparecem:

    Caractere Regra
    < Em um campo HTML, o sinal "menor que" indica o início de uma tag HTML, que é ignorada.
    + Uma string de um ou mais sinais de adição é tratada como parte da palavra se aparecer no final dela (C++).
    # O sinal de jogo da velha é tratado como parte da palavra se for precedido de a, b, c, d, e, f, g, j ou x (a# - g# são notas musicais, j# e x# são linguagens de programação e c# é as duas coisas). Se um termo é precedido por '#' (#google), ele é tratado como uma hashtag, e o sinal de jogo da velha se torna parte da palavra.
    ' O apóstrofo é uma letra se preceder a letra "s" seguida de uma quebra de palavra, como em "John's hat".
    . Se um ponto decimal aparecer entre dígitos, ele faz parte de um número, ou seja, é o separador de decimais. Ele também pode ser parte de uma palavra se usado em uma sigla (A.B.C).
    - O traço é parte de uma palavra se usado em uma sigla (I-B-M).
  • As palavras são divididas em tokens pelos outros caracteres de 7 bits que não sejam letras e dígitos ("A-Z", "a-z", "0-9"). Eles são tratados como sinais de pontuação.

  • Todo o resto é analisado como um caractere UTF-8.

Siglas

A tokenização tem regras especiais para reconhecer as siglas. Por exemplo, strings como "I.B.M.", "a-b-c" ou "C.I.A". Uma sigla é uma string de caracteres alfabéticos individuais com o mesmo caractere separador entre todos eles. Os separadores válidos são o ponto, o traço ou qualquer número de espaços. O caractere separador é removido da string quando a sigla é tokenizada. Assim, as strings de exemplo mencionadas acima tornam-se os tokens "ibm", "abc" e "cia". O texto original permanece no campo do documento.

Ao lidar com siglas, observe que:

  • Uma sigla não pode conter mais de 21 letras. Uma string de sigla válida com mais de 21 letras será dividida em uma série de siglas, cada uma com até 21 letras.
  • Se as letras de uma sigla forem separadas por espaços, todas precisarão ter a mesma capitalização. As siglas formadas com pontos e traços podem usar letras maiúsculas e minúsculas.
  • Ao pesquisar uma sigla, insira a forma canônica dela (a string sem separadores) ou a sigla pontuada com o traço ou o ponto (mas não ambos) entre as letras. Portanto, o texto "IBM" pode ser recuperado com qualquer um dos termos de pesquisa: "I-B-M", "I.B.M" ou "IBM".

Precisão do campo de data

Ao criar um campo de data em um documento, configure o valor como java.util.Date. Com o objetivo de indexar e pesquisar o campo de data, qualquer componente de tempo será ignorado, e a data será convertida para o número de dias desde 1/1/1970 UTC. Isso significa que, mesmo que esse campo contenha um valor de tempo preciso, a consulta de data especifica apenas um valor de campo de data no formato yyyy-mm-dd. Também significa que a ordem de classificação dos campos de data com a mesma data não está bem definida.

Outras propriedades do documento

A classificação de um documento é um número inteiro positivo que determina a ordem padrão dos documentos retornados de uma pesquisa. Por padrão, ela é definida no momento em que o documento é criado, pelo número de segundos desde 1º de janeiro de 2011. É possível definir a classificação explicitamente ao criar um documento. Não é uma boa ideia atribuir a mesma classificação a muitos documentos. Nunca defina a mesma classificação para mais de 10.000 documentos. Ao especificar opções de classificação, é possível usar a classificação como uma chave. Quando a classificação é usada em uma expressão de classificação ou de campo, ela é mencionada como _rank.

A propriedade "locale" especifica a linguagem na qual os campos são programados.

Para mais detalhes sobre esses atributos, consulte a página de referência da classe Document.

Como vincular de um documento a outros recursos

Use o doc_id de um documento e outros campos como links para outros recursos no aplicativo. Por exemplo, se você usa o Blobstore, associe o documento a um blob específico. Basta definir doc_id ou o valor de um campo atômico como BlobKey dos dados.

Criar documentos

Para criar um documento, solicite um novo builder aplicando o método Document.newBuilder(). Com acesso a um criador, o aplicativo especifica um identificador de documentos opcional e adiciona campos.

Campos, assim como documentos, são criados usando-se um construtor. O método Field.newBuilder() retorna um builder de campo para que você especifique o nome e o valor de um campo. O tipo de campo é especificado automaticamente escolhendo um método específico de configuração. Por exemplo, para indicar que um campo armazena texto simples, chame setText(). O código a seguir cria um documento com campos que representam uma saudação do livro de visitas.

Java 8

User currentUser = UserServiceFactory.getUserService().getCurrentUser();
String userEmail = currentUser == null ? "" : currentUser.getEmail();
String userDomain = currentUser == null ? "" : currentUser.getAuthDomain();
String myDocId = "PA6-5000";
Document doc =
    Document.newBuilder()
        // Setting the document identifer is optional.
        // If omitted, the search service will create an identifier.
        .setId(myDocId)
        .addField(Field.newBuilder().setName("content").setText("the rain in spain"))
        .addField(Field.newBuilder().setName("email").setText(userEmail))
        .addField(Field.newBuilder().setName("domain").setAtom(userDomain))
        .addField(Field.newBuilder().setName("published").setDate(new Date()))
        .build();

Java 7

User currentUser = UserServiceFactory.getUserService().getCurrentUser();
String userEmail = currentUser == null ? "" : currentUser.getEmail();
String userDomain = currentUser == null ? "" : currentUser.getAuthDomain();
String myDocId = "PA6-5000";
Document doc = Document.newBuilder()
    // Setting the document identifer is optional.
    // If omitted, the search service will create an identifier.
    .setId(myDocId)
    .addField(Field.newBuilder().setName("content").setText("the rain in spain"))
    .addField(Field.newBuilder().setName("email").setText(userEmail))
    .addField(Field.newBuilder().setName("domain").setAtom(userDomain))
    .addField(Field.newBuilder().setName("published").setDate(new Date()))
    .build();

Para acessar os campos no documento, use getOnlyField():

Java 8

String coverLetter = document.getOnlyField("coverLetter").getText();
String resume = document.getOnlyField("resume").getHTML();
String fullName = document.getOnlyField("fullName").getAtom();
Date submissionDate = document.getOnlyField("submissionDate").getDate();

Java 7

String coverLetter = document.getOnlyField("coverLetter").getText();
String resume = document.getOnlyField("resume").getHTML();
String fullName = document.getOnlyField("fullName").getAtom();
Date submissionDate = document.getOnlyField("submissionDate").getDate();

Como trabalhar com um índice

Como colocar documentos em um índice

Ao colocar um documento em um índice, ele é copiado para o armazenamento permanente, e cada um dos campos é indexado de acordo com nome, tipo e doc_id.

Veja a seguir um exemplo de código para acessar um índice e colocar um documento nele. Estas são as etapas:

Java 8

public static void indexADocument(String indexName, Document document)
    throws InterruptedException {
  IndexSpec indexSpec = IndexSpec.newBuilder().setName(indexName).build();
  Index index = SearchServiceFactory.getSearchService().getIndex(indexSpec);

  final int maxRetry = 3;
  int attempts = 0;
  int delay = 2;
  while (true) {
    try {
      index.put(document);
    } catch (PutException e) {
      if (StatusCode.TRANSIENT_ERROR.equals(e.getOperationResult().getCode())
          && ++attempts < maxRetry) { // retrying
        Thread.sleep(delay * 1000);
        delay *= 2; // easy exponential backoff
        continue;
      } else {
        throw e; // otherwise throw
      }
    }
    break;
  }
}

Java 7

public static void indexADocument(String indexName, Document document)
    throws InterruptedException {
  IndexSpec indexSpec = IndexSpec.newBuilder().setName(indexName).build();
  Index index = SearchServiceFactory.getSearchService().getIndex(indexSpec);

  final int maxRetry = 3;
  int attempts = 0;
  int delay = 2;
  while (true) {
    try {
      index.put(document);
    } catch (PutException e) {
      if (StatusCode.TRANSIENT_ERROR.equals(e.getOperationResult().getCode())
          && ++attempts < maxRetry) { // retrying
        Thread.sleep(delay * 1000);
        delay *= 2; // easy exponential backoff
        continue;
      } else {
        throw e; // otherwise throw
      }
    }
    break;
  }
}
É possível passar até 200 documentos por vez ao método put(). Fazer isso em lote é mais eficiente do que adicionar documentos um de cada vez.

Quando você coloca um documento em um índice e este já contém um documento com o mesmo doc_id, o novo documento substitui o anterior. Nenhum aviso é exibido. Chame Index.get(id) antes de criar ou adicionar um documento a um índice para verificar se um doc_id específico já existe.

Criar uma instância da classe Index não garante que um índice permanente exista de fato. Esse índice é criado na primeira vez em que um documento é adicionado a ele usando o método put. Se você quiser verificar se um índice realmente existe antes de começar a usá-lo, execute o método SearchService.getIndexes().

Como atualizar documentos

Um documento não pode ser alterado depois de adicionado a um índice. Não é possível adicionar ou remover campos nem alterar o valor de um campo. No entanto, é possível substituir o documento por outro que tenha o mesmo doc_id.

Como recuperar documentos por doc_id

Há duas maneiras de recuperar documentos de um índice usando identificadores de documento:
  • Use Index.get() para buscar um único documento pelo respectivo doc_id.
  • Use Index.getRange() para recuperar um grupo de documentos consecutivos ordenados por doc_id.

Cada chamada é demonstrada no exemplo abaixo.

Java 8

IndexSpec indexSpec = IndexSpec.newBuilder().setName(INDEX).build();
Index index = SearchServiceFactory.getSearchService().getIndex(indexSpec);

// Fetch a single document by its  doc_id
Document doc = index.get("AZ125");

// Fetch a range of documents by their doc_ids
GetResponse<Document> docs =
    index.getRange(GetRequest.newBuilder().setStartId("AZ125").setLimit(100).build());

Java 7

IndexSpec indexSpec = IndexSpec.newBuilder().setName(INDEX).build();
Index index = SearchServiceFactory.getSearchService().getIndex(indexSpec);

// Fetch a single document by its  doc_id
Document doc = index.get("AZ125");

// Fetch a range of documents by their doc_ids
GetResponse<Document> docs = index.getRange(
    GetRequest.newBuilder().setStartId("AZ125").setLimit(100).build());

Como pesquisar documentos pelo conteúdo

Para recuperar documentos de um índice, crie uma string de consulta e chama Index.search(). É possível transmitir a string de consulta diretamente como argumento ou incluí-la em um objeto Query transmitido como argumento. Por padrão, search() retorna documentos correspondentes classificados em ordem decrescente. Para controlar a quantidade de documentos retornados e a classificação deles ou adicionar campos calculados aos resultados, é preciso usar um objeto Query. Ele contém uma string de consulta e também especifica outras opções de pesquisa e classificação.

Java 8

final int maxRetry = 3;
int attempts = 0;
int delay = 2;
while (true) {
  try {
    String queryString = "product = piano AND price < 5000";
    Results<ScoredDocument> results = getIndex().search(queryString);

    // Iterate over the documents in the results
    for (ScoredDocument document : results) {
      // handle results
      out.print("maker: " + document.getOnlyField("maker").getText());
      out.println(", price: " + document.getOnlyField("price").getNumber());
    }
  } catch (SearchException e) {
    if (StatusCode.TRANSIENT_ERROR.equals(e.getOperationResult().getCode())
        && ++attempts < maxRetry) {
      // retry
      try {
        Thread.sleep(delay * 1000);
      } catch (InterruptedException e1) {
        // ignore
      }
      delay *= 2; // easy exponential backoff
      continue;
    } else {
      throw e;
    }
  }
  break;
}

Java 7

final int maxRetry = 3;
int attempts = 0;
int delay = 2;
while (true) {
  try {
    String queryString = "product = piano AND price < 5000";
    Results<ScoredDocument> results = getIndex().search(queryString);

    // Iterate over the documents in the results
    for (ScoredDocument document : results) {
      // handle results
      out.print("maker: " + document.getOnlyField("maker").getText());
      out.println(", price: " + document.getOnlyField("price").getNumber());
    }
  } catch (SearchException e) {
    if (StatusCode.TRANSIENT_ERROR.equals(e.getOperationResult().getCode())
        && ++attempts < maxRetry) {
      // retry
      try {
        Thread.sleep(delay * 1000);
      } catch (InterruptedException e1) {
        // ignore
      }
      delay *= 2; // easy exponential backoff
      continue;
    } else {
      throw e;
    }
  }
  break;
}

Como excluir um índice

Cada índice consiste em documentos indexados e um esquema de índice. Para excluir um índice, exclua todos os documentos inclusos nele e o esquema.

Para excluir documentos de um índice, especifique o doc_id de um ou mais documentos que você quer remover no método delete(). Faça a exclusão em lotes para melhorar a eficiência. É possível transmitir até 200 códigos de documento por vez ao método delete().

Java 8

try {
// looping because getRange by default returns up to 100 documents at a time
while (true) {
List<String> docIds = new ArrayList<>();
// Return a set of doc_ids.
GetRequest request = GetRequest.newBuilder().setReturningIdsOnly(true).build();
GetResponse<Document> response = getIndex().getRange(request);
if (response.getResults().isEmpty()) {
  break;
}
for (Document doc : response) {
  docIds.add(doc.getId());
}
getIndex().delete(docIds);
}
// Delete the index schema
getIndex().deleteSchema();
} catch (RuntimeException e) {
LOG.log(Level.SEVERE, "Failed to delete index", e);
}

Java 7

try {
// looping because getRange by default returns up to 100 documents at a time
while (true) {
List<String> docIds = new ArrayList<>();
// Return a set of doc_ids.
GetRequest request = GetRequest.newBuilder().setReturningIdsOnly(true).build();
GetResponse<Document> response = getIndex().getRange(request);
if (response.getResults().isEmpty()) {
  break;
}
for (Document doc : response) {
  docIds.add(doc.getId());
}
getIndex().delete(docIds);
}
// Delete the index schema
getIndex().deleteSchema();
} catch (RuntimeException e) {
LOG.log(Level.SEVERE, "Failed to delete index", e);
}
É possível transmitir até 200 documentos por vez ao método delete(). A exclusão em lote é mais eficiente do que o processamento individual.

Consistência eventual

Quando você manipula um documento em um índice usando put, update ou delete, a alteração é propagada por vários data centers. Normalmente esse processo é rápido, mas o tempo pode variar. Uma consistência eventual (link em inglês) é garantida pela API Search. Isso significa que, em alguns casos, uma pesquisa ou a recuperação de documentos pode retornar resultados que não refletem as alterações mais recentes.

Como determinar o tamanho de um índice

Documentos para recuperação são armazenados pelo índice. É possível recuperar um documento pelo código, uma série de documentos com códigos consecutivos ou todos os documentos em um índice. Também é possível pesquisar um índice para recuperar documentos que atendam a determinados critérios nos campos e valores correspondentes, especificados como uma string de consulta. Também é possível gerenciar grupos de documentos colocando-os em índices separados. Não há limite para a quantidade de documentos em um índice nem para o número de índices em uso. O tamanho total de todos os documentos em um único índice é limitado a 10 GB por padrão. No entanto, é possível aumentá-lo para até 200 GB. Envie uma solicitação na página de Pesquisa do App Engine do Console do Google Cloud Platform. O método Index.getStorageLimit() retorna o tamanho máximo permitido para um índice.

O método Index.getStorageUsage() é uma estimativa do espaço de armazenamento usado por um índice. Esse número é uma estimativa, já que o sistema de monitoramento de índice não é executado de maneira contínua. O uso real é calculado periodicamente. O storage_usage é ajustado entre pontos de amostragem contabilizando-se as adições de documentos, mas não as exclusões.

Esquemas de índice

Todos os índices têm esquemas que mostram os nomes e os tipos de campos que aparecem nos documentos que contêm. Não é possível definir um esquema. Os esquemas são mantidos dinamicamente e são atualizados conforme os documentos são adicionados ao índice. Um esquema simples pode ter a seguinte aparência em um formulário estilo JSON:

{'comment': ['TEXT'], 'date': ['DATE'], 'author': ['TEXT'], 'count': ['NUMBER']}

Cada chave do dicionário é o nome de um campo de documento. O valor da chave é uma lista dos tipos de campo usados com esse nome. Se você usar o mesmo nome de campo com tipos diferentes, o esquema listará mais de um tipo para cada nome, como no exemplo a seguir:

{'ambiguous-integer': ['TEXT', 'NUMBER', 'ATOM']}

Quando um campo aparece em um esquema, ele nunca mais pode ser removido. Não há como excluir um campo, mesmo que o índice não contenha mais nenhum documento com esse nome de campo específico.

É possível ver os esquemas dos índices assim:

Java 8

GetResponse<Index> response =
    SearchServiceFactory.getSearchService()
        .getIndexes(GetIndexesRequest.newBuilder().setSchemaFetched(true).build());

// List out elements of each Schema
for (Index index : response) {
  Schema schema = index.getSchema();
  for (String fieldName : schema.getFieldNames()) {
    List<FieldType> typesForField = schema.getFieldTypes(fieldName);
    // Just printing out the field names and types
    for (FieldType type : typesForField) {
      out.println(index.getName() + ":" + fieldName + ":" + type.name());
    }
  }
}

Java 7

GetResponse<Index> response = SearchServiceFactory.getSearchService().getIndexes(
    GetIndexesRequest.newBuilder().setSchemaFetched(true).build());

// List out elements of each Schema
for (Index index : response) {
  Schema schema = index.getSchema();
  for (String fieldName : schema.getFieldNames()) {
    List<FieldType> typesForField = schema.getFieldTypes(fieldName);
    // Just printing out the field names and types
    for (FieldType type : typesForField) {
      out.println(index.getName() + ":" + fieldName + ":" + type.name());
    }
  }
}
Não é possível que uma chamada para GetIndexes() retorne mais de 1.000 índices. Para recuperar mais índices, chame repetidamente o método usando setStartIndexName() com GetIndexesRequest.Builder.

No esquema, não há a definição de uma "classe" no sentido da programação de objeto. No que diz respeito à API Search, cada documento é único, e os índices podem conter diferentes tipos de documentos. Se você quiser tratar coleções de objetos com a mesma lista de campos como instâncias de uma classe, isso é uma abstração que precisará ser aplicada no seu código. Por exemplo, é possível garantir que todos os documentos com o mesmo conjunto de campos sejam mantidos no próprio índice. O esquema de índice pode ser visto como a definição da classe, e cada documento no índice é uma instância dessa classe.

Como ver índices no console do Google Cloud Platform

No Console do GCP, é possível visualizar informações sobre os índices do aplicativo e os documentos que eles contêm. Clique em um nome de índice para exibir os documentos contidos nele. Você verá todos os campos de esquema definidos para o índice e, para cada documento que contém um campo com aquele nome, você verá o valor desse campo. Também é possível emitir consultas aos dados do índice diretamente do console.

Cotas da API Search

A API Search tem várias cotas gratuitas:

Recurso ou chamada de API Cota gratuita
Armazenamento total (documentos e índices) 0,25 GB
Consultas 1.000 consultas por dia
Adição de documentos a índices 0,01 GB por dia

Esses limites são impostos pela API para garantir a confiabilidade do serviço. Eles são válidos para aplicativos gratuitos e pagos:

Recurso Cota de segurança
Uso máximo da consulta 100 minutos agregados de tempo de execução da consulta por minuto
Máximo de documentos adicionados ou excluídos 15.000 por minuto
Tamanho máximo por índice (número ilimitado de índices permitido) 10 GB

O uso da API é contado de diferentes maneiras, dependendo do tipo de chamada:

  • Index.search(): cada chamada de API conta como uma consulta. O tempo de execução é equivalente à latência da chamada.
  • Index.put(): quando você adiciona documentos a índices, o tamanho de cada documento e o número de documentos contam para a cota de indexação.
  • Todas as outras chamadas da API Search são contadas com base no número de operações incluídas:
    • SearchService.getIndexes(): uma operação é contada para cada índice realmente retornado, ou uma operação se nada for retornado;
    • Index.get() e Index.getRange(): uma operação é contada para cada documento realmente retornado, ou uma operação se nenhum resultado for retornado;
    • Index.delete(): uma operação é contada para cada documento na solicitação, ou uma operação se a solicitação estiver vazia.

A cota sobre a capacidade da consulta é imposta para que um único usuário não monopolize o serviço de pesquisa. Como as consultas podem ser executadas simultaneamente, cada aplicativo pode executar consultas que consumam até 100 minutos de tempo de execução por minuto no relógio. Se executar várias consultas curtas, você provavelmente não atingirá esse limite. porém, se exceder a cota, as consultas subsequentes falharão até a próxima fração de tempo, quando a cota é restaurada. A cota não é imposta rigorosamente em frações de um minuto. Uma variação do algoritmo leaky bucket é usada para controlar a largura de banda da pesquisa em incrementos de cinco segundos.

Veja mais informações na página Cotas. Se o app tentar exceder esses valores, é exibido um erro de cotas insuficientes.

Esses limites são aplicados por minuto, mas o console exibe os totais diários para cada um. Os clientes com suporte Silver, Gold ou Platinum podem solicitar limites de capacidade maiores. Basta entrar em contato com o representante do suporte.

Preços da API Search

Se você habilitar o faturamento para o aplicativo, será cobrado por uso adicional além das cotas gratuitas. As seguintes cobranças se aplicam aos aplicativos faturados:

Recurso Custo
Armazenamento total (documentos e índices) US$ 0,18 por GB/mês
Consultas US$ 0,50 por 10.000 consultas
Indexação de documentos pesquisáveis US$ 2,00 por GB

Para mais informações, acesse a página de Preços.

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Ambiente padrão do App Engine para Java 8