Recipes - Search command
If you need to perform complex queries, read operations may not suffice.
You can create a search command to use the full potential of your Database / ORM.
Setup
Start by generating a new cmd using the CLI.
Let's change the DTO to accept a string to be used as a regex:
export class SearchDto {
  @IsString()
  @$Transform((v: string) => v.replace(/[.*+?^$}{)(|[\]\\]/g, '\\$&'))
  userNameLike: string;
  @IsOptional()
  @IsString()
  visibility?: 'public' | 'private';
}
export type SearchReturnDto = any;
Note
We escape userNameLike of any regex character to avoid potential regex attacks (ReDoS).
Then, let's allow guests to use the search command in the security:
const getCmdSecurity = (search, profile): CmdSecurity => { 
    return {
        dto: SearchDto,
        rolesRights: {
            guest: {
                async defineCMDAbility(can, cannot, ctx) {
                    // Define abilities for guest
                    can(search, profile);
                }
            }
        },
    }
}
guestCanUseAll parameter for better performance.
const getCmdSecurity = (search, profile): CmdSecurity => { 
    return {
        dto: SearchDto,
        rolesRights: {},
        guestCanUseAll: true
    }
}
Implementation
We can now implement the command action, to make things simpler we use the CrudService->$find method.
We're using a simple regex to search on the username field, but you can use any of MikroOrm's operators like $and & $or.
export async function search(this: ProfileService, dto: SearchDto, ctx: CrudContext, inheritance?: any ){
    const query: Partial<Profile> = {
      userName: new RegExp(dto.userNameLike, 'i') as any,
    };
    if (dto.visibility) {
      query.visibility = dto.visibility as any;
    }
    const fakeCtx: CrudContext = {
      ...ctx,
      query,
      data: null,
      origin: 'crud',
      method: 'GET',
    };
    await this.crudAuthorization.authorize(fakeCtx, this.security);
    const opParams = ctx.queryOptions ? {options: ctx.queryOptions} : undefined; 
    return this.$find(query, fakeCtx, opParams);
}
Note
The call to service.crudAuthorization.authorize allows us to use the CRUD security for read operations. Anything forbidden for $find will be forbidden in our search cmd as well. 
Usage
Since search returns the result of $find, it behaves as a limited command.
In our front-end, we call it with CrudClient->cmdL to benefit from the limit and offset options.
const searchDto: SearchDto = {
    userNameLike: 'doe',
    visibility: 'public',
};
const options: ICrudOptions = {
    limit: 5,
}
const { data, total, limit } = await profileClient.cmdL(
    'search',
    searchDto,
    options
);
// results : [{ userName: "Michael Doe", ... }, { userName: "Jon Doe", ...}]
Note
If no limit is specified, the client will perform multiple requests until total profiles have been fetched. The maximum limit that can be used is set in the CmdSecurity.
Note
Since search is a read-only command, you can enable allowGetMethod in the CmdSecurity. This allows for searching using a simple url: /crud/s/profile/cmd/search?query={"userNameLike":"doe"}"