Query Language

Query Language

Pedigree Forge includes a SQL-like query language for searching and filtering your genealogy data. It provides a powerful alternative to the built-in canned queries, allowing you to ask custom questions about your database without writing Lua scripts.

Queries can be entered in the Queries panel (Tools > Queries) by selecting the text editor mode.

Basic syntax
SELECT surname, given_names, birth.date AS "Born"
FROM persons
WHERE birth.date.year > 1800
ORDER BY surname

A query has four main clauses:

ClausePurpose
SELECTWhich columns to show
FROMWhich type of record to search
WHEREFilter conditions (optional)
ORDER BYSort order (optional)

Two additional optional clauses:

ClausePurpose
DESCRIPTIONA human-readable name (appears before SELECT)
LIMITMaximum number of result rows
Tables (FROM clause)

The FROM clause specifies which type of record to query:

TableRoot typeDescription
personspersonAll people in the database
familiesfamilyAll families (couples)
factsfactAll facts and events
citationsfactCitation facts
sourcessourceAll source records
archivesarchiveAll archive records
placesplaceAll places
Column expressions (SELECT clause)

Columns are specified using dot-notation paths that navigate the record structure. The root type (determined by the FROM clause) is omitted from paths:

– When FROM persons, "birth.date" means "person.birth.date"
SELECT surname, given_names, birth.date, death.date
FROM persons
Aliases

Use AS to give a column a display name:

SELECT surname AS "Family Name", birth.date AS "Born"
FROM persons
COALESCE

Use COALESCE() to provide fallback values — common in genealogy where you might have a baptism but not a birth:

SELECT name, COALESCE(birth.date, baptism.date) AS "Born/Baptised"
FROM persons
SELECT *

Use SELECT * to include a default set of columns for the table:

TableDefault columns
personsid, name, sex, birth.date, birth.place.name, death.date, death.place.name
familiesid, names, marriage.date, marriage.place.name, status
factsowner.name, label, date, place.name, address
citationsdescription, page, source.title
sourcesid, title, author, archive.name
archivesid, name, town, county
placesname, town, county, country

Facts have two fields for the event type: label returns a human-readable name ("Birth", "Census", "Baptism"), while kind returns the raw GEDCOM tag ("BIRT", "CENS", "BAPM"). Use label in SELECT for display and kind in WHERE for filtering:

SELECT label AS "Event", date, place
FROM facts
WHERE kind = ’census’
DISTINCT

Use SELECT DISTINCT to deduplicate results — useful for data discovery:

SELECT DISTINCT trade
FROM persons
WHERE trade IS NOT NULL
ORDER BY trade
Filter conditions (WHERE clause)
Comparison operators
OperatorExample
=, !=surname = ’Darwin’
<, >, <=, >=birth.date.year > 1800
BETWEEN…ANDbirth.date.year BETWEEN 1800 AND 1900
INbirth.place.county IN (’Kent’, ’Sussex’, ’Surrey’)
IS NULLdeath.date IS NULL
IS NOT NULLbirth.date IS NOT NULL
WITHIN…OFbirth.place WITHIN 10 MILES OF ’Canterbury, Kent’
String matching
OperatorDescription
CONTAINSCase-insensitive substring match
STARTS_WITHMatches the start of a value
ENDS_WITHMatches the end of a value
SELECT name, trade
FROM persons
WHERE trade CONTAINS ’smith’
Phonetic matching

These operators help find variant spellings — particularly useful for surnames, which were often recorded inconsistently:

OperatorDescription
SOUNDS_LIKEBroad phonetic match (Soundex) — ’Smith’ matches Smyth, Smythe
PHONETICALLY_LIKETighter phonetic match (Double Metaphone) — fewer false positives
SIMILAR_TOFuzzy match (Levenshtein distance) — catches typos and OCR errors
SELECT name, birth.date
FROM persons
WHERE surname SOUNDS_LIKE ’Smith’
Spatial queries

Find people associated with places near a given location:

SELECT name, birth.place.name AS "Birthplace"
FROM persons
WHERE birth.place WITHIN 10 MILES OF ’Canterbury, Kent’

Units can be MILES or KM. Places without coordinates are excluded from the results.

