Skip to content

GOQL.es

Doug Blank edited this page Oct 5, 2026 · 3 revisions

🌐 English · Deutsch · Français · 简体中文

GOQL

Cada vista de lista de Gramps Connect (Personas, Familias, Eventos, etc.) tiene un cuadro de búsqueda. Por defecto, escribir en él hace una coincidencia rápida de texto simple sobre unos pocos campos comunes de esa vista (para Personas: ID de Gramps y nombre; para Lugar: ID, título y nombre del lugar; etc.), suficiente para la mayoría de las búsquedas cotidianas, y es el comportamiento por defecto porque es más sencillo que un lenguaje de consulta.

Junto al cuadro hay una casilla: «Usar Gramps Object Query Language». Actívela y ese mismo cuadro acepta en cambio una condición GOQL completa (gramps-object-query-language): un pequeño lenguaje creado específicamente para consultar registros de Gramps, capaz de cosas que la coincidencia de texto simple no puede ni rozar: recorrer relaciones (una unión o join, en términos de bases de datos: «todas las familias en las que la madre y el padre comparten apellido») e incluso recorrerlas hacia atrás («todas las notas a las que ya nada apunta»). Esta página es para cualquiera que quiera escribir una de esas condiciones; no hace falta experiencia en programación.

GOQL es también lo que hay detrás de las llamadas filter()/db. get_number_of() de los Gramplets y del argumento where= de people()/families()/etc.: la misma sintaxis que escribiría en el cuadro de búsqueda con la casilla activada también funciona desde el código Python de un complemento. (db.get_number_of() y el antiguo count() a secas son funciones de la API de Gramplets para contar registros coincidentes, no el any/len propio de GOQL que se explica más abajo: cosas distintas con el mismo nombre.)

¿No quiere escribirlo a mano en absoluto? El botón «Filtros» junto al título de cada lista ofrece decenas de reglas comunes como simples casillas (combinables con AND/OR, anidables y negables) y construye exactamente el mismo GOQL por debajo, mostrado en solo lectura para que vea lo que ha escrito. Consulte la lista de funciones de la página Descripción general para más detalles. Su «+ Nueva regla personalizada…» es el único lugar dentro de ese diálogo que todavía acepta una condición GOQL escrita a mano, con la misma sintaxis que toda esta página; una vez guardada y con nombre, se reutiliza como una casilla normal a partir de entonces, sin volver a escribirla nunca.

La idea básica

Una consulta siempre se escribe en el contexto de una vista (Persona, Familia, Evento, etc.) y es solo una condición:

surname == 'Smith'

Se lee así: en la vista Personas, buscar los registros cuyo apellido sea igual a 'Smith'. La vista en la que busca determina a qué tipo de registro se aplica la condición.

Unos pocos símbolos aparecen una y otra vez:

Símbolo Significa
==, != es igual a / no es igual a
<, <=, >, >= es menor que, como máximo, mayor que, como mínimo (anterior/posterior, más pequeño/más grande)
and, or, not combinan condiciones, o invierten una
in [ ... ] coincide con cualquiera de una lista de valores
'text' in field coincide si field contiene 'text' en cualquier parte
like(field, 'pattern') coincide con un patrón de texto, donde % significa «cualquier cosa»
regex(field, 'pattern') coincide con una expresión regular, para quienes ya las conocen

Los valores de texto van entre comillas simples ('Smith'); los números no (1968).

Combinar condiciones

and exige que ambos lados sean verdaderos; or solo necesita uno. Ambos se leen con naturalidad: gender == Person.MALE and surname == 'Smith' encuentra a todos los hombres apellidados Smith, mientras que given_name == 'John' or surname == 'Doyle' encuentra a todos los llamados John o a cualquiera apellidado Doyle. Mezclar ambos funciona igual que en la aritmética corriente, donde la multiplicación se hace antes que la suma (and se evalúa antes que or), pero los paréntesis dejan clara la intención en cualquier caso: (gender == Person.MALE and surname == 'Smith') or given_name == 'Mary' encuentra a todos los varones Smith, más a cualquiera llamada Mary, sea del sexo que sea.

