Skip to content

GOQL.fr

Doug Blank edited this page Sep 22, 2026 · 1 revision

🌐 English · Deutsch

GOQL

Chaque vue de liste dans Gramps Connect — Individus, Familles, Événements, et ainsi de suite — a un champ de recherche. Par défaut, y taper effectue une correspondance rapide en texte simple sur la poignée de champs courants de cette vue (pour Individus : ID Gramps et nom ; pour Lieu : ID, titre et nom du lieu ; et ainsi de suite) — suffisant pour la plupart des recherches quotidiennes, et le comportement par défaut parce que c'est plus simple qu'un langage de requête.

À côté du champ se trouve une case à cocher : « Utiliser le Gramps Object Query Language ». L'activer, et ce même champ accepte à la place une condition GOQL complète (gramps-object-query-language) — un petit langage construit spécifiquement pour interroger les fiches de Gramps, capable de choses qu'une correspondance en texte simple ne peut absolument pas toucher : atteindre à travers des relations (une jointure, en termes de base de données — « chaque famille dont la mère et le père partagent un nom de famille »), et même remonter à travers elles en sens inverse (« chaque note vers laquelle plus rien ne pointe »). Cette page s'adresse à quiconque veut écrire une de ces conditions — aucune expérience de programmation nécessaire.

GOQL est aussi ce qui fait fonctionner les appels filter()/db.get_number_of() des Gramplets et l'argument where= de people()/families()/etc. — la même syntaxe que l'on taperait dans le champ de recherche avec la case cochée fonctionne aussi depuis le code Python d'une extension. (db. get_number_of()/l'ancien count() tout court sont des fonctions de l'API Gramplet pour compter les fiches correspondantes, pas le any/len propre à GOQL couvert plus bas — une chose différente, le même nom.)

Vous ne voulez pas du tout l'écrire à la main ? Le bouton « Filtres » à côté du titre de chaque liste propose des dizaines de règles courantes sous forme de simples cases à cocher — combinables avec ET/OU, imbricables, et niables — et construit dessous exactement le même GOQL, affiché en lecture seule pour voir ce qu'il a écrit. Voir la liste des fonctionnalités de la page Aperçu à ce sujet. Son « + Nouvelle règle personnalisée… » est le seul endroit dans cette boîte de dialogue qui accepte encore une condition GOQL brute à la main, même syntaxe que toute cette page — une fois enregistrée et nommée, elle est réutilisée comme une case à cocher ordinaire à partir de là, jamais retapée.

L'idée de base

Une requête s'écrit toujours dans le contexte d'une vue — Individu, Famille, Événement, et ainsi de suite — et n'est qu'une seule condition :

surname == 'Smith'

Cela se lit comme : dans la vue Individu, trouver les fiches où le nom de famille est égal à 'Smith'. La vue dans laquelle on cherche fixe le type de fiche auquel la condition s'applique.

Quelques symboles reviennent sans cesse :

Symbole Signification
==, != est égal à / est différent de
<, <=, >, >= est inférieur à, au plus, supérieur à, au moins (plus tôt/plus tard, plus petit/plus grand)
and, or, not combiner des conditions, ou en inverser une
in [ ... ] correspond à l'une quelconque d'une liste de valeurs
'texte' in field correspond si field contient 'texte' n'importe où
like(field, 'motif') correspond à un motif de texte, où % signifie « n'importe quoi »
regex(field, 'motif') correspond à une expression régulière, pour ceux qui les connaissent déjà

Les valeurs textuelles vont entre apostrophes simples ('Smith') ; pas les nombres (1968).

Combiner des conditions

and exige que les deux côtés soient vrais ; or n'en a besoin que d'un. Les deux se lisent naturellement : gender == Person.MALE and surname == 'Smith' trouve chaque homme nommé Smith, tandis que given_name == 'John' or surname == 'Doyle' trouve tous ceux nommés John ou n'importe qui nommé Doyle. Mélanger les deux fonctionne comme l'arithmétique ordinaire, où la multiplication passe avant l'addition — and est vérifié avant or — mais les parenthèses rendent l'intention claire dans tous les cas : (gender == Person.MALE and surname == 'Smith') or given_name == 'Mary' trouve chaque Smith masculin, plus quiconque nommé Mary de n'importe quel genre.

