SALESFORCE_SEARCH_QUERY - Buscar Dados com SOQL no Salesforce
O que é este Node?
O SALESFORCE_SEARCH_QUERY é o node responsável por executar consultas SOQL (Salesforce Object Query Language) personalizadas, permitindo buscar e filtrar dados de qualquer objeto do Salesforce.
Por que este Node existe?
Queries customizadas são essenciais para buscar dados específicos. O SALESFORCE_SEARCH_QUERY existe para:
- Buscas Complexas: Encontrar registros com múltiplos critérios e filtros avançados
- Validação de Existência: Verificar se lead/contact/account já existe antes de criar
- Dados Relacionados: Buscar registros com joins e relacionamentos
- Agregações: Contar, somar, calcular médias de dados no Salesforce
Como funciona internamente?
Quando o SALESFORCE_SEARCH_QUERY é executado, o sistema:
- Recebe a query: Obtém string SOQL completa
- Processa variáveis: Substitui variáveis do contexto na query
- Valida query: Verifica se query foi informada
- Codifica para URL: Prepara query para envio na URL
- Autentica no Salesforce: Usa accessToken e instanceUrl
- Faz requisição GET: Envia para
/services/data/v60.0/query?q=... - Processa paginação: Se returnAll=true, busca todos os resultados paginados
- Retorna records: Array de registros encontrados
- Em caso de erro: Retorna mensagem detalhada (erro de sintaxe SOQL, etc.)
Código interno (salesforce-executor.service.ts:1324-1334 e 1366-1387):
private async searchQuery(
nodeData: FlowNodeData,
credentials: SalesforceCredentials,
context: any,
): Promise<any> {
const { query } = nodeData;
if (!query) throw new Error('Search query is required');
const soqlQuery = this.replaceVariables(query, context);
return this.salesforceQuery(soqlQuery, credentials, true);
}
private async salesforceQuery(
query: string,
credentials: SalesforceCredentials,
returnAll: boolean,
): Promise<any> {
const endpoint = `/query?q=${encodeURIComponent(query)}`;
let result = await this.salesforceApiRequest('GET', endpoint, credentials);
if (returnAll && result.nextRecordsUrl) {
const allRecords = [...result.records];
while (result.nextRecordsUrl) {
const nextUrl = result.nextRecordsUrl.replace('/services/data/v60.0', '');
result = await this.salesforceApiRequest('GET', nextUrl, credentials);
allRecords.push(...result.records);
}
return { records: allRecords, totalSize: allRecords.length };
}
return result;
}
Quando você DEVE usar este Node?
Use SALESFORCE_SEARCH_QUERY sempre que precisar de buscar dados com critérios específicos:
Casos de uso
- Evitar duplicatas: Buscar por email/CNPJ antes de criar registro
- Lookup de cliente: Encontrar cliente por telefone ou nome
- Validação: Verificar se registro existe antes de atualizar
- Análise: Contar leads por status, oportunidades por estágio
- Relacionamentos: Buscar todos os contatos de uma empresa
Quando NÃO usar SALESFORCE_SEARCH_QUERY
- Buscar por ID conhecido: Use operação GET diretamente
- Listar todos sem filtro: Use operação GETALL do recurso específico
- Busca full-text: Use Salesforce SOSL (não SOQL) se disponível
Parâmetros Detalhados
query (string, obrigatório)
O que é: Query SOQL completa para buscar dados. Suporta SELECT, FROM, WHERE, ORDER BY, LIMIT.
Sintaxe básica:
SELECT campo1, campo2 FROM Objeto WHERE condicao ORDER BY campo LIMIT quantidade
Exemplos:
-
Buscar lead por email:
SELECT Id, Name, Email FROM Lead WHERE Email = '{{user_email}}' -
Buscar contatos de uma empresa:
SELECT Id, Name, Email FROM Contact WHERE AccountId = '{{account_id}}' -
Contar oportunidades abertas:
SELECT COUNT() FROM Opportunity WHERE IsClosed = false -
Buscar empresas por nome (LIKE):
SELECT Id, Name, Phone FROM Account WHERE Name LIKE '%{{company_name}}%'
Flow completo para testar:
{
"name": "Teste Salesforce - Buscar por Email",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "email_1",
"type": "email",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Email",
"parameters": {
"message": "Digite seu email:",
"variableName": "email"
}
}
},
{
"id": "salesforce_1",
"type": "salesforce",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Buscar Lead",
"parameters": {
"resource": "search",
"operation": "query",
"accessToken": "{{salesforce_token}}",
"instanceUrl": "{{salesforce_instance}}",
"query": "SELECT Id, Name, Email, Company FROM Lead WHERE Email = '{{email}}'"
}
}
},
{
"id": "condition_1",
"type": "condition",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Encontrou?",
"parameters": {
"variable": "salesforce_1.totalSize",
"operator": ">",
"value": "0"
}
}
},
{
"id": "message_found",
"type": "message",
"position": { "x": 900, "y": 50 },
"data": {
"label": "Já Existe",
"parameters": {
"message": "Email já cadastrado! Nome: {{salesforce_1.records.0.Name}}"
}
}
},
{
"id": "message_not_found",
"type": "message",
"position": { "x": 900, "y": 150 },
"data": {
"label": "Não Existe",
"parameters": {
"message": "Email não encontrado. Pode prosseguir com cadastro."
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1100, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "email_1" },
{ "source": "email_1", "target": "salesforce_1" },
{ "source": "salesforce_1", "target": "condition_1" },
{ "source": "condition_1", "target": "message_found", "label": "true" },
{ "source": "condition_1", "target": "message_not_found", "label": "false" },
{ "source": "message_found", "target": "end_1" },
{ "source": "message_not_found", "target": "end_1" }
]
}
Teste: Digite um email. O sistema busca no Salesforce e informa se encontrou ou não.
Parâmetros
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| resource | string | Sim | Deve ser "search" |
| operation | string | Sim | Deve ser "query" |
| accessToken | string | Sim | Token OAuth do Salesforce |
| instanceUrl | string | Sim | URL da instância Salesforce |
| query | string | Sim | Query SOQL completa |
Exemplos de Queries SOQL
1. Buscar por campo exato
SELECT Id, Name FROM Account WHERE Phone = '1133334444'
2. Buscar com LIKE (parcial)
SELECT Id, Name FROM Account WHERE Name LIKE '%Tech%'
3. Múltiplas condições (AND)
SELECT Id, Name FROM Lead WHERE Status = 'Open' AND Industry = 'Technology'
4. Condições alternativas (OR)
SELECT Id, Name FROM Contact WHERE Email = '{{email}}' OR Phone = '{{phone}}'
5. Ordenação
SELECT Id, Name, CreatedDate FROM Opportunity ORDER BY CreatedDate DESC LIMIT 10
6. Relacionamentos (JOIN)
SELECT Id, Name, Account.Name FROM Contact WHERE Account.Name = '{{company}}'
7. Agregações
SELECT COUNT() FROM Case WHERE Status = 'Open'
SELECT StageName, SUM(Amount) FROM Opportunity GROUP BY StageName
8. Data/hora
SELECT Id, Name FROM Lead WHERE CreatedDate = TODAY
SELECT Id, Name FROM Opportunity WHERE CloseDate > 2025-01-01 AND CloseDate < 2025-12-31
Exemplo 1: Validar Email Antes de Criar Lead
Objetivo: Buscar se email já existe antes de criar novo lead
JSON para Importar
{
"name": "Validar Email - Salesforce Query",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "email_1",
"type": "email",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Coletar Email",
"parameters": {
"message": "Qual é o seu email?",
"variableName": "user_email"
}
}
},
{
"id": "salesforce_check",
"type": "salesforce",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Verificar Email",
"parameters": {
"resource": "search",
"operation": "query",
"accessToken": "{{salesforce_token}}",
"instanceUrl": "{{salesforce_instance}}",
"query": "SELECT Id, Name, Email, Company FROM Lead WHERE Email = '{{user_email}}' LIMIT 1"
}
}
},
{
"id": "condition_1",
"type": "condition",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Email Existe?",
"parameters": {
"variable": "salesforce_check.totalSize",
"operator": ">",
"value": "0"
}
}
},
{
"id": "message_exists",
"type": "message",
"position": { "x": 900, "y": 50 },
"data": {
"label": "Já Cadastrado",
"parameters": {
"message": "Este email já está cadastrado! Vou transferir você para o time comercial."
}
}
},
{
"id": "input_new",
"type": "input",
"position": { "x": 900, "y": 150 },
"data": {
"label": "Coletar Empresa",
"parameters": {
"message": "Qual é o nome da sua empresa?",
"variableName": "company"
}
}
},
{
"id": "salesforce_create",
"type": "salesforce",
"position": { "x": 1100, "y": 150 },
"data": {
"label": "Criar Lead",
"parameters": {
"resource": "lead",
"operation": "create",
"accessToken": "{{salesforce_token}}",
"instanceUrl": "{{salesforce_instance}}",
"company": "{{company}}",
"lastName": "Lead WhatsApp",
"email": "{{user_email}}"
}
}
},
{
"id": "message_created",
"type": "message",
"position": { "x": 1300, "y": 150 },
"data": {
"label": "Sucesso",
"parameters": {
"message": "Cadastro realizado! Em breve entraremos em contato."
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1100, "y": 50 },
"data": { "label": "Fim Existe" }
},
{
"id": "end_2",
"type": "end",
"position": { "x": 1500, "y": 150 },
"data": { "label": "Fim Criado" }
}
],
"edges": [
{ "source": "start_1", "target": "email_1" },
{ "source": "email_1", "target": "salesforce_check" },
{ "source": "salesforce_check", "target": "condition_1" },
{ "source": "condition_1", "target": "message_exists", "label": "true" },
{ "source": "condition_1", "target": "input_new", "label": "false" },
{ "source": "message_exists", "target": "end_1" },
{ "source": "input_new", "target": "salesforce_create" },
{ "source": "salesforce_create", "target": "message_created" },
{ "source": "message_created", "target": "end_2" }
]
}
Saída esperada (email existe):
Sistema: Qual é o seu email?
Usuário: joao@empresa.com
Sistema: Este email já está cadastrado! Vou transferir você para o time comercial.
Saída esperada (email novo):
Sistema: Qual é o seu email?
Usuário: novo@empresa.com
Sistema: Qual é o nome da sua empresa?
Usuário: Nova Empresa LTDA
Sistema: Cadastro realizado! Em breve entraremos em contato.
Exemplo 2: Buscar Oportunidades Abertas
Objetivo: Listar oportunidades em aberto de um cliente
JSON para Importar
{
"name": "Listar Oportunidades - Salesforce Query",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "variable_1",
"type": "variable",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Account ID",
"parameters": {
"variableName": "account_id",
"value": "0015g00000ABC123"
}
}
},
{
"id": "salesforce_1",
"type": "salesforce",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Buscar Oportunidades",
"parameters": {
"resource": "search",
"operation": "query",
"accessToken": "{{salesforce_token}}",
"instanceUrl": "{{salesforce_instance}}",
"query": "SELECT Id, Name, StageName, Amount, CloseDate FROM Opportunity WHERE AccountId = '{{account_id}}' AND IsClosed = false ORDER BY CloseDate ASC"
}
}
},
{
"id": "condition_1",
"type": "condition",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Tem Oportunidades?",
"parameters": {
"variable": "salesforce_1.totalSize",
"operator": ">",
"value": "0"
}
}
},
{
"id": "message_found",
"type": "message",
"position": { "x": 900, "y": 50 },
"data": {
"label": "Listar",
"parameters": {
"message": "Você tem {{salesforce_1.totalSize}} oportunidade(s) em aberto:\n\n1. {{salesforce_1.records.0.Name}} - {{salesforce_1.records.0.StageName}}"
}
}
},
{
"id": "message_none",
"type": "message",
"position": { "x": 900, "y": 150 },
"data": {
"label": "Nenhuma",
"parameters": {
"message": "Você não possui oportunidades em aberto no momento."
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1100, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "variable_1" },
{ "source": "variable_1", "target": "salesforce_1" },
{ "source": "salesforce_1", "target": "condition_1" },
{ "source": "condition_1", "target": "message_found", "label": "true" },
{ "source": "condition_1", "target": "message_none", "label": "false" },
{ "source": "message_found", "target": "end_1" },
{ "source": "message_none", "target": "end_1" }
]
}
Saída esperada:
Sistema: Você tem 2 oportunidade(s) em aberto:
1. Empresa ABC - Software ERP - Proposal/Price Quote
Resposta do Node
{
"records": [
{
"Id": "00Q5g00000ABC123",
"Name": "João Silva",
"Email": "joao@empresa.com",
"Company": "Empresa XYZ"
}
],
"totalSize": 1,
"done": true
}
Campos retornados:
- records: Array com registros encontrados
- totalSize: Total de registros retornados
- done: true se todos resultados foram retornados
Boas Práticas
✅ SIM:
- Sempre usar LIMIT para evitar retornar muitos registros
- Usar índices (Id, Email, CreatedDate) em WHERE para performance
- Validar existência antes de criar (evitar duplicatas)
- Usar variáveis do contexto em condições WHERE
- Selecionar apenas campos necessários (não SELECT *)
- Usar ORDER BY para resultados ordenados
- Combinar com CONDITION para processar resultados
❌ NÃO:
- Fazer queries sem LIMIT (pode retornar milhares de registros)
- Buscar por campos não indexados em tabelas grandes
- Usar queries muito complexas que demoram muito
- Ignorar erros de sintaxe SOQL
- Concatenar valores diretamente (use variáveis)
- Fazer múltiplas queries quando uma com JOIN resolve
Dicas
💡 Dica 1: Use totalSize em CONDITION para verificar se encontrou registros antes de tentar acessar records.0.
💡 Dica 2: Para buscas por nome parcial, use LIKE: WHERE Name LIKE '%{{search}}%' (funciona como autocomplete).
💡 Dica 3: Combine query com LOOP para processar múltiplos resultados um por um.
💡 Dica 4: Use relacionamentos para buscar dados conectados: Account.Name, Owner.Email, Contact.Phone.
💡 Dica 5: Para contagens rápidas, use SELECT COUNT() ao invés de buscar todos registros e contar.
Próximos Nodes
→ SALESFORCE_LEAD_CREATE - Criar lead se não encontrado → SALESFORCE_CONTACT_GET - Buscar contato específico por ID → SALESFORCE_ACCOUNT_GET - Buscar account específico por ID → CONDITION - Processar resultados da query