Pular para conteúdo

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:

  1. Buscas Complexas: Encontrar registros com múltiplos critérios e filtros avançados
  2. Validação de Existência: Verificar se lead/contact/account já existe antes de criar
  3. Dados Relacionados: Buscar registros com joins e relacionamentos
  4. Agregações: Contar, somar, calcular médias de dados no Salesforce

Como funciona internamente?

Quando o SALESFORCE_SEARCH_QUERY é executado, o sistema:

  1. Recebe a query: Obtém string SOQL completa
  2. Processa variáveis: Substitui variáveis do contexto na query
  3. Valida query: Verifica se query foi informada
  4. Codifica para URL: Prepara query para envio na URL
  5. Autentica no Salesforce: Usa accessToken e instanceUrl
  6. Faz requisição GET: Envia para /services/data/v60.0/query?q=...
  7. Processa paginação: Se returnAll=true, busca todos os resultados paginados
  8. Retorna records: Array de registros encontrados
  9. 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

  1. Evitar duplicatas: Buscar por email/CNPJ antes de criar registro
  2. Lookup de cliente: Encontrar cliente por telefone ou nome
  3. Validação: Verificar se registro existe antes de atualizar
  4. Análise: Contar leads por status, oportunidades por estágio
  5. 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:

  1. Buscar lead por email:

    SELECT Id, Name, Email FROM Lead WHERE Email = '{{user_email}}'
    

  2. Buscar contatos de uma empresa:

    SELECT Id, Name, Email FROM Contact WHERE AccountId = '{{account_id}}'
    

  3. Contar oportunidades abertas:

    SELECT COUNT() FROM Opportunity WHERE IsClosed = false
    

  4. 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