• 
      

    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups

    Review Request #15052 — Created May 13, 2026 and updated

    Information

    Review Board
    release-8.x

    Reviewers

    Adds the core faceted search engine, the reviewboard/search/facets/ package,
    along with benchmark tooling for performance validation.

    Key components:
    - DisjointFacetEngine (aggregations.py) builds and executes a single
    Elasticsearch msearch body per request containing: a paginated results
    query, one aggregation sub-query per faceted=True FilterSpec (each
    excluding its own filter clause so bucket counts remain accurate when the
    filter is active — the disjoint property), and one option-count query per
    compound filter choice.
    - FilterSpec / FilterBuilder (schema.py, builder.py) provide a
    declarative schema for filter dimensions and translate active filter values
    into Elasticsearch bool.filter clauses. Compound filters (ship-it, issues,
    reviews, file attachments) use custom build_fn callables.
    - FacetCache (cache.py) caches aggregation results per group, query, and
    filter state for the lifetime of a request.
    - FacetedSearchEngine (engine.py) orchestrates searches across
    ReviewRequestSearchGroup, UserSearchGroup, and ReviewGroupSearchGroup.
    For the active group it runs a full search with aggregations; for inactive
    groups it runs a count-only query so sidebar tab totals are accurate.
    - ReviewRequestSearchGroup additionally handles the Contents filter, which
    restricts the text query to specific ES fields and optionally runs a parallel
    query against comment indexes to surface review requests with matching
    comments.
    - Also adds populate_bench_data management command and bench_manager.py for
    populating and benchmarking persistent datasets at 1k, 10k, 100k, and 1M
    scale across Postgres, MySQL, and MariaDB.

    Ran full test suite.
    Verified that DisjointFacetEngine builds the correct msearch body structure,
    enforces the disjoint property on aggregation sub-queries, and produces exactly
    one msearch call per search regardless of how many filters are active. Verified
    that FilterBuilder generates correct Elasticsearch clauses for each filter
    type and that the permission filter is always injected. Verified that
    FacetedSearchEngine routes full searches to the active group and count-only
    queries to inactive groups.

    Performance benchmarks were run at 10k, 100k, and 1M review requests on
    Postgres, MySQL, and MariaDB. A 6-filter faceted search against a 1M-document
    index completes in 4.7ms with latency remaining flat across all scales. Full
    results are in the attached benchmark report.

    Summary ID
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    del unused comments.py
    ee52788d43f67517ef8a2c9cbc2afd768ce89a28
    Fix issue status filter to translate UI slugs to DB constants
    452e8e874e05dbbd64c8bad9d9e57c86485cc28e
    Updated styling to match standard
    00533e465f9c2ca0d8a5edd219293d104b6196a4
    Description From Last Updated

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    'django.core.management.base.CommandError' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    missing whitespace after ':' Column: 23 Error code: E231

    reviewbot reviewbot

    line too long (81 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    multiple statements on one line (def) Column: 5 Error code: E704

    reviewbot reviewbot

    multiple spaces before keyword Column: 35 Error code: E272

    reviewbot reviewbot

    multiple statements on one line (def) Column: 5 Error code: E704

    reviewbot reviewbot

    multiple statements on one line (def) Column: 5 Error code: E704

    reviewbot reviewbot

    multiple spaces before keyword Column: 33 Error code: E272

    reviewbot reviewbot

    multiple statements on one line (def) Column: 5 Error code: E704

    reviewbot reviewbot

    multiple spaces before keyword Column: 34 Error code: E272

    reviewbot reviewbot

    multiple statements on one line (def) Column: 5 Error code: E704

    reviewbot reviewbot

    multiple spaces before keyword Column: 33 Error code: E272

    reviewbot reviewbot

    multiple statements on one line (def) Column: 5 Error code: E704

    reviewbot reviewbot

    multiple spaces before keyword Column: 34 Error code: E272

    reviewbot reviewbot

    line too long (83 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (81 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (81 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    f-string is missing placeholders Column: 32 Error code: F541

    reviewbot reviewbot

    f-string is missing placeholders Column: 14 Error code: F541

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    f-string is missing placeholders Column: 10 Error code: F541

    reviewbot reviewbot

    multiple spaces before operator Column: 27 Error code: E221

    reviewbot reviewbot

    multiple spaces before operator Column: 28 Error code: E221

    reviewbot reviewbot

    multiple spaces before operator Column: 26 Error code: E221

    reviewbot reviewbot

    multiple spaces before operator Column: 28 Error code: E221

    reviewbot reviewbot

    f-string is missing placeholders Column: 13 Error code: F541

    reviewbot reviewbot

    no newline at end of file Column: 32 Error code: W292

    reviewbot reviewbot

    'time' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    expected 1 blank line, found 0 Column: 5 Error code: E301

    reviewbot reviewbot

    expected 1 blank line, found 0 Column: 5 Error code: E301

    reviewbot reviewbot

    expected 1 blank line, found 0 Column: 5 Error code: E301

    reviewbot reviewbot

    expected 1 blank line, found 0 Column: 5 Error code: E301

    reviewbot reviewbot

    expected 1 blank line, found 0 Column: 5 Error code: E301

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    f-string is missing placeholders Column: 16 Error code: F541

    reviewbot reviewbot

    'datetime.timezone' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    continuation line over-indented for visual indent Column: 47 Error code: E127

    reviewbot reviewbot

    continuation line over-indented for visual indent Column: 47 Error code: E127

    reviewbot reviewbot

    line too long (81 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (82 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    'django.db.models' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (82 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    'reviewboard.search.facets.cache.FacetCache' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    line too long (83 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (81 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (82 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    blank line contains whitespace Column: 1 Error code: W293

    reviewbot reviewbot

    blank line contains whitespace Column: 1 Error code: W293

    reviewbot reviewbot

    blank line contains whitespace Column: 1 Error code: W293

    reviewbot reviewbot

    blank line contains whitespace Column: 1 Error code: W293

    reviewbot reviewbot

    blank line contains whitespace Column: 1 Error code: W293

    reviewbot reviewbot

    line too long (81 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    'django.core.management.base.CommandError' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    missing whitespace after ':' Column: 23 Error code: E231

    reviewbot reviewbot

    line too long (81 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    multiple statements on one line (def) Column: 5 Error code: E704

    reviewbot reviewbot

    multiple spaces before keyword Column: 35 Error code: E272

    reviewbot reviewbot

    multiple statements on one line (def) Column: 5 Error code: E704

    reviewbot reviewbot

    multiple statements on one line (def) Column: 5 Error code: E704

    reviewbot reviewbot

    multiple spaces before keyword Column: 33 Error code: E272

    reviewbot reviewbot

    multiple statements on one line (def) Column: 5 Error code: E704

    reviewbot reviewbot

    multiple spaces before keyword Column: 34 Error code: E272

    reviewbot reviewbot

    multiple statements on one line (def) Column: 5 Error code: E704

    reviewbot reviewbot

    multiple spaces before keyword Column: 33 Error code: E272

    reviewbot reviewbot

    multiple statements on one line (def) Column: 5 Error code: E704

    reviewbot reviewbot

    multiple spaces before keyword Column: 34 Error code: E272

    reviewbot reviewbot

    line too long (83 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (81 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (81 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    f-string is missing placeholders Column: 32 Error code: F541

    reviewbot reviewbot

    f-string is missing placeholders Column: 14 Error code: F541

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    f-string is missing placeholders Column: 10 Error code: F541

    reviewbot reviewbot

    multiple spaces before operator Column: 27 Error code: E221

    reviewbot reviewbot

    multiple spaces before operator Column: 28 Error code: E221

    reviewbot reviewbot

    multiple spaces before operator Column: 26 Error code: E221

    reviewbot reviewbot

    multiple spaces before operator Column: 28 Error code: E221

    reviewbot reviewbot

    f-string is missing placeholders Column: 13 Error code: F541

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    'django.core.management.base.CommandError' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    missing whitespace after ':' Column: 23 Error code: E231

    reviewbot reviewbot

    expected 1 blank line, found 0 Column: 5 Error code: E301

    reviewbot reviewbot

    expected 1 blank line, found 0 Column: 5 Error code: E301

    reviewbot reviewbot

    expected 1 blank line, found 0 Column: 5 Error code: E301

    reviewbot reviewbot

    expected 1 blank line, found 0 Column: 5 Error code: E301

    reviewbot reviewbot

    expected 1 blank line, found 0 Column: 5 Error code: E301

    reviewbot reviewbot

    f-string is missing placeholders Column: 32 Error code: F541

    reviewbot reviewbot

    f-string is missing placeholders Column: 14 Error code: F541

    reviewbot reviewbot

    f-string is missing placeholders Column: 10 Error code: F541

    reviewbot reviewbot

    multiple spaces before operator Column: 27 Error code: E221

    reviewbot reviewbot

    multiple spaces before operator Column: 28 Error code: E221

    reviewbot reviewbot

    multiple spaces before operator Column: 26 Error code: E221

    reviewbot reviewbot

    multiple spaces before operator Column: 28 Error code: E221

    reviewbot reviewbot

    f-string is missing placeholders Column: 13 Error code: F541

    reviewbot reviewbot

    'time' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    'dataclasses.asdict' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    'dataclasses.field' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    f-string is missing placeholders Column: 16 Error code: F541

    reviewbot reviewbot

    'datetime.timezone' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    'django.db.models' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    blank line contains whitespace Column: 1 Error code: W293

    reviewbot reviewbot

    blank line contains whitespace Column: 1 Error code: W293

    reviewbot reviewbot

    line too long (80 > 79 characters) Column: 80 Error code: E501

    reviewbot reviewbot

    local variable 'rrs2' is assigned to but never used Column: 9 Error code: F841

    reviewbot reviewbot

    blank line contains whitespace Column: 1 Error code: W293

    reviewbot reviewbot

    blank line contains whitespace Column: 1 Error code: W293

    reviewbot reviewbot

    blank line contains whitespace Column: 1 Error code: W293

    reviewbot reviewbot

    blank line contains whitespace Column: 1 Error code: W293

    reviewbot reviewbot

    'django.contrib.auth.models.User' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot

    missing whitespace after ':' Column: 21 Error code: E231

    reviewbot reviewbot

    'reviewboard.search.facets.cache.FacetCache' imported but unused Column: 1 Error code: F401

    reviewbot reviewbot
    Checks run (1 failed, 1 succeeded)
    flake8 failed.
    JSHint passed.

    flake8

    dan.casares
    Review request changed
    Commits:
    Summary ID Author
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    5374b963609df18752e686d913433b2e1f1d8505 DanielCasaresIglesias
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    442abf12ff7610466b2e5f13584e7eaff19619cf DanielCasaresIglesias

    Checks run (1 failed, 1 succeeded)

    flake8 failed.
    JSHint passed.

    flake8

    dan.casares
    Review request changed
    Commits:
    Summary ID Author
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    442abf12ff7610466b2e5f13584e7eaff19619cf DanielCasaresIglesias
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    f889b02773719e8e15e86308f86bffe086c342c8 DanielCasaresIglesias

    Checks run (1 failed, 1 succeeded)

    flake8 failed.
    JSHint passed.

    flake8

    dan.casares
    dan.casares
    Review request changed
    Change Summary:

    Added the Any/All toggle.
    Fixed permission issues for group searches.
    Updated schema lables to correctly match issue status
    Made test_faceted_aggregation much more verbose, ensuring each filter works properly.

    Commits:
    Summary ID Author
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    9d6264ed7f45b8ba2d3f6c134eee21960a005a21 DanielCasaresIglesias
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    dce9e5f371006b906a6de9c9281c75f3f26efaa1 Daniel Casares-Iglesias
    Fix issue status filter to translate UI slugs to DB constants
    b27d978e486ace6500164cf5f4daf9fb555e164b Daniel Casares-Iglesias

    Checks run (1 failed, 1 succeeded)

    flake8 failed.
    JSHint passed.

    flake8

    dan.casares
    dan.casares
    Review request changed
    Change Summary:

    Updated styling

    Commits:
    Summary ID
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    dce9e5f371006b906a6de9c9281c75f3f26efaa1
    Fix issue status filter to translate UI slugs to DB constants
    b27d978e486ace6500164cf5f4daf9fb555e164b
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    del unused comments.py
    ee52788d43f67517ef8a2c9cbc2afd768ce89a28
    Fix issue status filter to translate UI slugs to DB constants
    452e8e874e05dbbd64c8bad9d9e57c86485cc28e
    Updated styling to match standard
    23a73ce313846d4d573c2929027ee2519912d4ba

    Checks run (1 failed, 1 succeeded)

    flake8 failed.
    JSHint passed.

    flake8

    dan.casares
    Review request changed
    Commits:
    Summary ID
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    del unused comments.py
    ee52788d43f67517ef8a2c9cbc2afd768ce89a28
    Fix issue status filter to translate UI slugs to DB constants
    452e8e874e05dbbd64c8bad9d9e57c86485cc28e
    Updated styling to match standard
    23a73ce313846d4d573c2929027ee2519912d4ba
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    del unused comments.py
    ee52788d43f67517ef8a2c9cbc2afd768ce89a28
    Fix issue status filter to translate UI slugs to DB constants
    452e8e874e05dbbd64c8bad9d9e57c86485cc28e
    Updated styling to match standard
    2a206452a8e36b6a964996e727ae9a88fdde797b

    Checks run (1 failed, 1 succeeded)

    flake8 failed.
    JSHint passed.

    flake8

    dan.casares
    Review request changed
    Commits:
    Summary ID
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    del unused comments.py
    ee52788d43f67517ef8a2c9cbc2afd768ce89a28
    Fix issue status filter to translate UI slugs to DB constants
    452e8e874e05dbbd64c8bad9d9e57c86485cc28e
    Updated styling to match standard
    2a206452a8e36b6a964996e727ae9a88fdde797b
    Search: Add DisjointFacetEngine, FilterBuilder, FacetCache, and search groups
    del unused comments.py
    ee52788d43f67517ef8a2c9cbc2afd768ce89a28
    Fix issue status filter to translate UI slugs to DB constants
    452e8e874e05dbbd64c8bad9d9e57c86485cc28e
    Updated styling to match standard
    00533e465f9c2ca0d8a5edd219293d104b6196a4

    Checks run (2 succeeded)

    flake8 passed.
    JSHint passed.