В списке пять записей, но заказов только три: один пришёл дважды из разных источников, другой повторился целиком. Уберём повторы по ID через Remove Duplicates в n8n. Заодно проверим опасную ошибку: одинаковый email не делает два разных заказа дублями. AI-модель и API-ключи для этого не нужны.
Результат инструкции
Соберём отдельный ручной workflow из семи узлов. Из массива orders получим отдельные записи, проверим order_id и оставим по одному экземпляру каждого ID. В Ready будут O1, O2 и O3 с суммами 1500, 2400 и 800. Некорректные ID отправим в Review: они не смешаются с результатом удаления дублей.
Схема фильтрует текущий входной набор. Она не удаляет заказы в CRM, не складывает суммы и не запоминает обработку между запусками. Одинаковый ID с разными суммами считается повтором по выбранному ключу: узел оставляет первый экземпляр, а не определяет, какой из них правильный.
Что понадобится
- Отдельный пустой workflow в n8n. Проверенная версия: 2.42.4, Remove Duplicates 2, Edit Fields 3.4.
- Готовый JSON из шага 2. Данные вымышлены, подключать настоящую CRM или почту не нужно.
- Возможность запускать workflow вручную. Платный тариф, AI-модель и внешние credentials в упражнении не используются.
Установить n8n локально через Docker
Шаг 1. Создайте отдельный workflow
Откройте Overview → Create workflow. Не выбирайте работающую схему с платежами, письмами или CRM. Запуск будет через Manual Trigger и кнопку Execute workflow. Publish не нажимайте: расписания и внешних событий в примере нет.
Шаг 2. Импортируйте готовую схему
Скачать n8n-dedup-orders-demo.json
Скопируйте весь JSON файла, щёлкните свободное место нового холста и нажмите Ctrl+V. Можно также открыть More actions → Import from File. Импортируйте только один раз, затем нажмите Zoom to Fit. Имя схемы - ezgpt-dedup-orders-demo.
На холсте должны быть Manual Trigger, Test Orders, Split Orders, Check ID, Deduplicate Orders, Ready и Review. Основная цепочка проходит через Check ID: true ведёт к удалению повторов и Ready, false - к Review. Узлы уже настроены; ниже проверим каждую настройку и результат.
Шаг 3. Проверьте пять исходных записей
Откройте Test Orders. Это Edit Fields: Mode = JSON Output, Include Other Input Fields выключен. В поле JSON должен стоять объект:
Показать код (34 строк)
{
"orders": [
{
"order_id": "O1",
"email": "anna@example.invalid",
"amount": 1500,
"source": "A"
},
{
"order_id": "O1",
"email": "anna@example.invalid",
"amount": 1500,
"source": "B"
},
{
"order_id": "O2",
"email": "anna@example.invalid",
"amount": 2400,
"source": "A"
},
{
"order_id": "O3",
"email": "boris@example.invalid",
"amount": 800,
"source": "A"
},
{
"order_id": "O3",
"email": "boris@example.invalid",
"amount": 800,
"source": "A"
}
]
}
O1 встречается дважды: отличается только source. O3 повторяется целиком. O1 и O2 - разные заказы одного клиента с одинаковым email. Поэтому сравнивать будем order_id, а не email и не все поля сразу. Домены example.invalid и суммы - учебные, не клиентские данные.
Нажмите Execute step. Пока на выходе один item, внутри которого массив orders из пяти объектов. Не ждите пяти отдельных items на этом шаге.
Шаг 4. Разделите массив на отдельные записи
Откройте Split Orders, узел Split Out. Fields To Split Out = orders, Include = No Other Fields. Destination Field Name не задавайте. Нажмите Execute step: OUTPUT должен содержать пять items с order_id на верхнем уровне.
Если order_id остался внутри массива, следующее выражение не сможет проверить ID текущего заказа. Сначала сверяйте INPUT, потом меняйте путь к полю.
Разобрать JSON-массив через Split Out
Шаг 5. Отделите некорректные ID
Откройте Check ID, узел If. Условие типа Boolean: значение в Expression, оператор is true. Проверка такая:
{{ typeof $json.order_id === 'string' && $json.order_id.trim().length > 0 && $json.order_id === $json.order_id.trim() }}
ID должен быть строкой с непустым текстом и без пробелов по краям. Число 1, пустая строка, отсутствующее поле и " O4 " отправятся по false в Review. Включать автоматическое преобразование типов не нужно. В исходном наборе все пять записей пройдут по true.
Проверяем только формат ID, не сумму, email или существование заказа. Не считайте прохождение этого условия разрешением выставлять счёт. Мы намеренно не приводим ID к числу: строки "001" и "1" могут обозначать разные записи.
Шаг 6. Выберите удаление повторов в текущем входе
Откройте Deduplicate Orders, узел Remove Duplicates. В Operation выберите Remove Items Repeated Within Current Input. Не выбирайте режим сравнения с предыдущими executions: это другое поведение, которое здесь не настраиваем.
При такой операции два последовательных запуска с теми же пятью записями оба дадут по три заказа. Узел не превращает ручную схему в защиту от повторного платежа или повторной отправки письма.
Шаг 7. Задайте единственный ключ сравнения
В Compare выберите Selected Fields. В Fields To Compare в режиме Fixed впишите order_id: имя входного поля, без фигурных скобок и без {{ $json.order_id }}. Options: Disable Dot Notation и Remove Other Fields выключены.
Так две записи O1 совпадают по ключу, хотя source отличается. Сумма, email и source первого экземпляра сохраняются. Remove Other Fields включать нельзя, если они нужны в результате. Работа со вложенными ключами в этой инструкции не проверялась.
Шаг 8. Проверьте статусы выходных веток
Откройте Ready: Edit Fields, Mode = Manual Mapping. Поле processing_status имеет тип String и значение Fixed = deduplicated_in_current_input. Include Other Input Fields включён, Input Fields to Include = All. У Review настройки сохранения такие же, но статус = invalid_order_id.
Ready означает только «после фильтрации повторов в этом входе», а не «заказ полностью проверен». Review содержит записи с неподходящим ID. Копии, отфильтрованные Remove Duplicates, в Review не попадают: отдельного хранилища дублей или аудиторского журнала схема не создаёт.
Шаг 9. Запустите всю цепочку и сверьте три заказа
Закройте панель узла, нажмите Execute workflow на холсте. Откройте Ready → OUTPUT → JSON. Должно быть три записи:
[
{
"order_id": "O1",
"email": "anna@example.invalid",
"amount": 1500,
"source": "A",
"processing_status": "deduplicated_in_current_input"
},
{
"order_id": "O2",
"email": "anna@example.invalid",
"amount": 2400,
"source": "A",
"processing_status": "deduplicated_in_current_input"
},
{
"order_id": "O3",
"email": "boris@example.invalid",
"amount": 800,
"source": "A",
"processing_status": "deduplicated_in_current_input"
}
]
Сверьте ID O1, O2, O3, суммы 1500, 2400, 800 и source = A у каждой. O2 сохранился, несмотря на одинаковый email с O1. Review пуст. Повторите полный запуск: снова должны быть три записи, а не ноль. Такое поведение проверено повторным запуском того же workflow без переимпорта.
Шаг 10. Проверьте, почему email и все поля не подходят
В Deduplicate Orders временно выберите Compare = All Fields и выполните всю цепочку. Ready получит четыре записи: обе O1 останутся из-за разных source, а точная копия O3 исчезнет. Это корректное сравнение целых объектов, но не удаление повторов заказов по ID.
Теперь верните Compare = Selected Fields, а Fields To Compare временно замените на email. Выполните всю цепочку. В Ready останутся только два заказа:
[
{
"order_id": "O1",
"email": "anna@example.invalid",
"amount": 1500,
"source": "A",
"processing_status": "deduplicated_in_current_input"
},
{
"order_id": "O3",
"email": "boris@example.invalid",
"amount": 800,
"source": "A",
"processing_status": "deduplicated_in_current_input"
}
]
O2 исчез, потому что email совпал с O1. Это ошибка выбора ключа, не ошибка n8n. Верните order_id и снова выполните весь workflow: результат должен совпасть с шагом 9. Проверять только счётчик items недостаточно: всегда сверяйте конкретные ID.
Шаг 11. Проверьте конфликт сумм и порядок записей
Оставьте сравнение по order_id. В Test Orders временно замените JSON следующим набором: во второй копии O1 сумма отличается.
Показать код (34 строк)
{
"orders": [
{
"order_id": "O1",
"email": "anna@example.invalid",
"amount": 1500,
"source": "A"
},
{
"order_id": "O1",
"email": "anna@example.invalid",
"amount": 9999,
"source": "B"
},
{
"order_id": "O2",
"email": "anna@example.invalid",
"amount": 2400,
"source": "A"
},
{
"order_id": "O3",
"email": "boris@example.invalid",
"amount": 800,
"source": "A"
},
{
"order_id": "O3",
"email": "boris@example.invalid",
"amount": 800,
"source": "A"
}
]
}
После полного запуска у O1 останется amount = 1500 и source = A: это первый экземпляр. Теперь поменяйте местами только два объекта O1 и повторите запуск. У O1 будет amount = 9999 и source = B. Узел выбирает первый экземпляр, не самый новый и не самый достоверный. Эти два результата проверены отдельно.
Если для одного ID пришли разные цены, статусы или даты, одной дедупликации мало. До внешних действий нужен отдельный этап поиска конфликтов и правило выбора версии. В нашем примере такой блок не построен: не используйте результат для автоматической оплаты. Верните исходный JSON из шага 3.
Шаг 12. Проверьте ошибки ID и восстановите пример
В Test Orders временно вставьте тест из шести записей:
Показать код (33 строк)
{
"orders": [
{
"row": "I1",
"amount": 100
},
{
"row": "I2",
"order_id": "",
"amount": 200
},
{
"row": "I3",
"order_id": " ",
"amount": 300
},
{
"row": "I4",
"order_id": 1,
"amount": 400
},
{
"row": "I5",
"order_id": " O4 ",
"amount": 500
},
{
"row": "I6",
"order_id": "O4",
"amount": 600
}
]
}
Выполните всю цепочку. В Ready должен остаться только I6 с order_id = O4 и суммой 600. В Review должно быть пять записей с сохранёнными исходными полями и статусом invalid_order_id. Для быстрой сверки их идентификаторы строк и статусы:
[
{
"row": "I1",
"processing_status": "invalid_order_id"
},
{
"row": "I2",
"processing_status": "invalid_order_id"
},
{
"row": "I3",
"processing_status": "invalid_order_id"
},
{
"row": "I4",
"processing_status": "invalid_order_id"
},
{
"row": "I5",
"processing_status": "invalid_order_id"
}
]
I1 не имеет order_id, I2 содержит пустую строку, I3 - пробелы, I4 - число, I5 - ID с пробелами по краям. Это разные причины ручного разбора, а не дубли одного заказа. Если отдельным тестом отправить строковые ID O1, o1, 001 и 1, схема сохранит все четыре: регистр и ведущие нули здесь не нормализуются.
Верните исходный JSON шага 3, Selected Fields и order_id. Последний полный запуск должен снова дать ровно O1, O2 и O3 с исходными суммами. Оставьте схему ручной и неопубликованной. Пустой массив orders должен дать ноль записей; не включайте Always Output Data ради искусственного item.
Если результат отличается
| Симптом | Причина | Что сделать |
|---|---|---|
| Четыре заказа вместо трёх | Сравнение All Fields | Selected Fields → order_id |
| Пропал заказ O2 | Сравнение по email | Сравнивать ID заказа |
| order_id не найден | Массив не разделён | Split Out → orders |
| Пропали суммы и email | Remove Other Fields | Выключить опцию |
| Запись ушла в Review | ID пуст, не строка или с пробелами | Проверить исходный формат ID |
| Выбрана неверная сумма | Первый экземпляр ID | Отдельно разбирать конфликт версий |
Что изменить перед подключением настоящих заказов
Сначала проверьте реальный источник: ID может называться id, external_id или состоять из нескольких полей. Если два магазина используют одинаковые номера заказов, одного order_id недостаточно: ключ должен учитывать магазин. Текущий пример этого не делает.
Получить JSON через HTTP Request
Проверить входные данные через If
Не удаляйте записи в CRM, не отправляйте письма и не создавайте платежи сразу после Ready. Для настоящей интеграции отдельно нужны проверка обязательных полей, обработка конфликтов, сохранение ошибок и защита от повторного выполнения внешнего действия. Дедупликация одного входного массива не гарантирует идемпотентность и не предотвращает одновременную обработку двумя запусками.
Что проверено редакцией
10 октября 2026 года пример выполнен на n8n 2.42.4. Девять CLI-сценариев в отдельной тестовой базе: исходный набор, повторный запуск того же workflow, все поля, email, конфликт сумм, обратный порядок конфликтующих записей, неверные ID, регистр и ведущие нули, пустой массив. Все проверки прошли. В отдельном workflow локального редактора выполнены исходный пример, ошибочный ключ email и восстановление order_id; исходный и восстановленный OUTPUT совпали с CLI-результатом. Старые workflows не изменены, новая схема не опубликована, учебный контейнер снова остановлен. Cloud, CRM, режим предыдущих executions, вложенные ключи, большие наборы, конкурентные запуски и мобильная вёрстка не тестировались. Обложка - редакционная иллюстрация, не снимок интерфейса.
Частые вопросы
Почему при следующем запуске заказы снова проходят?
Выбрана операция Within Current Input. Она сравнивает только записи текущего входа, а не историю обработанных заказов. В тесте два запуска подряд дали по три записи. Для внешних действий нужна отдельная защита от повторного выполнения.
Можно ли удалять дубли по email?
Только если email действительно является ключом нужной сущности. В нашем списке один клиент сделал O1 и O2: сравнение по email ошибочно убрало O2. Для заказов используем ID заказа.
Какой экземпляр останется при одинаковом ID?
В проверенном примере сохраняется первый экземпляр. При смене порядка двух O1 поменялась выбранная сумма. Remove Duplicates не устанавливает правильную версию и не складывает значения.
Куда попадают записи без ID?
Узел Check ID отправляет их в Review со статусом invalid_order_id. В проверке пять неправильных ID ушли туда, одна корректная запись прошла дальше. Review не является списком удалённых дублей.
Удалит ли workflow дубли в моей CRM?
Нет. В примере только вымышленные данные и локальная фильтрация выходных items. Внешних credentials и узлов записи нет. Исходные записи в CRM не читаются и не меняются.