Logical operators

Combine conditions with AND, OR, and NOT. Use parentheses for grouping:

SELECT name, birth.date, death.date
FROM persons
WHERE birth.place.county = ’Kent’
AND birth.date.year > 1800
AND (death.date IS NULL OR death.date.year > 1850)
Sorting (ORDER BY clause)

Sort by one or more paths, with optional ASC (ascending, the default) or DESC (descending):

SELECT surname, given_names, birth.date
FROM persons
ORDER BY surname ASC, birth.date DESC

Sorting is type-aware — each field sorts in a way that makes sense for its data type. Dates sort chronologically (including non-Gregorian calendars), ages sort by total duration, and numeric sub-fields like year, month, and day sort as numbers rather than text. See the Sorts as column in the Data Expressions field reference for details. Empty or missing values always sort last, regardless of ASC or DESC.

Absence queries

A common research question is "who is missing X?" The path system supports .count on filtered collections:

– People with no 1881 census
SELECT name, birth.date
FROM persons
WHERE facts[kind=census & year=1881].count = 0

– People with no death record who are not flagged as living
SELECT name, birth.date
FROM persons
WHERE facts[kind=death].count = 0 AND living = ’false’
Parameters

Queries can include @parameter placeholders in WHERE values, turning saved queries into reusable templates with input fields. Use a DECLARE block before SELECT to declare the parameter name, type, and optional label and default value:

DESCRIPTION ’Facts near a place’
DECLARE
@place PLACE LABEL ’Near’;
@radius NUMBER LABEL ’Miles’ DEFAULT 5;
SELECT owner.name, label AS "Event", date, place.name
FROM facts
WHERE place WITHIN @radius MILES OF @place
ORDER BY date

Each declaration has the form:

@name TYPE [LABEL ’display text’] [DEFAULT value];
Parameter types
TypeInput
TEXTPlain text field
NUMBERNumeric field
DATEDate field with calendar picker
PLACEPlace field with place search
PERSONPerson name field with autocomplete
SEXChoice of M or F

The type determines the input control shown in the Queries panel when the query is selected. Place and date fields include the same entry assistants used elsewhere in the application.

Undeclared parameters

A @parameter used in WHERE but not listed in a DECLARE block is treated as TEXT with no default. This keeps simple ad-hoc queries lightweight:

SELECT * FROM persons WHERE surname CONTAINS @name
Lua and MCP

Parameters can be supplied from Lua scripts and MCP tool calls. See Scripting API and the query MCP tool for details.

Comments

Use for line comments:

– Find people born in Kent after 1800
SELECT name, birth.date
FROM persons
WHERE birth.place.county = ’Kent’ – restrict to Kent
AND birth.date.year > 1800
Examples
People born in a county
DESCRIPTION ’People born in Kent’
SELECT surname, given_names, birth.date AS "Born",
birth.place.name AS "Birthplace"
FROM persons
WHERE birth.place.county = ’Kent’
ORDER BY surname, given_names
Sources from a particular archive
SELECT title, archive.name AS "Repository"
FROM sources
WHERE archive.name CONTAINS ’National’
ORDER BY title
Census facts for a specific year
SELECT label AS "Event", date, place.name, owner.name AS "Person"
FROM facts
WHERE kind = ’census’ AND date.year = 1881
ORDER BY owner.name
Editable results

Query results are displayed in an editable grid. Where the underlying data supports it, you can edit values directly in the results — the changes are written back to the database. This makes the query language an efficient tool for bulk data review and correction.

Script queries

In addition to query-language queries, the Queries panel also shows Lua script queries (prefixed with "Script:") when @query scripts are installed. See Scripts for how to write query scripts and where to place them.

Saving queries to disk

Saved queries are loaded from .pql text files. Pedigree Forge searches two directories:

  1. Bundled queries — the queries folder alongside the application.
  2. User queries%APPDATA%\Pedigree Forge\queries on Windows (or ~/Library/Application Support/Pedigree Forge/queries on macOS).

Each .pql file contains a single query. To save a query you have written, copy its text into a .pql file in your user queries directory. It will appear in the drop-down the next time the panel is opened.