Skip to content

Feat: Add RequireAllPeople setting for AND-based person filtering#609

Open
FlorianObermayer wants to merge 3 commits intoimmichFrame:mainfrom
FlorianObermayer:main
Open

Feat: Add RequireAllPeople setting for AND-based person filtering#609
FlorianObermayer wants to merge 3 commits intoimmichFrame:mainfrom
FlorianObermayer:main

Conversation

@FlorianObermayer
Copy link
Copy Markdown

@FlorianObermayer FlorianObermayer commented Mar 22, 2026

This pull request introduces a new RequireAllPeople setting that changes how ImmichFrame filters assets by person.

Before: Assets are returned if they feature any of the configured people (OR logic).
After: When RequireAllPeople: true is set, only assets featuring all configured people are returned (AND logic).

This is useful for finding shared moments — for example, only showing photos where both Person A and Person B appear together.

Usage:

People:
  - <person-id-1>
  - <person-id-2>
RequireAllPeople: true

Summary by CodeRabbit

  • New Features

    • Added a per-account configuration option to change people-filtering to require all selected people (AND mode) instead of any (OR mode).
  • Documentation

    • Updated configuration docs to include the new per-account RequireAllPeople setting and its default.
  • Tests

    • Added tests covering multi-person AND searches, pagination aggregation, and empty-result behavior.

…l-people-flag

[FEAT] Introduced RequireAllPeople flag to filter assets for people in an AND fashion
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 22, 2026

📝 Walkthrough

Walkthrough

A new per-account RequireAllPeople setting was added and wired into configuration/models and IAccountSettings. PersonAssetsPool.LoadAssets now groups person IDs into either a single AND-mode search or per-person OR-mode searches based on that setting. Tests and test resources were added to validate the behavior and pagination.

Changes

Cohort / File(s) Summary
Core Feature Implementation
ImmichFrame.Core/Interfaces/IServerSettings.cs, ImmichFrame.Core/Logic/Pool/PersonAssetsPool.cs
Added RequireAllPeople to account settings interface; modified PersonAssetsPool.LoadAssets to either issue a single search with all PersonIds (AND semantics) or iterate per-person (OR semantics), preserving per-request pagination.
Configuration Models
ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs, ImmichFrame.WebApi/Models/ServerSettings.cs
Added RequireAllPeople boolean property (default false) to V1 server settings and server account settings; exposed via adapter.
Test Coverage
ImmichFrame.Core.Tests/Logic/Pool/PersonAssetsPoolTests.cs
Added three NUnit tests verifying AND-mode single-search, pagination aggregation across pages, and empty-result behavior when no asset matches all people.
Test Resources
ImmichFrame.WebApi.Tests/Resources/TestV1.json, .../TestV2.json, .../TestV2.yml, .../TestV2_NoGeneral.json
Updated test resource files to include RequireAllPeople: true for relevant test accounts.
Docs
docs/docs/getting-started/configuration.md
Documented new per-account RequireAllPeople configuration option.

Sequence Diagram(s)

sequenceDiagram
    participant Pool as PersonAssetsPool
    participant Settings as AccountSettings
    participant API as MetadataSearchService
    participant Storage as Storage/DB

    Pool->>Settings: read People[], RequireAllPeople
    alt RequireAllPeople == true
        Pool->>API: Search(MetadataSearchDto{PersonIds = [all people], Page=1})
    else
        loop per personId
            Pool->>API: Search(MetadataSearchDto{PersonIds = [personId], Page=1})
        end
    end
    API->>Storage: query by PersonIds + pagination
    Storage-->>API: page results
    API-->>Pool: results (may be multiple pages)
    loop while API has next page
        Pool->>API: Search(next Page)
        API-->>Pool: next page results
    end
    Pool->>Pool: aggregate results per strategy
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • JW-CH

Poem

