A API Search fornece um modelo para indexar documentos que contêm dados estruturados. Pode pesquisar um índice e organizar e apresentar os resultados da pesquisa. A API suporta a correspondência de texto completo em campos de strings. Os documentos e os índices são guardados num armazenamento persistente separado otimizado para operações de pesquisa. A API Search pode indexar qualquer número de documentos. O App Engine Datastore pode ser mais adequado para aplicações que precisam de obter conjuntos de resultados muito grandes.
Vista geral
A API Search baseia-se em quatro conceitos principais: documentos, índices, consultas e resultados.
Documentos
Um documento é um objeto com um ID exclusivo e uma lista de campos que contêm dados do utilizador. Cada campo tem um nome e um tipo. Existem vários tipos de campos, identificados pelos tipos de valores que contêm:
- Campo Atom: uma string de carateres indivisível.
- Campo de texto: uma string de texto simples que pode ser pesquisada palavra a palavra.
- Campo HTML: uma string que contém etiquetas de marcação HTML. Só é possível pesquisar o texto fora das etiquetas de marcação.
- Campo de número: um número de vírgula flutuante.
- Campo de data: um objeto de data com ano/mês/dia e hora opcional.
- Campo de geoponto: um objeto de dados com coordenadas de latitude e longitude.
O tamanho máximo de um documento é de 1 MB.
Índices
Um índice armazena documentos para obtenção. Pode obter um único documento pelo respetivo ID, um intervalo de documentos com IDs consecutivos ou todos os documentos num índice. Também pode pesquisar um índice para obter documentos que satisfaçam determinados critérios em campos e respetivos valores, especificados como uma string de consulta. Pode gerir grupos de documentos colocando-os em índices separados.
Não existe limite para o número de documentos num índice nem para o número de índices que pode usar. O tamanho total de todos os documentos num único índice está limitado a 10 GB por predefinição. Os utilizadores com a função Administrador do App Engine podem enviar um pedido a partir da página Pesquisa do App Engine da Google Cloud consola para aumentar o tamanho até 200 GB.
Consultas
Para pesquisar um índice, cria uma consulta, que tem uma string de consulta e, possivelmente, algumas opções adicionais. Uma string de consulta especifica condições para os valores de um ou mais campos de documentos. Quando pesquisa um índice, recebe apenas os documentos no índice com campos que satisfazem a consulta.
A consulta mais simples, por vezes denominada "pesquisa global", é uma string que contém apenas valores de campos. Esta pesquisa usa uma string que procura documentos que contenham as palavras "rosa" e "água":
Esta pesquisa procura documentos com campos de data que contenham a data 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. Pode conter um ou mais termos, cada um com o nome de um campo e uma restrição no valor do campo. A forma exata de um termo depende do tipo de campo. Por exemplo, supondo que existe um campo de texto denominado "produto" e um campo numérico denominado "preço", eis uma string de consulta com dois termos:
As opções de consulta, como o nome indica, não são obrigatórias. Permitem uma variedade de funcionalidades:
- Controlar o número de documentos devolvidos nos resultados da pesquisa.
- Especifique os campos de documentos a incluir nos resultados. A predefinição é incluir todos os campos do documento original. Pode especificar que os resultados incluem apenas um subconjunto de campos (o documento original não é afetado).
- Ordene os resultados.
- Crie "campos calculados" para documentos com
FieldExpressions
e campos de texto abreviados com fragmentos. - Suporte a paginação nos resultados da pesquisa devolvendo apenas uma parte dos documentos correspondentes em cada consulta (através de deslocamentos e cursores)
Recomendamos que registe as strings de consulta na sua aplicação se quiser manter um registo das consultas que foram executadas.
Resultados da pesquisa
Uma chamada parasearch()
só pode devolver um número limitado de documentos correspondentes.
A sua pesquisa pode encontrar mais documentos do que os que podem ser devolvidos numa única chamada. Cada chamada de pesquisa devolve uma instância da classe
SearchResults
, que contém informações sobre quantos documentos foram encontrados e quantos foram devolvidos, juntamente com a lista de documentos devolvidos. Pode repetir a mesma pesquisa usando cursores ou desvios para obter o conjunto completo de documentos correspondentes.
Material de formação adicional
Além desta documentação, pode ler a aula de formação em duas partes sobre a API Search na Google Developers Academy. A classe inclui uma aplicação Python de exemplo.
Documentos e campos
A classe Document representa documentos. Cada documento tem um identificador de documento e uma lista de campos.Identificador do documento
Cada documento num índice tem de ter um identificador de documento exclusivo ou doc_id
.
O identificador pode ser usado para obter um documento a partir de um índice sem realizar uma pesquisa. Por predefinição, a API Search gera automaticamente um doc_id
quando
é criado um documento. Também pode especificar o doc_id
quando
cria um documento. Um doc_id
tem de conter apenas carateres ASCII visíveis e imprimíveis (códigos ASCII de 33 a 126 inclusive) e não ter mais de 500 carateres. Um identificador de documento não pode começar com um ponto de exclamação ("!")
e não pode começar nem terminar com dois sublinhados ("__").
Embora seja conveniente criar identificadores de documentos únicos legíveis e significativos, não pode incluir o elemento doc_id
numa pesquisa. Considere este cenário: tem um índice com documentos que representam peças, usando o número de série da peça como o doc_id
. Será muito eficiente obter o documento para qualquer peça única, mas será impossível pesquisar um intervalo de números de série juntamente com outros valores de campos, como a data de compra. Armazenar o número de série num campo atom resolve o problema.
Campos de documentos
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, pode definir dois campos com o nome "age": um com um tipo de texto (o valor "twenty-two") e o outro com um tipo de número (o valor 22).
Nomes dos campos
Os nomes dos campos são sensíveis a maiúsculas e minúsculas e só podem conter carateres ASCII. Têm de começar por uma letra e podem conter letras, dígitos ou um sublinhado. O nome de um campo não pode ter mais de 500 carateres.
Campos com vários valores
Um campo só pode conter um valor, que tem de corresponder ao tipo do campo. Os nomes dos campos não têm de ser exclusivos. Um documento pode ter vários campos com o mesmo nome e o mesmo tipo, o que é uma forma 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 tipos de campos diferentes.
Tipos de campos
Existem três tipos de campos que armazenam strings de carateres. Coletivamente, referimo-nos a eles como campos de string:
- Campo de texto: uma string com um comprimento máximo de 1024**2 carateres.
- Campo HTML: uma string formatada em HTML com um comprimento máximo de 1024**2 carateres.
- Campo Atom: uma string com um comprimento máximo de 500 carateres.
Existem também três tipos de campos que armazenam dados não textuais:
- Campo numérico: um valor de vírgula flutuante de precisão dupla entre -2 147 483 647 e 2 147 483 647.
- Campo de data:
datetime.date
oudatetime.datetime
. - Campo geoponto: um ponto na Terra descrito por coordenadas de latitude e longitude.
Os tipos de campos são especificados pelas classes
TextField
,
HtmlField
,
AtomField
,
NumberField
,
DateField
,
e GeoField
.
Tratamento especial de campos de strings e datas
Quando um documento com campos de data, texto ou HTML é adicionado a um índice, ocorre um processamento especial. É útil compreender o que se passa "nos bastidores" para usar a API Search de forma eficaz.
Tokenizar campos de string
Quando um campo de texto ou HTML é indexado, o respetivo conteúdo é transformado em tokens. A string é dividida em tokens sempre que aparecem espaços em branco ou carateres especiais (sinais de pontuação, símbolo de cardinal, barra invertida, etc.). O índice vai incluir uma entrada para cada token. Isto permite-lhe pesquisar palavras-chave e expressões que compreendem apenas parte do valor de um campo. Por exemplo, uma pesquisa por "escuro" vai corresponder a um documento com um campo de texto que contenha a string "era uma noite escura e tempestuosa", e uma pesquisa por "tempo" vai corresponder a um documento com um campo de texto que contenha a string "este é um sistema em tempo real".
Nos campos HTML, o texto nas etiquetas de marcação não é tokenizado, pelo que um documento com um campo HTML que contenha it was a <strong>dark</strong> night
corresponde a uma pesquisa de "noite", mas não de "forte". Se quiser poder pesquisar texto de marcação, armazene-o num campo de texto.
Os campos Atom não são tokenizados. Um documento com um campo atom que tenha o valor "mau tempo" só corresponde a uma pesquisa da string completa "mau tempo". Não corresponde a uma pesquisa de "mau" ou "tempo" isoladamente.
Regras de tokenização
Os carateres de sublinhado (_) e comercial (&) não dividem as palavras em tokens.
Estes carateres de espaços em branco dividem sempre as palavras em tokens: espaço, retorno de carro, quebra de linha, tabulação horizontal, tabulação vertical, mudança de página e NULL.
Estes carateres são tratados como pontuação e dividem as palavras em tokens:
! " % ( ) * , - | / [ ] ] ^ ` : = > ? @ { } ~ € Os carateres na tabela seguinte normalmente dividem as palavras em tokens, mas podem ser processados de forma diferente consoante o contexto em que aparecem:
Personagem Regra <
Num campo HTML, o sinal "menor que" indica o início de uma etiqueta HTML que é ignorada. +
Uma sequência de um ou mais sinais de "mais" é tratada como parte da palavra se aparecer no final da palavra (C++). #
O sinal "hash" é tratado como parte da palavra se for precedido por 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; c# é ambos). Se um termo for precedido por "#" (#google), é tratado como uma hashtag e o símbolo de cardinal passa a fazer parte da palavra. '
O apóstrofo é uma letra se preceder a letra "s" seguida de uma quebra de palavra, como em "o chapéu do João". .
Se aparecer um ponto entre dígitos, faz parte de um número (ou seja, o separador decimal). Também pode fazer parte de uma palavra se for usado num acrónimo (A.B.C). -
O travessão faz parte de uma palavra se for usado num acrónimo (I-B-M). Todos os outros carateres de 7 bits que não sejam letras nem dígitos ("A-Z", "a-z", "0-9") são processados como pontuação e dividem as palavras em tokens.
Tudo o resto é analisado como um caráter UTF-8.
Acrónimos
A tokenização usa regras especiais para reconhecer acrónimos (strings como "I.B.M.", "a-b-c" ou "C I A"). Uma sigla é uma string de carateres alfabéticos únicos, com o mesmo caráter separador entre todos eles. Os separadores válidos são o ponto, o traço ou qualquer número de espaços. O caráter separador é removido da string quando um acrónimo é tokenizado. Assim, as strings de exemplo mencionadas acima tornam-se os tokens "ibm", "abc" e "cia". O texto original permanece no campo do documento.
Quando trabalhar com acrónimos, tenha em atenção que:
- Um acrónimo não pode conter mais de 21 letras. Uma string de acrónimo válida com mais de 21 letras é dividida numa série de acrónimos, cada um com 21 letras ou menos.
- Se as letras de um acrónimo estiverem separadas por espaços, todas as letras têm de estar em maiúsculas ou minúsculas. Os acrónimos construídos com um ponto e um traço podem usar letras em maiúsculas e minúsculas.
- Quando pesquisa um acrónimo, pode introduzir a forma canónica do acrónimo (a string sem separadores) ou o acrónimo com pontuação com o traço ou o ponto (mas não ambos) entre as letras. Assim, o texto "I.B.M" pode ser obtido com qualquer um dos termos de pesquisa "I-B-M", "I.B.M" ou "IBM".
Precisão do campo de data
Quando cria um campo de data num documento, define o respetivo valor como datetime.date
ou datetime.datetime
. Tenha em atenção que só podem ser usados objetos de data e hora"ingénuos" do Python. Não são permitidos objetos "Aware".
.
Para fins de indexação e pesquisa do campo de data, qualquer componente de tempo é ignorado e a data é convertida no número de dias desde 01/01/1970 UTC. Isto significa que, embora um campo de data possa conter um valor de hora preciso, uma consulta de data só pode especificar um valor de campo de data no formato yyyy-mm-dd
. Isto 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 ordenação predefinida dos documentos devolvidos por uma pesquisa. Por predefinição, a classificação é definida no momento em que o documento é criado para o número de segundos desde 1 de janeiro de 2011. Pode definir a classificação explicitamente quando cria um documento. É uma má ideia atribuir a mesma classificação a muitos documentos e nunca deve atribuir a mais de 10 000 documentos a mesma classificação.
Se especificar opções de ordenação,
pode usar a classificação como chave de ordenação. Tenha em atenção que, quando a classificação é usada numa expressão de ordenação
ou numa expressão de campo
, é referenciada como _rank
.
A propriedade language especifica o idioma no qual os campos estão codificados.
Consulte a página de referência da classe
Document
para ver mais detalhes sobre estes atributos.
Criar links de um documento para outros recursos
Pode usar o doc_id
de um documento e outros campos como links para outros recursos na sua aplicação. Por exemplo, se usar o Blobstore, pode associar o documento a um blob específico definindo o doc_id
ou o valor de um campo Atom para o BlobKey dos dados.
Criar um documento
O exemplo de código seguinte mostra como criar um objeto de documento. O construtor Document é chamado com o argumento fields definido como uma lista de objetos de campo.
Cada objeto na lista é criado e inicializado através da função de construtor da classe do campo. Tenha em atenção a utilização do construtor GeoPoint
e da classe datetime
do Python para criar os tipos adequados de valores de campos.
Trabalhar com um índice
Colocar documentos num índice
Quando coloca um documento num índice, o documento é copiado para o armazenamento persistente e cada um dos respetivos campos é indexado de acordo com o respetivo nome, tipo e doc_id
.
O exemplo de código seguinte mostra como aceder a um índice e colocar um documento no mesmo.
Pode transmitir até 200 documentos de cada vez ao métodoput()
. O processamento em lote é mais eficiente do que adicionar documentos um de cada vez.
Quando coloca um documento num índice e o índice já contém um documento com o mesmo doc_id
, o novo documento substitui o antigo. Não é dado nenhum aviso. Pode chamar
Index.get(id)
antes de criar ou adicionar um documento a um índice para verificar se um
doc_id
específico já existe.
O método put
devolve uma lista de PutResults
, uma para cada documento transmitido como argumento. Se não especificou o elemento doc_id
, pode examinar o atributo id
do resultado para descobrir o elemento doc_id
que foi gerado:
Tenha em atenção que a criação de uma instância da classe Index
não garante que existe efetivamente um índice persistente. É criado um índice persistente da primeira vez que
lhe adiciona um documento com o método put
.
Se quiser verificar se um índice existe realmente antes de começar a usá-lo, use a função search.get_indexes()
.
Atualizar documentos
Não é possível alterar um documento depois de o adicionar a um índice. Não pode adicionar nem remover campos, nem alterar o valor de um campo. No entanto, pode substituir o documento por um novo documento com o mesmo doc_id
.
A obter documentos por doc_id
Existem duas formas de obter documentos de um índice através de identificadores de documentos:- Use
Index.get()
para obter um único documento pelo respetivodoc_id
. - Use
Index.get_range()
para obter um grupo de documentos consecutivos ordenados pordoc_id
.
Cada chamada é demonstrada no exemplo abaixo.
Pesquisar documentos pelo respetivo conteúdo
Para obter documentos de um índice, cria uma string de consulta e chama o método
Index.search()
.
A string de consulta pode ser transmitida diretamente como o argumento ou pode incluir a string num objeto Query, que é transmitido como o argumento.
Por predefinição, search()
devolve documentos correspondentes ordenados por ordem decrescente da classificação. Para controlar quantos documentos são devolvidos, como são ordenados ou adicionar campos calculados aos resultados, tem de usar um objeto Query
, que contém uma string de consulta e também pode especificar outras opções de pesquisa e ordenação.
Eliminar um índice
Cada índice consiste nos respetivos documentos indexados e num esquema de índice. Para eliminar um índice, elimine todos os documentos num índice e, em seguida, elimine o esquema do índice.
Pode eliminar documentos num índice especificando o doc_id
de
um ou mais documentos
que quer eliminar no método Index.delete().
Deve eliminar documentos em lotes para melhorar a eficiência. Pode transmitir até 200 IDs de documentos de cada vez ao método delete()
.
def delete_index(index): # index.get_range by returns up to 100 documents at a time, so we must # loop until we've deleted all items. while True: # Use ids_only to get the list of document IDs in the index without # the overhead of getting the entire document. document_ids = [ document.doc_id for document in index.get_range(ids_only=True)] # If no IDs were returned, we've deleted everything. if not document_ids: break # Delete the documents for the given IDs index.delete(document_ids) # delete the index schema index.delete_schema()
delete()
. O processamento em lote das eliminações é mais eficiente do que o processamento individual.
Esta abordagem pode demorar muito tempo se precisar de eliminar um grande número de entradas do índice de pesquisa. Para resolver este problema, experimente o seguinte:
- Eliminar o projeto e as respetivas dependências.
- Peça uma quota mais elevada para eliminações mais rápidas.
Consistência eventual
Quando coloca, atualiza ou elimina um documento num índice, a alteração propaga-se por vários centros de dados. Normalmente, isto acontece rapidamente, mas o tempo que demora pode variar. A API Search garante a consistência eventual. Isto significa que, em alguns casos, uma pesquisa ou uma obtenção de um ou mais documentos pode devolver resultados que não refletem as alterações mais recentes.
Determinar o tamanho de um índice
Um índice armazena documentos para obtenção. Pode obter um único documento pelo respetivo ID, um intervalo de documentos com IDs consecutivos ou todos os documentos num índice. Também pode pesquisar um índice para obter documentos que satisfaçam determinados critérios em campos e respetivos valores, especificados como uma string de consulta. Pode gerir
grupos de documentos colocando-os em índices separados. Não existe limite para o número de documentos num índice nem para o número de índices que pode usar. O tamanho total de todos os documentos num único índice está limitado a 10 GB por predefinição, mas pode ser aumentado até 200 GB através do envio de um pedido a partir da páginaGoogle Cloud consola App Engine Search. A propriedade de índice storage_limit
é o tamanho máximo permitido de um índice.
storage_usage
é uma estimativa da quantidade de espaço de armazenamento usado por um índice. Este número é uma estimativa porque o sistema de monitorização do índice não é executado continuamente. A utilização real é calculada periodicamente. O storage_usage
é ajustado entre os pontos de amostragem tendo em conta as adições de documentos, mas não as eliminações.
Realizar operações assíncronas
Pode usar chamadas assíncronas para executar várias operações sem bloqueio e, em seguida, obter todos os resultados ao mesmo tempo, bloqueando apenas uma vez. Por exemplo, o código seguinte executa várias pesquisas de forma assíncrona:
Esquemas de índice
Cada índice tem um esquema que mostra todos os nomes e tipos de campos que aparecem nos documentos que contém. Não pode definir um esquema. Os esquemas são mantidos dinamicamente e são atualizados à medida que os documentos são adicionados a um índice. Um esquema simples pode ter o seguinte aspeto, num formato semelhante a JSON:
{'comment': ['TEXT'], 'date': ['DATE'], 'author': ['TEXT'], 'count': ['NUMBER']}
Cada chave no dicionário é o nome de um campo de documento. O valor da chave é uma lista dos tipos de campos usados com esse nome de campo. Se tiver usado o mesmo nome de campo com diferentes tipos de campos, o esquema apresenta mais do que um tipo de campo para um nome de campo, da seguinte forma:
{'ambiguous-integer': ['TEXT', 'NUMBER', 'ATOM']}
Depois de um campo aparecer num esquema, nunca pode ser removido. Não é possível eliminar um campo, mesmo que o índice já não contenha documentos com esse nome de campo específico.
Pode ver os esquemas dos seus índices da seguinte forma:from google.appengine.api import search
...
for index in search.get_indexes(fetch_schema=True):
logging.info("index %s", index.name)
logging.info("schema: %s", index.schema)
get_indexes
não pode devolver
mais de 1000 índices. Para obter mais índices,
chame a função repetidamente usando o argumento start_index_name
.
Um esquema não define uma "classe" no sentido da programação orientada a objetos. No que diz respeito à API Search, cada documento é único e os índices podem conter diferentes tipos de documentos. Se quiser tratar coleções de objetos com a mesma lista de campos como instâncias de uma classe, essa é uma abstração que tem de aplicar no seu código. Por exemplo, pode garantir que todos os documentos com o mesmo conjunto de campos são mantidos no respetivo índice. O esquema do índice pode ser visto como a definição da classe e cada documento no índice seria uma instância da classe.
Visualizar índices na Google Cloud consola
Na Google Cloud consola, pode ver informações sobre os índices da sua aplicação e os documentos que contêm. Se clicar no nome de um índice, são apresentados os documentos que esse índice contém. São apresentados todos os campos do esquema definidos para o índice. Para cada documento com um campo desse nome, é apresentado o valor do campo. Também pode emitir consultas sobre os dados do índice diretamente a partir da consola.
Quotas da API Search
A API Search tem várias quotas gratuitas:
Recurso ou chamada API | Quota gratuita |
---|---|
Armazenamento total (documentos e índices) | 0,25 GB |
Consultas | 1000 consultas por dia |
Adicionar documentos a índices | 0,01 GB por dia |
A API Search impõe estes limites para garantir a fiabilidade do serviço. Estas aplicam-se a apps gratuitas e pagas:
Recurso | Quota de segurança |
---|---|
Utilização máxima de consultas | 100 minutos agregados de tempo de execução de consultas por minuto |
Número máximo de documentos adicionados ou eliminados | 15 000 por minuto |
Tamanho máximo por índice (número ilimitado de índices permitido) | 10 GB |
A utilização da API é contabilizada de diferentes formas consoante o tipo de chamada:
Index.search()
: cada chamada API conta como uma consulta; o tempo de execução é equivalente à latência da chamada.Index.put()
: quando adiciona documentos a índices, o tamanho de cada documento e o número de documentos contam para a quota de indexação.- Todas as outras chamadas da API Google Search são contabilizadas com base no número de operações que envolvem:
-
search.get_indexes()
: é contabilizada 1 operação para cada índice efetivamente devolvido ou 1 operação se nada for devolvido. -
Index.get()
eIndex.get_range()
: 1 operação contabilizada para cada documento efetivamente devolvido ou 1 operação se nada for devolvido. -
Index.delete()
: 1 operação contabilizada para cada documento no pedido ou 1 operação se o pedido estiver vazio.
-
A quota de débito de consultas é imposta para que um único utilizador não possa monopolizar o serviço de pesquisa. Uma vez que as consultas podem ser executadas em simultâneo, cada aplicação pode executar consultas que consomem até 100 minutos de tempo de execução por um minuto de tempo real. Se estiver a executar muitas consultas curtas, provavelmente não vai atingir este limite. Quando excede a quota, as consultas subsequentes falham até ao próximo intervalo de tempo, quando a quota é restaurada. A quota não é imposta estritamente em intervalos de um minuto. É usada uma variação do algoritmo de leaky bucket para controlar a largura de banda de pesquisa em intervalos de cinco segundos.
Pode encontrar mais informações sobre as quotas na página Quotas. Quando uma app tenta exceder estes valores, é devolvido um erro de quota insuficiente.
Tenha em atenção que, embora estes limites sejam aplicados por minuto, a consola apresenta os totais diários de cada um. Os clientes com apoio técnico Silver, Gold ou Platinum podem solicitar limites de débito mais elevados contactando o respetivo representante do apoio técnico.
Preços da API Search
As seguintes cobranças são aplicadas à utilização que exceda as quotas gratuitas:
Recurso | Custo |
---|---|
Armazenamento total (documentos e índices) | 0,18 $ por GB por mês |
Consultas | 0,50 $ por 10 mil consultas |
Indexação de documentos pesquisáveis | 2,00 USD por GB |
Pode encontrar informações adicionais sobre os preços na página Preços.