feat: add role-family sweeps and domain/language filters
Broaden the search beyond engineer/developer titles and stop three
classes of bad result from ever reaching the CSV.
config.ts
- PLATFORM_KEYWORDS (architect, SRE, infra, security, MLOps),
RESEARCH_KEYWORDS (research/scientific software, HPC, data platform)
and FIELD_KEYWORDS (forward-deployed, customer/sales, founding).
Separate OR-strings, not folded into KEYWORDS: LinkedIn ranks a
focused query far better than one 50-term disjunction.
- sweep() generates one search per area per family. 7 searches -> 17.
fetch.ts
- BIO_DOMAIN_RE drops life-science roles (bioinformatics, biostats,
GMP/MSAT, genomics, pathology, drug discovery). Filters on the role,
never the employer: software jobs at Roche/Novartis/sciCORE stay.
- requiresGerman() drops English postings demanding German above B1.
Two rules, both from real false results: the nearest marker wins
("Infrastructure skills are a plus" sat 95 chars before "Good
knowledge of German"), and an intervening language name breaks the
binding ("Fluent in English; German and/or French advantageous").
- GERMAN_TITLE_RE catches "(German-speaking)" without a page fetch.
- isViableContent/isViableUrl run the DE/FR and German-requirement
checks on a single fetch; isEnglishContent/isEnglishUrl kept for
recheck.ts and temp scripts.
- SOFTWARE_TITLE_RE widened so the new families survive it.
index.ts
- both content-check paths use the combined check and report which
filter dropped a job.
This commit is contained in:
@@ -46,6 +46,55 @@ export const KEYWORDS = [
|
||||
"Solution Engineer",
|
||||
].join(" OR ");
|
||||
|
||||
// --- Role families beyond the core engineer/developer titles ---
|
||||
// Kept as separate OR-strings rather than folded into KEYWORDS: LinkedIn ranks
|
||||
// a focused query far better than one 50-term disjunction, and each family
|
||||
// needs a different set of areas (research is Basel-heavy, field/remote is not).
|
||||
|
||||
/** Platform, reliability, cloud and security — the DevOps/Swisscom lane. */
|
||||
export const PLATFORM_KEYWORDS = [
|
||||
"Solutions Architect",
|
||||
"Cloud Architect",
|
||||
"Site Reliability Engineer",
|
||||
"Infrastructure Engineer",
|
||||
"Cloud Engineer",
|
||||
"Security Engineer",
|
||||
"DevSecOps",
|
||||
"Application Security Engineer",
|
||||
"Cloud Security Engineer",
|
||||
"MLOps Engineer",
|
||||
"AI Platform Engineer",
|
||||
"ML Platform Engineer",
|
||||
"AI Infrastructure",
|
||||
].join(" OR ");
|
||||
|
||||
/** Research software engineering — Basel pharma/university cluster.
|
||||
* Deliberately no bioinformatics/computational-biology terms: those are
|
||||
* domain-science roles, filtered out by BIO_DOMAIN_RE in fetch.ts. */
|
||||
export const RESEARCH_KEYWORDS = [
|
||||
"Research Software Engineer",
|
||||
"Scientific Software Engineer",
|
||||
"Scientific Programmer",
|
||||
"Scientific Computing",
|
||||
"Research Engineer",
|
||||
"HPC Engineer",
|
||||
"Data Platform Engineer",
|
||||
].join(" OR ");
|
||||
|
||||
/** Customer-facing and early-stage product roles — the consulting lane. */
|
||||
export const FIELD_KEYWORDS = [
|
||||
"Forward Deployed Engineer",
|
||||
"Field Engineer",
|
||||
"Customer Engineer",
|
||||
"Sales Engineer",
|
||||
"Pre-Sales Engineer",
|
||||
"Integration Engineer",
|
||||
"API Engineer",
|
||||
"Analytics Engineer",
|
||||
"Founding Engineer",
|
||||
"Product Engineer",
|
||||
].join(" OR ");
|
||||
|
||||
// --- Define your searches here ---
|
||||
export const searches: SearchConfig[] = [
|
||||
{
|
||||
@@ -104,3 +153,48 @@ export const searches: SearchConfig[] = [
|
||||
timePosted: "month",
|
||||
},
|
||||
];
|
||||
|
||||
// --- Role-family sweeps ---------------------------------------------------
|
||||
// Same filters as the core searches, run across a narrower set of areas.
|
||||
// Research is Basel/Zürich only (that's where the institutes are); platform
|
||||
// and field roles also sweep Switzerland-wide remote.
|
||||
|
||||
const CORE_AREAS: { label: string; geoId?: string; location: string }[] = [
|
||||
{ label: "Basel Metropolitan Area", geoId: "90009885", location: "Basel Metropolitan Area" },
|
||||
{ label: "Zürich Metropolitan Area", geoId: "107814425", location: "Zürich Metropolitan Area" },
|
||||
{ label: "Bern", location: "Bern, Switzerland" },
|
||||
];
|
||||
|
||||
const RESEARCH_AREAS = CORE_AREAS.filter((a) => a.label !== "Bern");
|
||||
|
||||
function sweep(
|
||||
family: string,
|
||||
keywords: string,
|
||||
areas: typeof CORE_AREAS,
|
||||
opts: { remote?: boolean } = {}
|
||||
): SearchConfig[] {
|
||||
const out: SearchConfig[] = areas.map((a) => ({
|
||||
label: `${family} — ${a.label}`,
|
||||
keywords,
|
||||
geoId: a.geoId,
|
||||
location: a.location,
|
||||
workType: ["onsite", "hybrid"] as WorkType[],
|
||||
timePosted: "month" as TimePosted,
|
||||
}));
|
||||
if (opts.remote) {
|
||||
out.push({
|
||||
label: `${family} — Switzerland Remote`,
|
||||
keywords,
|
||||
location: "Switzerland",
|
||||
workType: ["remote"],
|
||||
timePosted: "month",
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
searches.push(
|
||||
...sweep("Platform", PLATFORM_KEYWORDS, CORE_AREAS, { remote: true }),
|
||||
...sweep("Research", RESEARCH_KEYWORDS, RESEARCH_AREAS),
|
||||
...sweep("Field", FIELD_KEYWORDS, CORE_AREAS, { remote: true })
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user