not invierte una condición y coincide siempre que lo que contiene no sea verdadero: not (surname == 'Smith') son todas las personas cuyo apellido no es Smith. Las comparaciones también se pueden encadenar, como permite el Python real: Date('Jan 1, 1900') < birth.date.sortval < Date('Jan 1, 1950') encuentra a todas las personas nacidas estrictamente entre esas dos fechas, en una línea en lugar de dos unidas con and.

Coincidencias de texto

Además del == simple, unas cuantas formas más flexibles de comparar texto cubren la mayoría de las búsquedas cotidianas. given_name in ['John', 'Jane'] coincide con cualquier nombre de la lista; añada tantos como quiera. 'an' in given_name coincide en cualquier parte del nombre (Jane, Alexander, Susan, ...) sin necesidad de comodines. like(field, 'J%') coincide con un patrón en el que % significa «cualquier cosa», así que like(given_name, 'J%') coincide con John, Jane, James, etc. Para quien ya se maneje con expresiones regulares, regex(field, 'pattern') es aún más potente: regex(surname, '^[SD]') encuentra de una vez a todos los Smith y Doyle, algo que like(...) no puede expresar sin escribir una condición aparte para cada letra inicial.

Llegar a registros relacionados

Un campo no tiene por qué estar directamente en el registro que está buscando. birth y death van de una persona a su evento de nacimiento o de defunción, así que birth.date.sortval es la fecha de ese evento, y birth.place.title da un paso más, hasta el lugar de ese evento. La misma idea funciona con otros tipos de registro: father y mother van de una familia al registro Persona de cada progenitor (father.surname == 'Smith'), source va de una cita a lo que cita, y enclosed_by va de un lugar al lugar que lo contiene (el condado de una ciudad, por ejemplo; y se encadena consigo mismo, así que enclosed_by.enclosed_by.title sube dos niveles).

Los dos lados de una comparación pueden ser una de estas rutas, no solo un valor fijo, que es lo que hace posible una consulta como «familias en las que la madre y el padre comparten apellido»: father.surname == mother.surname. Encadenar y comparar se pueden combinar libremente: father.birth.date.sortval < Date('Jan 1, 1850') avanza dos pasos desde una familia (hasta el padre y luego hasta su evento de nacimiento) para encontrar generaciones anteriores sin saber de antemano quiénes son.

No hay límite al número de saltos que puede dar una ruta, y cada salto puede llegar a un tipo de registro totalmente distinto. Partiendo de la vista Familias, father.birth.place.title == 'Chicago, Cook, Illinois, USA' va de la familia al registro Persona del padre, de ahí a su Evento de nacimiento y de ahí al Lugar de ese evento: tres saltos por tres tipos de registro distintos, terminando en el nombre del lugar, todo en una línea. El mismo encadenamiento funciona desde cualquier vista, en cualquier combinación que permitan las relaciones anteriores.

Colecciones: any y len

Una persona tiene exactamente un evento de nacimiento, pero cualquier número de hijos, notas, citas u objetos multimedia asociados: eso necesita otro tipo de comprobación. any(c.given_name == 'Steve' for c in children) coincide con una familia si alguno de los hijos cumple la condición; omitir la condición (any(n for n in notes)) solo pregunta si hay algo asociado, que es como not any(n for n in notes) encuentra a las personas sin ninguna nota registrada. len(...) pregunta «cuántos» en lugar de «al menos uno»: len([c for c in children]) > 2 encuentra familias con más de dos hijos, y len([c for c in children if c.gender == Person.MALE]) > 1 lo restringe a contar solo los hijos varones.