🐰 I hopped through code with eager paws,
I gathered people, counted claws,
When all must match, one search I make,
Pages collected for efficiency's sake,
A little hop, a joyful tweak — hooray for fewer calls today! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding a RequireAllPeople setting that enables AND-based person filtering instead of OR-based filtering.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@FlorianObermayer FlorianObermayer changed the title [FEATAURE] Add RequireAllPeople flag for AND filtering of people's assets Add RequireAllPeople setting for AND-based person filtering Mar 22, 2026
@FlorianObermayer FlorianObermayer marked this pull request as ready for review March 22, 2026 21:43
@FlorianObermayer FlorianObermayer changed the title Add RequireAllPeople setting for AND-based person filtering Feat: Add RequireAllPeople setting for AND-based person filtering Mar 22, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs (1)

12-23: ⚠️ Potential issue | 🟡 Minor

Guard against an empty People list in AND mode.

With RequireAllPeople enabled, an empty list now becomes one SearchAssetsAsync call with PersonIds = []. That changes the old “no people => no assets” behavior and can widen the query if Immich treats an empty filter as “unset”. Please short-circuit empty lists before building personIdGroups.

Suggested fix
         var people = accountSettings.People;
-        if (people == null)
+        if (people == null || !people.Any())
         {
             return personAssets;
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs` around lines 12 - 23, The
code builds personIdGroups using accountSettings.RequireAllPeople but doesn't
guard against an empty people list, which causes an unintended broad query when
RequireAllPeople is true; to fix, check accountSettings.People (the people
variable) for null or empty before constructing personIdGroups and short-circuit
by returning personAssets if people.Count == 0 when
accountSettings.RequireAllPeople is true; adjust the logic around personIdGroups
(the ternary using RequireAllPeople) so the empty-list case returns early rather
than creating a single empty group, ensuring SearchAssetsAsync is never called
with PersonIds = [].
🧹 Nitpick comments (1)
ImmichFrame.Core.Tests/Logic/Pool/PersonAssetsPoolTests.cs (1)

127-153: Consider adding an edge-case test for single-person AND mode.

When RequireAllPeople=true and only one person is configured, the behavior should be identical to OR mode. Adding a test for this edge case would document and verify this equivalence.

📋 Suggested additional test
[Test]
public async Task LoadAssets_RequireAllPeople_SinglePerson_BehavesSameAsOrMode()
{
    // Arrange
    var personId = Guid.NewGuid();
    _mockAccountSettings.SetupGet(s => s.People).Returns(new List<Guid> { personId });
    _mockAccountSettings.SetupGet(s => s.RequireAllPeople).Returns(true);

    var assets = Enumerable.Range(0, 5).Select(i => CreateAsset($"asset_{i}")).ToList();

    _mockImmichApi.Setup(api => api.SearchAssetsAsync(
            It.Is<MetadataSearchDto>(d => d.PersonIds.Contains(personId) && d.PersonIds.Count == 1),
            It.IsAny<CancellationToken>()))
        .ReturnsAsync(CreateSearchResult(assets, 5));

    // Act
    var result = (await _personAssetsPool.TestLoadAssets()).ToList();

    // Assert
    Assert.That(result.Count, Is.EqualTo(5));
    _mockImmichApi.Verify(api => api.SearchAssetsAsync(It.IsAny<MetadataSearchDto>(), It.IsAny<CancellationToken>()), Times.Once);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ImmichFrame.Core.Tests/Logic/Pool/PersonAssetsPoolTests.cs` around lines 127
- 153, Add a new unit test in PersonAssetsPoolTests named
LoadAssets_RequireAllPeople_SinglePerson_BehavesSameAsOrMode that sets
_mockAccountSettings.People to a single Guid and
_mockAccountSettings.RequireAllPeople to true, arranges _mockImmichApi.Setup to
expect a MetadataSearchDto containing that single personId (and PersonIds.Count
== 1) and to return a sample CreateSearchResult, invokes
_personAssetsPool.TestLoadAssets(), and asserts the returned count equals the
expected number and that SearchAssetsAsync was called exactly once; this
verifies the AND-mode behavior for one person matches OR-mode.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs`:
- Around line 12-23: The code builds personIdGroups using
accountSettings.RequireAllPeople but doesn't guard against an empty people list,
which causes an unintended broad query when RequireAllPeople is true; to fix,
check accountSettings.People (the people variable) for null or empty before
constructing personIdGroups and short-circuit by returning personAssets if
people.Count == 0 when accountSettings.RequireAllPeople is true; adjust the
logic around personIdGroups (the ternary using RequireAllPeople) so the
empty-list case returns early rather than creating a single empty group,
ensuring SearchAssetsAsync is never called with PersonIds = [].

---

Nitpick comments:
In `@ImmichFrame.Core.Tests/Logic/Pool/PersonAssetsPoolTests.cs`:
- Around line 127-153: Add a new unit test in PersonAssetsPoolTests named
LoadAssets_RequireAllPeople_SinglePerson_BehavesSameAsOrMode that sets
_mockAccountSettings.People to a single Guid and
_mockAccountSettings.RequireAllPeople to true, arranges _mockImmichApi.Setup to
expect a MetadataSearchDto containing that single personId (and PersonIds.Count
== 1) and to return a sample CreateSearchResult, invokes
_personAssetsPool.TestLoadAssets(), and asserts the returned count equals the
expected number and that SearchAssetsAsync was called exactly once; this
verifies the AND-mode behavior for one person matches OR-mode.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7faac249-635c-485a-b58e-ee27fe4b4c8f

📥 Commits

Reviewing files that changed from the base of the PR and between 081b5bf and 35870e8.

📒 Files selected for processing (9)
  • ImmichFrame.Core.Tests/Logic/Pool/PersonAssetsPoolTests.cs
  • ImmichFrame.Core/Interfaces/IServerSettings.cs
  • ImmichFrame.Core/Logic/Pool/PeopleAssetsPool.cs
  • ImmichFrame.WebApi.Tests/Resources/TestV1.json
  • ImmichFrame.WebApi.Tests/Resources/TestV2.json
  • ImmichFrame.WebApi.Tests/Resources/TestV2.yml
  • ImmichFrame.WebApi.Tests/Resources/TestV2_NoGeneral.json
  • ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs
  • ImmichFrame.WebApi/Models/ServerSettings.cs

@JW-CH JW-CH self-requested a review March 23, 2026 07:48
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
docs/docs/getting-started/configuration.md (1)

141-142: Consider clarifying the OR vs AND semantics.

The current description is accurate but doesn't clearly explain the key distinction between the default behavior (OR logic) and the enabled behavior (AND logic). Users might not immediately understand when or why to use this setting.

Consider revising to something like:

-    # If this is set, all specified people must be present in an image for it to be displayed.
-    RequireAllPeople: false # boolean
+    # Controls person filtering logic. When false (default), images featuring ANY of the specified people are displayed (OR logic).
+    # When true, only images featuring ALL specified people are displayed (AND logic).
+    RequireAllPeople: false # boolean

This makes the default behavior explicit and highlights the OR vs AND distinction that's central to this feature.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/docs/getting-started/configuration.md` around lines 141 - 142, Update
the description for the RequireAllPeople configuration entry to explicitly state
the OR vs AND semantics: explain that when RequireAllPeople is false (default)
images are shown if they contain any of the listed people (OR logic), and when
true images are shown only if they contain all listed people (AND logic), and
include a short example for each case referencing the RequireAllPeople key to
make the behavior unambiguous.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/docs/getting-started/configuration.md`:
- Around line 141-142: Update the description for the RequireAllPeople
configuration entry to explicitly state the OR vs AND semantics: explain that
when RequireAllPeople is false (default) images are shown if they contain any of
the listed people (OR logic), and when true images are shown only if they
contain all listed people (AND logic), and include a short example for each case
referencing the RequireAllPeople key to make the behavior unambiguous.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 184b95ad-fb07-455c-883e-f3c246bdcb3c

📥 Commits

Reviewing files that changed from the base of the PR and between 35870e8 and 3f7f4c1.

📒 Files selected for processing (1)
  • docs/docs/getting-started/configuration.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant