Índices y documentos

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 del conjunto de documentos en un solo índice está limitado a 10 GB de manera predeterminada, pero puede aumentarse hasta 200 GB mediante el envío de una solicitud desde la página de App Engine Search en Google Cloud Platform Console.

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:

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

Las opciones de consulta, como su nombre lo indica, no son obligatorias. 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 calculados" para documentos con FieldExpressions y campos de texto abreviados mediante fragmentos
  • Paginar los resultados de la búsqueda mostrando solo una parte de los documentos coincidentes en cada consulta (con desplazamientos y cursores)

Resultados de la búsqueda

Una llamada a search() solo puede mostrar un número limitado 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 búsqueda mediante cursores, o bien 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 manera predeterminada, la API de búsqueda genera automáticamente un doc_id cuando se crea un documento. También puedes especificar el doc_id tú mismo al crear un documento. Un doc_id debe contener solamente caracteres ASCII visibles y que se puedan imprimir (códigos ASCII del 33 al 126 inclusive), y no debe tener más de 500 caracteres en total. Un identificador de documento no puede empezar con un signo de exclamación ('!') ni puede empezar ni terminar con doble guion bajo ("__").

Aunque es conveniente crear identificadores de documento 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

Hay tres tipos de campos que almacenan strings de caracteres java.lang.String; nos referimos a ellos de manera colectiva como campos de strings:

  • 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: un objeto java.util.Date
  • Campo de punto geográfico: un punto del planeta descrito por sus coordenadas de latitud y longitud

Los tipos de campo se especifican mediante las enumeraciones de 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, una 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 una 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, no se asignan tokens al texto que está dentro de etiquetas de lenguaje de marcado; por lo tanto, si un campo HTML contiene it was a <strong>dark</strong> night, aparecerá en una búsqueda de "night", pero no en una búsqueda de "strong". Si quieres poder 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:

    Regla de caracteres
    < 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.

Siglas

En la asignación de tokens se usan reglas especiales para reconocer siglas (strings como "I.B.M.", "a-b-c" o "C I A"). Una sigla es una string de caracteres alfabéticos individuales con separadores entre cada carácter y el siguiente. 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, configuras su valor como un objeto java.util.Date. A fin de indexar el campo de fecha y hacer búsquedas en él, se ignora cualquier componente de hora y la fecha se convierte al número de días desde el 1/1/1970 UTC. Eso significa que, aunque un campo de fecha puede contener un valor de hora preciso, las consultas de fecha solo pueden especificar valores de campo de fecha en la forma 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 ordenamiento 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.

Cómo establecer vínculos desde un documento a otros recursos

Puedes usar el doc_id de un documento y otros campos como vínculos a otros recursos de tu aplicación. Por ejemplo, si usas Blobstore, puedes asociar el documento con un BLOB específico mediante la asignación del doc_id o el valor de un campo Atom a la BlobKey de los datos.

Cómo crear un documento

Para crear un documento, solicita un compilador nuevo con el método Document.newBuilder(). Una vez que la aplicación tenga acceso a un creador, puede especificar un identificador de documento opcional y agregar campos.

Los campos, al igual que los documentos, se generan mediante un creador. 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.

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 acceder a los campos dentro del documento, usa 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();

Cómo trabajar con un índice

Cómo agregar documentos a un índice

Cuando agregas un documento en un índice, se copia en un almacén persistente y cada uno de sus campos se indexa según su nombre, tipo y doc_id.

El siguiente ejemplo de código muestra cómo acceder a un índice y agregarle un documento. Estos son los pasos:

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;
  }
}
Puedes pasarle hasta 200 documentos a la vez al método put(). La agrupación en lotes de este método es más eficiente que agregar los documentos uno a la vez.

Cuando pones un documento en un índice que ya contiene un documento con el mismo doc_id, el documento 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 quieres verificar si un índice existe antes de empezar a usarlo, emplea el método SearchService.getIndexes().

Cómo actualizar documentos

Los documentos no pueden modificarse una vez agregados a un índice. No puedes agregar o quitar campos, ni cambiar el valor de un campo. Sin embargo, puedes reemplazar el documento por uno nuevo que tenga el mismo doc_id.

Cómo recuperar documentos por doc_id

Hay dos formas de recuperar documentos de un índice mediante sus identificadores:
  • Usar Index.get() para recuperar un solo documento por su doc_id
  • Usar Index.getRange() para recuperar un grupo de documentos consecutivos ordenados por doc_id

Las llamadas correspondientes se demuestran en el siguiente ejemplo.

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

Cómo buscar 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 bien puedes incluir la string en un objeto Query que se pasa como argumento. De manera 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.

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

Cómo borrar 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.

A fin de borrar documentos de un índice, especifica el doc_id de uno o más documentos en el método delete(). Es conveniente borrar documentos por lotes para mejorar la eficiencia. Puedes pasarle al método delete() hasta 200 ID de documentos a la vez.

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);
}
Puedes pasarle al método delete() hasta 200 documentos a la vez. 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 del conjunto de documentos en un solo índice está limitado a 10 GB de manera predeterminada, pero puede aumentarse hasta 200 GB mediante el envío de una solicitud desde la página de App Engine Search en Google Cloud Platform Console. El método Index.getStorageLimit() muestra el tamaño máximo permitido de un índice.

El método 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 continuamente; el uso real se computa de manera periódica. storage_usage se ajusta entre puntos de muestreo, y se toman en cuenta los documentos agregados, pero no los borrados.

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:

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());
    }
  }
}
Ten en cuenta que una llamada a GetIndexes() no puede mostrar más de 1,000 índices. Para recuperar más índices, repite la llamada al método con setStartIndexName() y 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.

Cómo ver índices en Google Cloud Platform Console

En GCP Console, puedes ver información acerca de 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 sin cargo
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. Estos límites aplican por igual a las aplicaciones gratuitas y las pagadas:

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 la API se calcula de maneras distintas según el tipo de llamada:

  • Index.search(): Cada llamada a la API se 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 el número de documentos 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(), Index.getRange(): en ambos casos 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 incluido en 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

Si habilitas la facturación para tu app, se te cobrará por el uso que supere las cuotas sin cargo. Se aplican los siguientes cargos a las aplicaciones facturadas:

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.

¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...

Entorno estándar de App Engine para Java 8