Sin embargo, una colección es lo más lejos que puede llegar una consulta en esa dirección. El encadenamiento descrito más arriba (father.birth.place.title) solo funciona a través de relaciones que conectan con exactamente un registro: any/len pueden decirle si algo dentro de una colección cumple una condición, pero la consulta no puede seguir encadenando más allá de esa coincidencia para llegar a uno de sus propios registros relacionados. El padre de una familia tiene su propia familia de origen, por ejemplo, pero como una persona puede figurar como hijo de más de una familia, ese vínculo también es una colección, un paso más de lo que una consulta puede seguir hoy. Así que «todas las familias cuyo padre y abuelo nacieron en el mismo condado» no es algo que GOQL pueda expresar todavía.

Referencias inversas: backlinks

Todas las colecciones anteriores van hacia fuera: los propios hijos de una familia, las propias notas de una persona. backlinks es la única colección que va en sentido contrario: ¿hay algo más en el árbol que apunte a este registro? Está disponible en los diez tipos de registro, incluso en los que no tienen colecciones propias hacia fuera (una Etiqueta no tiene hijos ni notas propias, pero muchos registros pueden estar etiquetados, así que la condición backlinks de Tag encuentra etiquetas que nada usa):

not any(bl for bl in backlinks)                                    # nada hace referencia a este registro
any(bl._class == 'Person' for bl in backlinks)                     # al menos una Persona hace referencia a él
any(bl for bl in backlinks) and len([bl for bl in backlinks]) > 1  # referenciado por más de una cosa

Esto es lo que hace posible una consulta como «todas las fuentes que ya nadie cita» o «todos los lugares sin ningún evento registrado en ellos»: not any(bl for bl in backlinks), ejecutado en la vista Fuentes o Lugares.

_class es el único campo que puede comprobar una condición backlinks: el tipo de registro del que hace la referencia ('Person', 'Family', ...). Como quien hace la referencia en un retroenlace puede ser cualquiera de los diez tipos de registro a la vez, _class no se puede seguir más a fondo como una relación normal (no existe _class.primary_name), pero sí acepta in/not in con una lista:

any(bl._class in ['Person', 'Family'] for bl in backlinks)
any(bl._class != 'Media' for bl in backlinks)

len([bl for bl in backlinks]) funciona exactamente igual que el len(...) anterior.

Fechas

Date('...') entiende texto de fecha corriente, así que birth.date.sortval >= Date('Jan 1, 1968') encuentra a todas las personas nacidas ese día o después. Algo que conviene saber: sortval es siempre un único punto en el tiempo, sin ningún calificativo de «hacia», «antes de» o «estimado»: una fecha de nacimiento introducida como «antes de 1968» tiene el mismo sortval que un simple «Jan 1, 1968», así que una comparación >= contaría a esa persona como nacida en la fecha límite o después, aunque «antes de» signifique lo contrario. Cuando esa distinción importe, compare birth.date.modifier con una constante con nombre, como Date.MOD_ABOUT.

Constantes con nombre

Algunos campos, como el sexo de una persona o la confianza de una cita, se comparan con una constante con nombre en lugar de con un número en bruto (gender == Person.MALE o confidence >= Citation.CONF_HIGH), tomada directamente del propio Gramps para que nunca se desincronice de lo que Gramps realmente guarda.

Lo que GOQL no puede hacer

GOQL es un conjunto pequeño y fijo de piezas, no un lenguaje de programación completo: todo lo que quede fuera de los patrones anteriores se rechaza con un error en lugar de intentar adivinarlo.

Más información

Esta página cubre los patrones cotidianos; el botón «i» del propio cuadro de búsqueda, junto al campo de búsqueda de cada vista, enumera los campos y relaciones exactos disponibles en esa vista. Para la referencia completa de la sintaxis (cada operador, relación, colección y caso límite, además del razonamiento detrás de cada uno), consulte where_expr.md en el repositorio gramps-object-query-language, el proyecto sobre el que se construye GOQL.

Clone this wiki locally