---
title: User search
summary: Searches external users with optional filters for access group, customer, identity, and status.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-30
library: SAIF.Business.External.Users
reference_type: package
operation_id: OP-EU-10
trigger: Caller invokes the search overload of `clsUserCollection.GetUsers(...)`.
---

# User search

This operation runs the dynamic user-search query exposed by `clsUserCollection.GetUsers(...)`. It starts from the base `EXT_USER` projection, joins the related person, user-type, company, access-group, and email tables, and appends only the predicates that the caller supplied.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-EU-10 |
| **Trigger** | Search UI or support workflow submits user-search criteria |
| **Inputs** | Test-user flag, access-group code, customer ID, partial AD ID, partial email, partial name, status code, user-type code, optional row limit |
| **Outputs** | Collection of matching `clsUser` rows |
| **Primary components** | `clsUserCollection.GetUsers(inDPI, inAccessGroupCode, inCustomerID, ...)` |
| **Entry point** | `Business\SAIF.Business.External.Users\clsUser.vb` |

## Steps

1. **Start from the base user projection.** The search method uses the same core `clsUser.Criteria.RetrieveSqlStatement` projection that normal user retrieval uses, swapping the search placeholder for company name text.
2. **Join the related search tables.** The query adds left joins to `PERSON`, `EXT_USER_TYPE`, `EXT_USER_ACCESS_GROUP`, `COMPANY`, `PERSON_EMAIL`, and `EMAIL_ADDRESS` so name, company, access-group, and email filters can be applied ([SQL-020](../sql/SQL-020-saif-business-external-users.md), [EXT_USER](../database-tables.md#ext_user), [PERSON](../database-tables.md#person), [EXT_USER_TYPE](../database-tables.md#ext_user_type), [EXT_USER_ACCESS_GROUP](../database-tables.md#ext_user_access_group), [COMPANY](../database-tables.md#company), [PERSON_EMAIL](../database-tables.md#person_email), [EMAIL_ADDRESS](../database-tables.md#email_address)).
3. **Apply the required test-user filter first.** Every search binds `:TestUserFlag` and restricts the result set to either test users or non-test users.
4. **Append optional filters only when the caller supplied them.**
   - Partial AD user IDs become `upper(active_directory_id) like :ActiveDirectoryID`.
   - Access-group filters match `g.ext_user_access_group_code` exactly.
   - Customer filters match `t.customer_id` exactly and also search `credential_1_id` with a wildcard pattern.
   - Partial email filters match `lower(e.email_address) like :Email`.
   - Partial name filters search first name, last name, or company name.
   - Status and user-type filters match their exact stored codes.
5. **Apply an optional row limit after the dynamic query is assembled.** When the caller supplied `inRowLimit`, the method wraps the search and appends `where ROWNUM <= ...`.
6. **Load the resulting rows into `clsUser` objects.** `col.Load(...)` runs the dynamic SQL and returns the materialized collection to the caller.

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
    Start([Search request]) --> Base[Build base user query]
    Base --> AddFilters[Append only the supplied filters]
    AddFilters --> Limit{Row limit supplied?}
    Limit -->|Yes| Wrap[Apply ROWNUM limit]
    Limit -->|No| Execute[Execute query]
    Wrap --> Execute
    Execute --> Done([Return matching users])
```

## Failure and edge-case behavior

- Customer-ID search always includes the `credential_1_id` fallback pattern in the traced path because `searchOnlyIW` is hard-coded to `False`.
- A row limit truncates the search after the filter set is built; it does not change which predicates are applied.
- Because the query uses left joins, missing related person, company, or email rows do not exclude the base user row unless the caller filtered on those values.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [SQL catalog](../sql/index.md)
- [Database tables](../database-tables.md)

## Sources
- `Business\SAIF.Business.External.Users\clsUser.vb`