not inverse une condition, correspondant chaque fois que ce qu'elle contient n'est pas vrai : not (surname == 'Smith') désigne tous ceux dont le nom de famille n'est pas Smith. Les comparaisons peuvent aussi être enchaînées, comme le permet le vrai Python — Date('Jan 1, 1900') < birth.date.sortval < Date('Jan 1, 1950') trouve tous ceux nés strictement entre ces deux dates, en une ligne au lieu de deux jointes par and.

Correspondance de texte

Au-delà d'un simple ==, quelques façons plus souples de faire correspondre du texte couvrent la plupart des recherches quotidiennes. given_name in ['John', 'Jane'] correspond à n'importe quel nom de la liste — en ajouter autant que voulu. 'an' in given_name correspond n'importe où dans le nom (Jane, Alexander, Susan, …) sans avoir besoin de caractères génériques. like(field, 'J%') correspond à un motif où % signifie « n'importe quoi », like(given_name, 'J%') correspond donc à John, Jane, James, et ainsi de suite. Pour ceux déjà à l'aise avec les expressions régulières, regex(field, 'motif') est encore plus puissant — regex(surname, '^[SD]') trouve chaque Smith et Doyle d'un coup, quelque chose que like(...) ne peut pas exprimer sans écrire une condition séparée pour chaque lettre de départ.

Atteindre des fiches liées

Un champ n'a pas besoin de résider directement sur la fiche recherchée. birth et death mènent d'une personne à son événement de naissance ou de décès, birth.date.sortval est donc la date de cet événement, et birth.place.title va un pas plus loin, jusqu'au lieu de cet événement. La même idée fonctionne sur d'autres types de fiches : father et mother mènent d'une famille au propre fiche Individu de chaque parent (father.surname == 'Smith'), source mène d'une citation à ce qu'elle cite, et enclosed_by mène d'un lieu au lieu qui l'englobe (le comté d'une ville, par exemple — et cela s'enchaîne avec lui-même, enclosed_by.enclosed_by.title remonte donc de deux niveaux).

Les deux côtés d'une comparaison peuvent être l'un de ces chemins, pas seulement une valeur fixe, ce qui rend possible une requête comme « familles où la mère et le père partagent un nom de famille » : father.surname == mother.surname. Enchaînement et comparaison se combinent librement — father.birth.date.sortval < Date('Jan 1, 1850') fait deux pas depuis une famille (vers le père, puis vers son événement de naissance) pour trouver des générations plus anciennes sans savoir à l'avance qui elles sont.

Il n'y a pas de limite au nombre de ces sauts qu'un chemin peut traverser, et chaque saut peut atterrir sur un type de fiche entièrement différent. En partant de la vue Famille, father.birth.place.title == 'Chicago, Cook, Illinois, USA' mène de la famille au propre Individu du père, de là à son Événement de naissance, et de là au Lieu de cet événement — trois sauts à travers trois types de fiches différents, se terminant au nom du lieu, tout en une seule ligne. Le même enchaînement fonctionne depuis n'importe quelle vue, dans toute combinaison que les relations ci-dessus permettent.

Collections : any et len

Une personne a exactement un événement de naissance, mais un nombre quelconque d'enfants, de notes, de citations, ou de médias attachés — ceux-là ont besoin d'un autre type de vérification. any(c.given_name == 'Steve' for c in children) correspond à une famille si un enfant quelconque satisfait la condition ; omettre la condition (any(n for n in notes)) demande simplement si quelque chose du tout est attaché, ce qui est comment not any(n for n in notes) trouve les personnes sans note enregistrée. len(...) demande « combien » plutôt que « au moins un » — len([c for c in children]) > 2 trouve les familles avec plus de deux enfants, et len([c for c in children if c.gender == Person.MALE]) > 1 restreint cela au compte des fils seulement.

Une collection est cependant aussi loin qu'une seule requête peut aller dans cette direction. L'enchaînement décrit ci-dessus (father.birth.place.title) ne fonctionne qu'à travers des relations qui mènent à exactement une fiche — any/len peuvent dire si quelque chose à l'intérieur d'une collection correspond à une condition, mais la requête ne peut alors pas continuer à enchaîner au-delà de cette correspondance pour atteindre l'une de ses propres fiches liées. Le père d'une famille a sa propre famille parentale, par exemple — mais puisqu'une personne peut être enregistrée comme enfant de plus d'une famille, ce lien est aussi une collection, un pas de plus qu'une requête ne peut actuellement suivre. « Chaque famille dont le père et le grand-père sont nés dans le même comté » n'est donc pas quelque chose que GOQL peut encore exprimer.

Références inverses : backlinks

Chaque collection ci-dessus mène vers l'extérieur — les propres enfants d'une famille, les propres notes d'une personne. backlinks est la seule collection qui mène dans l'autre sens : quelque chose d'autre dans l'arbre pointe-t-il vers cette fiche ? Elle est disponible depuis chacun des dix types de fiches, même ceux sans propres collections vers l'extérieur (une Étiquette n'a pas d'enfants ni de notes propres, mais beaucoup de fiches peuvent être étiquetées, la propre condition backlinks de Tag trouve donc les étiquettes que rien n'utilise) :

not any(bl for bl in backlinks)                                    # rien ne référence cette fiche du tout
any(bl._class == 'Person' for bl in backlinks)                     # au moins un Individu la référence
any(bl for bl in backlinks) and len([bl for bl in backlinks]) > 1  # référencée par plus d'une chose

C'est ce qui rend possible une requête comme « chaque source que plus personne ne cite » ou « chaque lieu sans événement enregistré à cet endroit » — not any(bl for bl in backlinks), exécutée dans la vue Source ou Lieu.

_class est le seul champ qu'une condition backlinks peut tester — le propre type de fiche du référent ('Person', 'Family', …). Puisque le référent d'un rétro-lien peut être n'importe lequel des dix types de fiches à la fois, _class ne peut pas être suivi plus profondément comme le permettrait une relation normale — il n'y a pas de _class.primary_name — mais il accepte in/not in contre une liste :

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

len([bl for bl in backlinks]) fonctionne exactement comme len(...) ci-dessus.

Dates

Date('...') comprend le texte de date ordinaire, `birth.date.sortval

= Date('Jan 1, 1968')trouve donc tous ceux nés à cette date ou après. Une chose à savoir :sortvalest toujours un point unique dans le temps, sans qualificatif « vers », « avant » ou « estimé » attaché — une date de naissance saisie comme « avant 1968 » a le *même*sortvalque le simple « Jan 1, 1968 », une comparaison>=compterait donc cette personne comme née à cette date ou après, même si « avant » signifie le contraire. Quand cette distinction compte, comparer plutôtbirth.date.modifierà une constante nommée, commeDate.MOD_ABOUT`.

Constantes nommées

Certains champs, comme le genre d'une personne ou la confiance d'une citation, se comparent à une constante nommée plutôt qu'à un nombre brut — gender == Person.MALE ou confidence >= Citation.CONF_HIGH — tirées directement de Gramps lui-même pour qu'elles ne dérivent jamais de ce que Gramps stocke réellement.

Ce que GOQL ne peut pas faire

GOQL est un petit ensemble fixe de briques de base, pas un langage de programmation complet — tout ce qui sort des motifs ci-dessus est rejeté avec une erreur plutôt que deviné.

En savoir plus

Cette page couvre les motifs quotidiens ; le propre bouton « i » du champ de recherche, à côté du champ de recherche de chaque vue, liste les champs et relations exacts disponibles pour cette vue. Pour la référence de syntaxe complète — chaque opérateur, relation, collection et cas particulier, avec le raisonnement derrière chacun — voir where_expr.md dans le dépôt gramps-object-query-language, le projet sur lequel GOQL est construit.

Clone this wiki locally