Issue #3586315: Avoid full catalog scan when agent has no scope subscriptions.

Description

AiContextSelector::getPrefilteredPublishedItemIds() returned NULL when $scopeSubscriptions was empty, causing the chunk loop to fall back to a full catalog scan on every AI request.

For SELECTION_MODE_MINIMAL with no subscriptions, only global items and target-entity auto-included items can ever be selected. Subscription-scoped items are always discarded during scoring, so loading every published context item is unnecessary. On sites with many context items, this results in dozens of extra chunked queries per request, affecting the common "global items only" agent configuration.

This MR replaces the full scan with a targeted prefilter when using SELECTION_MODE_MINIMAL with no subscriptions.

The prefilter includes:

  • Global items — retrieved directly from the scope index (scope_id = 'global').
  • Unindexed items — items without any scope index entries. These contain only non-indexed scopes (such as target_entity, stored separately via a DER field) or no scopes at all, making them a safe superset of target-entity-only candidates.
  • Current target-entity items — items that explicitly reference the current target entity via the DER field. This also covers the edge case where an item has both a subscription scope (indexed) and a target entity reference.

SELECTION_MODE_MATCH_ALL is unchanged and continues to scan the full catalog.

Files changed

  • AiContextScopeIndexService

    • Added getIndexedItemIds() to return which IDs have at least one scope index entry.
  • AiContextScopeResolver

    • Added getUnindexedItemIds() to return IDs that are absent from the scope index.
  • AiContextSelector

    • Added buildNoSubscriptionCandidateIds() and loadTargetEntityCandidateIds().
    • Updated getPrefilteredPublishedItemIds() to accept $selectionMode and apply the targeted prefilter for SELECTION_MODE_MINIMAL when no subscriptions are present.
  • AiContextSelectorNoSubscriptionPrefilterTest

    • Added a kernel test covering:

      • global selection
      • MATCH_ALL behavior
      • neverInclude
      • alwaysInclude
      • no-subscription behavior

Testing instructions

  1. Enable the module and create several context items:

    • at least one with Global scope
    • one or more with a non-global scope (for example, Language or Tag)
  2. Create an agent at /admin/config/ai/agents and leave its scope subscriptions empty.

  3. Enable debug logging at Configuration → AI Context → Settings → General → Enable debug logging.

  4. Trigger an AI request using that agent (for example, through the AI Agents Debugger).

  5. Open the Drupal log (/admin/reports/dblog) and verify a message similar to:

    AiContextSelector: no-subscription prefilter selected X of Y published item IDs

    where X is significantly smaller than Y (only global and unindexed candidates are selected rather than the full catalog).

  6. Verify that the agent response still includes the global context item.

  7. Repeat the test with an agent that has scope subscriptions and verify behavior remains unchanged.

Checklist

  • I have updated the MR title to use format: Issue #1234: My issue title.
  • I have updated the MR description to include: Closes #1234
  • I have performed a self-review of my own code
  • I have added or updated tests, or explained in the description why this change is not covered by tests
  • I have updated documentation for any new or changed functionality
  • I have written testing instructions and verified them locally
  • I have noted any required post-merge steps (config imports, cache rebuilds, manual changes)
  • This MR contains no breaking API or hook changes, or they are explicitly documented in the description

AI Compliance

Note

Check the one that best describes your usage, or leave all unchecked if AI was not significantly used.

  • AI Assisted Code
    Mainly written by a human; AI used for autocomplete or partial generation under full human supervision.

  • AI Generated Code
    Mainly generated by AI, reviewed and approved by a human before this MR was created.

  • Vibe Coded
    Generated by AI and only functionally reviewed before this MR was created.

Closes #3586315

Edited by Pravesh Poonia

Merge request reports

Loading