Seu plano de execução
Ao analisar o plano de consulta, podemos ver que um índice é tocado para atender a duas operações de filtro.

Em termos simples, devido ao operador TOP, uma meta de linha foi definida. Muito mais informações e pré-requisitos sobre metas de linha podem ser encontrados aqui
Da mesma fonte:
Uma estratégia de meta de linha geralmente significa favorecer operações de navegação sem bloqueio (por exemplo, junções de loops aninhados, pesquisas de índice e pesquisas) sobre operações de bloqueio baseadas em conjuntos, como classificação e hash. Isso pode ser útil sempre que o cliente puder se beneficiar de uma inicialização rápida e um fluxo constante de linhas (talvez com um tempo de execução geral mais longo - veja a postagem de Rob Farley acima). Existem também os usos mais óbvios e tradicionais, por exemplo, na apresentação de resultados de uma página por vez.
A tabela inteira é sondada nos filtros com o uso de uma semi junção esquerda que tem uma meta de linha definida, na esperança de retornar as 5 linhas o mais rápido e eficiente possível.
Isso não acontece, resultando em muitas iterações sobre o TVF .Fulltextmatch.

Recriando
Com base no seu plano , consegui recriar um pouco o seu problema:
CREATE TABLE dbo.Person(id int not null,lastname varchar(max));
CREATE UNIQUE INDEX ui_id ON dbo.Person(id)
CREATE FULLTEXT CATALOG ft AS DEFAULT;
CREATE FULLTEXT INDEX ON dbo.Person(lastname)
KEY INDEX ui_id
WITH STOPLIST = SYSTEM;
GO
INSERT INTO dbo.Person(id,lastname)
SELECT top(12000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)),
REPLICATE(CAST('A' as nvarchar(max)),80000)+ CAST(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as varchar(10))
FROM master..spt_values spt1
CROSS APPLY master..spt_values spt2;
CREATE CLUSTERED INDEX cx_Id on dbo.Person(id);
Executando a consulta
SELECT TOP (5) *
FROM dbo.Person
WHERE "id" = 1 OR contains("lastName", '"B*"');
Resultados em um plano de consulta comparável ao seu:

No exemplo acima, B não existe no índice de texto completo. Como resultado, depende do parâmetro e dos dados a eficiência do plano de consulta.
Uma explicação melhor disso pode ser encontrada em Row Goals, Part 2: Semi Joins por Paul White
... Em outras palavras, em cada iteração de uma aplicação, podemos parar de olhar para a entrada B assim que a primeira correspondência for encontrada, usando o predicado de junção push-down. É exatamente para isso que uma meta de linha é boa: gerar parte de um plano otimizado para retornar as primeiras n linhas correspondentes rapidamente (onde n = 1 aqui).
Por exemplo, alterando o predicado para que os resultados sejam encontrados mais cedo (no início da varredura).
select top (5) *
from dbo.Person
where "id" = 124
or contains("lastName", '"A*"');

o where "id" = 124é eliminado devido ao predicado do índice de texto completo já retornar 5 linhas, satisfazendo o TOP()predicado.
Os resultados mostram isso também
id lastname
1 'AAA...'
2 'AAA...'
3 'AAA...'
4 'AAA...'
5 'AAA...'
E as execuções de TVF:

Inserindo algumas novas linhas
INSERT INTO dbo.Person
SELECT 12001, REPLICATE(CAST('B' as nvarchar(max)),80000);
INSERT INTO dbo.Person
SELECT 12002, REPLICATE(CAST('B' as nvarchar(max)),80000);
Executando a consulta para encontrar essas linhas inseridas anteriores
SELECT TOP (2) *
from dbo.Person
where "id" = 1
or contains("lastName", '"B*"');
Isso novamente resulta em muitas iterações em quase todas as linhas para retornar o último, mas um valor encontrado.


id lastname
1 'AAA...'
12001 'BBB...'
Resolver
Ao remover o objetivo da linha usando o traceflag 4138
SELECT TOP (5) *
FROM dbo.Person
WHERE "id" = 124
OR contains("lastName", '"B*"')
OPTION(QUERYTRACEON 4138 );
O otimizador usa um padrão de junção mais próximo da implementação de a UNION; no nosso caso, isso é favorável, pois empurra os predicados para as respectivas buscas de índice em cluster e não usa o operador de semi junção esquerda com objetivo de linha.

Outra maneira de escrever isso, sem usar o traceflag acima mencionado:
SELECT top (5) *
FROM
(
SELECT *
FROM dbo.Person
WHERE "id" = 1
UNION
SELECT *
FROM dbo.Person
WHERE contains("lastName", '"B*"')
) as A;
Com o plano de consulta resultante:

onde a função de texto completo é aplicada diretamente

Como nota de rodapé, por op, o hotfix do otimizador de consultas traceflag 4199 resolveu seu problema. Ele implementou isso adicionando OPTION(QUERYTRACEON(4199))à consulta. Não fui capaz de reproduzir esse comportamento do meu lado. Esse hotfix contém uma otimização de semi-junção:
Sinalizador de rastreamento: 4102 Função: SQL 9 - O desempenho da consulta é lento se o plano de execução da consulta contiver operadores de semi-junção Normalmente, operadores de semi-junção são gerados quando a consulta contém a palavra-chave IN ou a palavra-chave EXISTS. Ative os sinalizadores 4102 e 4118 para superar isso.
Fonte
Extra
Durante a otimização baseada em custos, o otimizador também pode adicionar um spool de índice ao plano de execução, implementado por LogOp_Spool Index on fly Eager (ou a contraparte física)
Faz isso com meu conjunto de dados para, TOP(3)mas não paraTOP(2)
SELECT TOP (3) *
from dbo.Physician
where "id" = 1
or contains("lastName", '"B*"')

Na primeira execução, um spool ansioso lê e armazena toda a entrada antes de retornar o subconjunto de linhas solicitado pelas execuções do Predicate Later e ler e retornar o mesmo ou um subconjunto de linhas da tabela de trabalho, sem precisar executar o filho nós novamente.
Fonte
Com o predicado de busca aplicado a este spool ansioso do índice:
