La API de búsqueda proporciona un modelo para indexar documentos que contienen datos estructurados. Puedes realizar búsquedas en un índice, y organizar y presentar los resultados de la búsqueda. La API es compatible con la búsqueda de coincidencias de texto completa para los campos de string. Los documentos y los índices se guardan en un almacén persistente independiente optimizado para operaciones de búsqueda. La API de búsqueda puede indexar cualquier cantidad de documentos. Para aplicaciones que necesitan recuperar grandes conjuntos de resultados, puede ser más conveniente usar App Engine Datastore.
Descripción general
La API de búsqueda se basa en cuatro conceptos principales: documentos, índices, consultas y resultados.
Documentos
Un documento es un objeto con un ID único y una lista de campos que contienen datos del usuario. Cada campo tiene un nombre y un tipo. Hay varios tipos de campos, que se identifican por las clases de valores que contienen:
- Campo atómico: una string de caracteres indivisible
- Campo de texto: una string de texto sin formato donde se pueden realizar búsquedas palabra por palabra
- Campo HTML: una string que contiene etiquetas de lenguaje de marcado HTML; solo admite búsquedas sobre el texto que está por fuera de las etiquetas
- Campo numérico: un número de punto flotante
- Campo de fecha: un objeto de fecha
- Campo de punto geográfico: un objeto de datos con coordenadas de latitud y longitud
El tamaño máximo de un documento es de 1 MB.
Índices
Un índice almacena documentos para su recuperación. Puedes recuperar un solo documento por su ID, un rango de documentos con ID consecutivos o todos los documentos de un índice. También puedes buscar en un índice para recuperar documentos que satisfagan determinados criterios en los campos y sus valores, especificados como una cadena de consulta. Puedes administrar grupos de documentos colocándolos en índices separados.
No hay límite para el número de índices o de documentos por índice que puedes usar. El tamaño total de todos los documentos en un solo índice se limita a 10 GB de forma predeterminada. Los usuarios con el rol Administrador de App Engine pueden enviar una solicitud desde la página Búsqueda de App Engine de la consola de Google Cloud para aumentar el tamaño hasta 200 GB.
Consultas
Para hacer búsquedas en un índice, debes generar una consulta, que incluye una cadena de consulta y posibles opciones adicionales. Una cadena de consulta especifica condiciones para los valores de uno o más de los campos del documento. Cuando haces búsquedas en un índice, obtienes como resultado solamente los documentos con campos que satisfagan la consulta.
La consulta más sencilla, llamada a veces "consulta global", es una string que contiene solo valores de campos. La siguiente búsqueda usa una string que busca documentos con las palabras “rose” y “water”:
En el siguiente ejemplo, se buscan documentos con campos de fecha que contengan la fecha 4 de julio de 1776, o campos de texto que incluyan la string “1776-07-04”:
Una cadena de consulta también puede ser más específica. Puede contener uno o más términos; cada uno nombrará un campo y una restricción al valor de ese campo. La forma exacta de un término depende del tipo del campo. Por ejemplo, si hay un campo de texto llamado “product” y un campo numérico llamado “price”, puede generarse una cadena de consulta con dos términos como la siguiente:
Las opciones de consulta, como su nombre lo indica, no son necesarias. Estas habilitan diferentes características, entre ellas:
- Controlar cuántos documentos se muestran en los resultados de la búsqueda
- Especificar qué campos de los documentos deben incluirse en los resultados: según la configuración predeterminada, se incluyen todos los campos del documento original, pero puedes especificar que los resultados solo incluyan un subconjunto de campos (el documento original no se ve afectado)
- Ordenar los resultados
- Crear “campos procesados” para documentos mediante
FieldExpressions
y campos de texto abreviados mediante fragmentos - Admitir la paginación en los resultados de la búsqueda, para lo que se muestra solo una parte de los documentos coincidentes en cada consulta (con desplazamientos y cursores)
Te recomendamos registrar las strings de consulta en tu aplicación si deseas mantener un registro de las consultas que se ejecutaron.
Resultados de la búsqueda
Una llamada asearch()
solo puede mostrar una cantidad limitada de documentos coincidentes.
Es posible que tu búsqueda encuentre más documentos que los que pueden mostrarse en una sola llamada. Cada llamada de búsqueda muestra una instancia de la clase Results
, que contiene información sobre cuántos documentos se encontraron y cuántos se mostraron, junto con la lista de los documentos mostrados. Puedes repetir la misma búsqueda mediante cursores o desplazamientos para recuperar el conjunto completo de documentos coincidentes.
Material de capacitación adicional
Además de esta documentación, puedes leer las dos partes de la capacitación acerca de la API de búsqueda en Google Developer's Academy. (Aunque en la clase se usa la API de Python, el análisis adicional de los conceptos relacionados con la búsqueda podría serte útil).
Documentos y campos
La clase Document representa documentos. Cada documento tiene un identificador de documento y una lista de campos.Identificador de documento
Cada documento de un índice debe tener un identificador de documento único, o doc_id
.
El identificador puede usarse para recuperar un documento de un índice sin realizar una búsqueda. De forma predeterminada, la API de búsqueda genera de forma automática un doc_id
cuando se crea un documento. También puedes especificar el doc_id
por tu cuenta cuando creas un documento. Un doc_id
debe contener solo caracteres ASCII visibles y que puedan imprimirse (códigos ASCII del 33 al 126 inclusive) y no debe tener más de 500 caracteres en total. Los identificadores de documento no pueden empezar con un signo de exclamación (“!”), y no pueden empezar ni terminar con doble guion bajo (“__”).
Aunque es conveniente crear identificadores de documento únicos, legibles y significativos, no puedes incluir el doc_id
en una búsqueda. Considera la siguiente situación: tienes un índice con documentos que representan partes, en el que se usa el número de serie de la parte como doc_id
. Será muy eficiente para recuperar el documento de cualquier parte individual, pero no permitirá buscar un rango de números de serie junto con otros valores de campo, como la fecha de compra. Este problema se soluciona almacenando el número de serie en un campo atómico.
Campos del documento
Un documento incluye campos que tienen un nombre, un tipo y un solo valor de ese tipo. Dos o más campos pueden tener el mismo nombre, pero diferentes tipos. Por ejemplo, puedes definir dos campos con el nombre "edad": uno de tipo texto (valor "veintidós") y el otro de tipo numérico (valor 22).
Nombres de campos
Los nombres de campos distinguen mayúsculas y minúsculas, y solo pueden contener caracteres ASCII. Deben comenzar con una letra y pueden incluir letras, dígitos o guiones bajos. Los nombres de campo no pueden tener más de 500 caracteres de longitud.
Campos con valores múltiples
Un campo solo puede contener un valor, que debe coincidir con el tipo del campo. No es necesario que los nombres de campo sean únicos. Un documento puede tener múltiples campos con el mismo nombre y el mismo tipo, que es una forma de representar un campo con valores múltiples (sin embargo, no es posible repetir campos numéricos y de fecha con el mismo nombre). Un documento también puede contener múltiples campos con el mismo nombre y tipos distintos.
Tipos de campo
Existen tres tipos de campo que almacenan strings de caracteres java.lang.String
; nos referimos a ellos de manera colectiva como campos de string:
- Campo de texto: una string con una longitud máxima de 1,024**2 caracteres
- Campo HTML: una string de formato HTML con una longitud máxima de 1,024**2 caracteres
- Campo atómico: una string con una longitud máxima de 500 caracteres
También hay tres tipos de campo que almacenan datos no textuales:
- Campo numérico: un valor de punto flotante de doble precisión entre -2,147,483,647 y 2,147,483,647
- Campo de fecha:
java.util.Date
- Campo de punto geográfico: Es un punto del planeta descrito por coordenadas de latitud y longitud.
Los tipos de campo se especifican mediante las enumeraciones Field.FieldType
TEXT
, HTML
, ATOM
, NUMBER
, DATE
y GEO_POINT
.
Tratamiento especial de los campos de string y de fecha
Se aplica un tratamiento especial cuando se agrega a un índice un documento con campos HTML, de fecha o de texto. Es útil entender qué sucede internamente para usar la API de búsqueda de manera efectiva.
Asignación de tokens a campos de string
Cuando se indexa un campo HTML o de texto, se asignan tokens a su contenido. La string se divide en tokens interpretando como separadores los espacios o caracteres especiales (signos de puntuación, numeral, barra invertida, entre otros). El índice incluirá una entrada para cada token. Esto te permite buscar palabras clave y frases que correspondan a solo una parte del valor de un campo. Por ejemplo, la búsqueda de la palabra “dark” mostrará un documento con un campo de texto que contenga la string “it was a dark and stormy night”, y la búsqueda de la palabra “time” mostrará un documento con un campo de texto que contenga “this is a real-time system”.
En los campos HTML, el texto dentro de las etiquetas de lenguaje de marcado no contiene tokens, por lo que un documento con un campo HTML que contenga it was a <strong>dark</strong> night
coincidirá con la búsqueda “night”, pero no con “strong”. Si quieres ser capaz de buscar texto del lenguaje de marcado, almacénalo en un campo de texto.
No se realiza la asignación de tokens en los campos Atom. Un documento con un campo Atom que tenga el valor "bad weather" solo aparecerá en una búsqueda de la string completa "bad weather", pero no en las búsquedas de las palabras individuales "bad" o "weather".
Reglas de asignación de tokens
Los caracteres guión bajo (_) y et (&) no dividen las palabras en tokens.
Los siguientes caracteres de espacio en blanco siempre dividen las palabras en tokens: espacio, retorno de carro, salto de línea, tabulación horizontal, tabulación vertical, salto de página y carácter nulo.
Los siguientes caracteres se tratan como puntuación y dividen las palabras en tokens:
! " % ( ) * , - | / [ ] ] ^ ` : = > ? @ { } ~ $ Los caracteres de la siguiente tabla por lo general dividen las palabras en tokens, pero se pueden tratar de otra manera según el contexto en el que aparezcan:
Carácter Regla <
En un campo HTML, el signo "menor que" indica el principio de una etiqueta HTML que se ignora. +
Una string de uno o varios signos "más" se trata como parte de la palabra si aparece al final (C++). #
El símbolo "numeral" se trata como parte de la palabra si aparece después de las letras a, b, c, d, e, f, g, j o x (a# hasta g# son notas musicales; j# y x# son lenguajes de programación; c# entra en ambas categorías). Si a un término lo antecede este símbolo (#google), se lo trata como un hashtag y el numeral se convierte en parte de la palabra. '
El apóstrofo se considera una letra si antecede a la letra "s" seguida de un espacio, como en "John's hat". .
Si un punto decimal aparece entre dígitos, es parte de un número (es decir, el separador decimal). También puede ser parte de una palabra si se lo usa en una sigla (A.B.C). -
El guión medio es parte de una palabra si se usa en una sigla (I-B-M). Todos los demás caracteres de 7 bits que no sean letras ni dígitos ('A-Z', 'a-z', '0-9') se tratan como puntuación y dividen las palabras en tokens.
Cualquier otro elemento se analiza como carácter UTF-8.
Acrónimos
La asignación de tokens usa reglas especiales para reconocer acrónimos (strings como “I.B.M”, “a-b-c” o “C I A”). Un acrónimo es una string de caracteres alfabéticos individuales que tiene el mismo separador entre cada uno de los caracteres. Los separadores válidos son el punto, el guión medio o cualquier cantidad de espacios. El carácter separador se quita de la string cuando se asignan tokens a una sigla. Así, las strings de ejemplo mencionadas antes se convierten en los tokens "ibm", "abc" y "cia". El texto original permanece en el campo del documento.
Al trabajar con siglas, ten en cuenta lo siguiente:
- Una sigla no puede contener más de 21 letras. Una string de sigla válida de más de 21 caracteres se dividirá en una serie de siglas con 21 letras o menos cada una.
- Si las letras de una sigla están separadas por espacios, todas ellas deben estar en mayúscula o en minúscula. Las siglas que incluyen puntos o guiones medios pueden combinar mayúsculas y minúsculas.
- Al buscar una sigla, puedes ingresarla en su forma canónica (la string sin separadores) o bien con punto o guión medio (uno de los dos, no ambos) entre las letras. Por lo tanto, el texto "I.B.M" se puede recuperar mediante cualquiera de los siguientes términos de búsqueda: "I-B-M", "I.B.M" o "IBM".
Precisión de los campos de fecha
Cuando creas un campo de fecha en un documento, le asignas el valor java.util.Date
.
A fin de indexar y buscar el campo de fecha, se ignora cualquier componente de tiempo y la fecha se convierte en la cantidad de días a partir del día 1/1/1970 UTC. Esto significa que, aunque un campo de fecha puede contener un valor preciso de hora, una consulta de fecha solo puede especificar un valor de campo de fecha en el formato yyyy-mm-dd
. Esto también significa que no se define bien el orden de clasificación de los campos de fecha con la misma fecha.
Otras propiedades de los documentos
La clasificación de un documento es un número entero positivo que determina el orden predeterminado de los documentos que se muestran en una búsqueda. De manera predeterminada, la clasificación se configura cuando se crea el documento con el número de segundos transcurridos desde el 1 de enero de 2011. Puedes configurar la clasificación de manera explícita cuando creas un documento. No conviene que asignes la misma clasificación a muchos documentos y, en ninguna circunstancia, deberías asignar la misma clasificación a más de 10,000 documentos.
Si especificas las opciones de ordenamiento, puedes usar la clasificación como clave de ordenamiento. Ten en cuenta que, cuando se usa la clasificación en una expresión de orden o una expresión de campo, se hace referencia a ella como _rank
.
La propiedad de configuración regional especifica el idioma en el que se codifican los campos.
Visita la página de referencia de la clase Document
para obtener más detalles acerca de estos atributos.
Establece vínculos desde un documento a otros recursos
Puedes usar doc_id
y otros campos de un documento como vínculos a otros recursos en la aplicación. Por ejemplo, si usas Blobstore, puedes asociar el documento con un BLOB específico mediante la configuración del doc_id
o el valor de un campo Atom a la BlobKey de los datos.
Crea un documento
Para crear un documento, solicita un compilador nuevo mediante el método Document.newBuilder()
. Una vez que la aplicación tenga acceso a un compilador, puede especificar un identificador de documento opcional y agregar campos.
Los campos, al igual que los documentos, se generan mediante un compilador. El método Field.newBuilder()
muestra un compilador de campos que te permite especificar el nombre y el valor de un campo. El tipo de campo se asigna de forma automática cuando eliges un método de configuración específico. Por ejemplo, para indicar que un campo contiene texto sin formato, llama a setText()
.
El siguiente código crea un documento con campos que representan un saludo de un libro de visitas.
Para acceder a los campos dentro del documento, usa getOnlyField()
:
Trabaja con un índice
Agrega documentos a un índice
Cuando agregas documentos a un índice, este se copia en un almacenamiento persistente y cada uno de sus campos se indexa según su nombre, tipo y doc_id
.
En el siguiente ejemplo de código, se muestra cómo acceder a un índice y agregarle un documento. Estos son los pasos:
- Crea una
IndexSpec
. - Crea un
SearchService
. - Llama a
SearchService.getIndex()
para crear una instancia de índice. - Llama a
Index.put()
para agregar el documento al índice.
put()
. La agrupación en lotes de este método es más eficiente que agregar los documentos uno a la vez.
Cuando agregas un documento en un índice que ya contiene un documento con el mismo doc_id
, el nuevo reemplaza al antiguo. No se da ninguna advertencia. Puedes llamar a Index.get(id)
antes de crear o agregar un documento a un índice para verificar si ya existe un doc_id
específico.
Ten en cuenta que crear una instancia de la clase Index
no garantiza que exista un índice persistente. Los índices persistentes se crean la primera vez que les agregas un documento con el método put
.
Si deseas verificar si un índice existe antes de empezar a usarlo, emplea el método SearchService.getIndexes()
.
Actualiza documentos
Los documentos no pueden modificarse una vez agregados a un índice. No puedes agregar o quitar campos ni cambiar sus valores. Sin embargo, puedes reemplazar el documento con uno nuevo que tenga el mismo doc_id
.
Recupera documentos por doc_id
Existen dos formas de recuperar documentos de un índice mediante sus identificadores:- Usa
Index.get()
para recuperar un solo documento por sudoc_id
. - Usa
Index.getRange()
para recuperar un grupo de documentos consecutivos ordenados pordoc_id
.
Cada llamada se demuestra en el siguiente ejemplo.
Busca documentos por contenido
Para recuperar documentos de un índice, construye una cadena de consulta y llama a Index.search()
.
La cadena de consulta puede pasarse directamente como argumento, o puedes incluir la string en un objeto Query que se pasa como argumento.
De forma predeterminada, search()
muestra los documentos coincidentes ordenados de forma descendente según su clasificación. Para controlar cuántos documentos se muestran y cómo se ordenan, o agregar campos calculados a los resultados, necesitas usar un objeto Query
, que contendrá una cadena de consulta y también puede especificar otras opciones de búsqueda y ordenamiento.
Borra un índice
Cada índice consiste en sus documentos indexados y un esquema de índice. Para borrar un índice, borra todos los documentos que lo integran y, luego, el esquema de índice.
Puedes borrar documentos en un índice si especificas el doc_id
de uno o más documentos que desees borrar en el método delete()
.
Es conveniente borrar documentos por lotes para mejorar la eficiencia. Puedes pasarle hasta 200 ID de documentos a la vez al método delete()
.
delete()
. Borrar por lotes es más eficiente que borrar un documento a la vez.
Coherencia eventual
Cuando agregas, actualizas o borras un documento de un índice, el cambio se propaga entre múltiples centros de datos. Esto suele ocurrir rápido, pero el tiempo que demora puede variar. La API de búsqueda garantiza la coherencia eventual. Eso significa que, en algunos casos, la búsqueda o recuperación de uno o más documentos puede mostrar resultados que no reflejen los cambios más recientes.
Cómo determinar el tamaño de un índice
Un índice almacena documentos para su recuperación. Puedes recuperar un solo documento por su ID, un rango de documentos con ID consecutivos o todos los documentos de un índice. También puedes buscar en un índice para recuperar documentos que satisfagan determinados criterios en los campos y sus valores, especificados como una cadena de consulta. Puedes administrar grupos de documentos colocándolos en índices separados. No hay límite para el número de índices o de documentos por índice que puedes usar. El tamaño total de todos los documentos en un solo índice se limita a 10 GB de forma predeterminada, pero se puede aumentar hasta 200 GB si se envía una solicitud desde la página App Engine Search de la consola de Google Cloud. Mediante el método Index.getStorageLimit()
, se muestra el tamaño máximo permitido de un índice.
Index.getStorageUsage()
es una estimación de la cantidad de espacio de almacenamiento que usa un índice. Este número es una estimación porque el sistema de supervisión de índices no se ejecuta de forma continua; el uso real se computa de manera periódica. El storage_usage
se ajusta entre los puntos de muestreo, y se toman en cuenta los documentos agregados, pero no los eliminados.
Esquemas de índice
Cada índice tiene un esquema que muestra todos los nombres y tipos de campo de los documentos que contiene. No puedes definir un esquema por tu cuenta. Los esquemas se mantienen de manera dinámica; se actualizan a medida que se agregan documentos al índice. Un esquema simple puede verse así, en formato JSON:
{'comment': ['TEXT'], 'date': ['DATE'], 'author': ['TEXT'], 'count': ['NUMBER']}
Cada clave del diccionario es el nombre de un campo del documento. El valor de la clave es una lista de los tipos de campo en uso con ese nombre de campo. Si usaste el mismo nombre de campo con diferentes tipos de campo, el esquema mostrará más de un tipo de campo por nombre de campo:
{'ambiguous-integer': ['TEXT', 'NUMBER', 'ATOM']}
Una vez que un campo aparece en un esquema, no se lo puede quitar. No hay forma de borrar un campo, incluso si el índice ya no contiene ningún documento con ese nombre de campo.
Puedes ver los esquemas de tus índices de la siguiente forma: Ten en cuenta que una llamada aGetIndexes()
no puede mostrar más de 1,000 índices. Para recuperar más índices, llama al método repetidas veces mediante el setStartIndexName()
junto con GetIndexesRequest.Builder
.
Un esquema no define una “clase” como las que se usan en la programación de objetos. Con relación a la API de búsqueda, cada documento es único y los índices pueden contener diferentes tipos de documentos. Si quieres tratar colecciones de objetos con la misma lista de campos como instancias de una clase, deberás contemplar esa abstracción en tu código. Por ejemplo, podrías garantizar que todos los documentos con el mismo conjunto de campos se mantengan en su propio índice. El esquema de índice podría verse como la definición de la clase, y cada documento del índice sería una instancia de la clase.
Visualiza índices en la consola de Google Cloud
En la consola de Google Cloud, puedes ver información sobre los índices de tu aplicación y los documentos que contienen. Si haces clic en el nombre de un índice, se mostrarán los documentos que contiene. Verás todos los campos del esquema del índice. Por cada documento con un campo que tenga ese nombre, verás el valor del campo. También puedes realizar consultas sobre los datos del índice directamente desde la consola.
Cuotas de la API de búsqueda
La API de búsqueda incluye varias cuotas sin cargo:
Recurso o llamada a la API | Cuota gratuita |
---|---|
Almacenamiento total (índices y documentos) | 0.25 GB |
Consultas | 1,000 consultas por día |
Agregar documentos a los índices | 0.01 GB por día |
La API de búsqueda impone los siguientes límites a fin de garantizar la fiabilidad del servicio. Los límites se aplican por igual a las aplicaciones gratuitas y a las pagas:
Recurso | Cuota de seguridad |
---|---|
Uso máximo de consultas | 100 minutos agregados de tiempo de ejecución de consultas por minuto |
Máximo de documentos agregados o borrados | 15,000 por minuto |
Tamaño máximo por índice (se permite un número ilimitado de índices) | 10 GB |
El uso de API se calcula de distintas maneras según el tipo de llamada:
Index.search()
: Cada llamada a la API cuenta como una consulta; el tiempo de ejecución es equivalente a la latencia de la llamada.Index.put()
: Cuando agregas documentos a los índices, se tienen en cuenta el tamaño de cada documento y la cantidad en el cálculo de la cuota de indexación.- Todas las demás llamadas a la API de búsqueda se cuentan según el número de operaciones que involucran:
SearchService.getIndexes()
: Se cuenta 1 operación por cada índice mostrado o 1 operación si no se muestra nada.Index.get()
yIndex.getRange()
: Se cuenta 1 operación por cada documento mostrado o 1 operación si no se muestra nada.Index.delete()
: Se cuenta 1 operación por cada documento de la solicitud o 1 operación si la solicitud está vacía.
Se impone la cuota de capacidad de procesamiento de consultas para que ningún usuario individual pueda monopolizar el servicio de búsqueda. Debido a que las consultas se pueden ejecutar de manera simultánea, a cada aplicación se le permite ejecutar consultas que consuman hasta 100 minutos de ejecución por minuto de reloj. Si ejecutas muchas consultas cortas, probablemente no alcances este límite. Una vez que excedas la cuota, fallarán las consultas subsiguientes hasta que comience un nuevo período, cuando se restablecerá tu cuota. Los períodos de la cuota no son estrictamente de un minuto; se usa una variación del algoritmo de cubeta con goteo para controlar el ancho de banda de búsqueda en incrementos de cinco segundos.
Puedes obtener más información en la página Cuotas. Cuando una aplicación intenta exceder estas cantidades, se muestra un error de cuota insuficiente.
Ten en cuenta que, aunque estos límites se aplican por minuto, la consola muestra los totales diarios de cada uno. Los clientes con asistencia de nivel Plata, Oro o Platino pueden comunicarse con su representante de asistencia para solicitar el aumento de los límites de capacidad de procesamiento.
Precios de la API de búsqueda
Los siguientes cargos se aplican al uso cuando se superan las cuotas gratuitas:
Recurso | Costo |
---|---|
Almacenamiento total (índices y documentos) | $0.18 por GB por mes |
Consultas | $0.50 por 10,000 consultas |
Indexación de documentos que se pueden buscar | $2.00 por GB |
Para obtener más información, consulta la página Precios.