Faceted
Search.
What it does
Search in Pazator used to be a linear scan — take the query, loop over every person and entity, check each field, return what matches. Fine when you have 50 entries. Painful when you have 500, and useless when you need to narrow down by nationality, threat level, or tags without retyping your query.
Faceted search builds an inverted index on load and uses it to give you filter pills (facets) that update their counts in real-time. Click a facet value to filter results further. The index covers all the object system fields — threat level, nationality, religion, occupation, ethnicity, gender, immigration status, social class, income level — plus tags.
How it works
On load, a FacetIndex is built from all humans and entities. Each facet type
(nationality, threat level, etc.) becomes a map from value to set of IDs. When you type a query, the
text search runs first (still a loop, but only over the fields you selected). The results feed into
the facet bar, which shows the top 8 values per facet type along with how many results they match.
When you click a facet pill, the current results are filtered to only show items matching that value. The facet counts update immediately to reflect the new filtered set. Multiple facets stack — e.g., threat level = High AND nationality = Iranian shows only the intersection.
InvertedIndex
threatLevel: { "high" => Set(id1, id5, ...), "critical" => Set(...) }
nationality: { "iranian" => Set(id1, id3, ...), ... }
tag: { "suspect" => Set(id2, id5), ... }
On search: text query returns results, facet bar shows counts,
clicking a value re-filters in O(1) per item via the index.
Facet types
The facet bar shows up to eight values per type, sorted by count. The types are:
- Threat Level — None, Low, Medium, High, Critical
- Nationality
- Religion
- Occupation
- Ethnicity
- Gender
- Immigration Status
- Social Class
- Income Level
- Tags — any tag applied to an entry
The order and visibility is determined by what values actually exist in your results. If nobody has a religion set, that facet group just doesnt show. The counts next to each value tell you how many of the current results match it — useful for figuring out what the data looks like before filtering.
Saved searches
The old recent searches still work (history chips below the search bar). On top of that, saved
searches are persisted in localStorage under pazator_saved_searches. A saved
search stores the query string and the active facets. Clicking a saved search restores everything
— query + facets — in one click.
Saved searches appear as bookmark chips in the search results bar. Theyre shown before recent searches and have a yellow tint to distinguish them. The save button appears next to the search clear button when you have an active query or facets.
Performance
The inverted index is built synchronously at load time. For datasets under ~10,000 entries this is effectively instant. The text search itself still walks every entry, but the facet filtering after that is a constant-time set lookup per item. The big win is that you avoid re-querying the database — once you have results, facet filtering is pure JS object lookups.
Code
The facet system lives in app/pazator_facets.js as a standalone module.
The pazator_facets.FacetIndex class handles building the index, filtering,
and count generation. Saved search CRUD is also there. The UI integration is in
app/scriptz.js where performSearch was rewritten
to work with facets, and renderFacetBar handles the dynamic pill generation.
Facets build on top of the object system from pazator_objects.js. The
same field types used in the autocomplete fields (nationality, occupation, etc.) are the ones
that power the facet bar. See the app and open the
Search tab to try it.