Уникальность элементов

Уникальность элементов массива в JSON Schema реализуется через ключевое слово uniqueItems, поддерживаемое библиотекой Ajv и используемое для контроля отсутствия дубликатов внутри массивов данных.

Ключ uniqueItems: true накладывает ограничение на массив: каждый его элемент должен быть уникальным относительно остальных. При валидации Ajv выполняет попарное сравнение элементов массива и фиксирует нарушение при обнаружении хотя бы одной повторяющейся пары.

Основной принцип работы:

  • каждый элемент массива сравнивается с остальными;
  • совпадение фиксируется по строгому равенству;
  • при обнаружении дубликатов формируется ошибка валидации.

Важным аспектом является то, что уникальность определяется не только по примитивным значениям, но и по структурам данных.

Сравнение значений и особенности алгоритма

Для проверки уникальности Ajv использует строгую семантику сравнения:

  • примитивы (number, string, boolean, null) сравниваются по значению;
  • объекты сравниваются по структуре и содержимому;
  • массивы сравниваются рекурсивно по элементам.

Таким образом, два объекта с одинаковыми полями и значениями считаются дубликатами, даже если это разные ссылки в памяти.

Пример:

{
  "type": "array",
  "uniqueItems": true,
  "items": {
    "type": "object",
    "properties": {
      "id": { "type": "number" }
    },
    "required": ["id"]
  }
}

Данные:

[
  { "id": 1 },
  { "id": 1 }
]

Результат валидации: ошибка уникальности.

Влияние структуры данных на проверку

Поведение uniqueItems становится особенно значимым при работе со сложными структурами.

Объекты

При проверке объектов учитываются все свойства. Порядок ключей не влияет на результат, так как сравнение выполняется по нормализованной структуре данных.

[
  { "a": 1, "b": 2 },
  { "b": 2, "a": 1 }
]

Элементы считаются идентичными.

Массивы

Массивы сравниваются по длине и поэлементно:

[
  [1, 2],
  [1, 2]
]

Такие элементы трактуются как дубликаты.

Внутренние оптимизации Ajv

В современных версиях Ajv применяется оптимизация проверки уникальности для повышения производительности при работе с большими массивами.

Основные механизмы:

  • использование кэширования сериализованных представлений объектов;
  • сокращение количества сравнений за счёт предварительной индексации;
  • ранний выход при обнаружении первого дубликата;
  • оптимизация для примитивных типов через структуры наподобие Set.

Для примитивных значений проверка часто сводится к использованию хеш-структур, что уменьшает сложность с квадратичной до линейной в типичных случаях.

Сложные случаи сравнения

NaN и специальные значения

Особенности JavaScript влияют на результаты проверки:

  • NaN не равен самому себе по стандартному сравнению, но Ajv учитывает его как одинаковое значение при сериализации;
  • -0 и 0 считаются эквивалентными;
  • undefined в JSON отсутствует, но в промежуточных структурах может учитываться при кастомной обработке.

Вложенные структуры

Глубокая вложенность увеличивает стоимость проверки уникальности. Например:

[
  { "data": { "items": [1, 2, 3] } },
  { "data": { "items": [1, 2, 3] } }
]

Такие элементы требуют рекурсивного обхода всей структуры, что влияет на производительность при больших объёмах данных.

Поведение при ошибках валидации

При нарушении uniqueItems Ajv формирует объект ошибки, содержащий:

  • путь до массива;
  • индекс конфликтующих элементов;
  • описание нарушения уникальности.

Ошибки не агрегируются по всем парам, а фиксируются по факту обнаружения конфликта, что снижает нагрузку при обработке больших массивов.

Влияние строгого режима Ajv

В строгом режиме (strict: true) поведение uniqueItems становится более предсказуемым:

  • запрещаются неоднозначные схемы;
  • усиливается проверка типов;
  • уменьшается вероятность скрытых несоответствий в структуре данных.

Это особенно важно при работе с динамическими API, где структура входящих данных может изменяться.

Ограничения уникальности

Механизм uniqueItems имеет ряд ограничений:

  • не поддерживает частичное сравнение полей объектов;
  • не позволяет задавать кастомную функцию сравнения напрямую через JSON Schema;
  • может существенно влиять на производительность при больших массивах;
  • требует полной сериализации сложных структур для корректного сравнения.

Альтернативные подходы к обеспечению уникальности

В случаях, когда стандартного uniqueItems недостаточно, применяются дополнительные схемы контроля:

  • использование enum для фиксированных наборов значений;
  • применение const для жёстко заданных структур;
  • предварительная нормализация данных до валидации;
  • кастомные ключевые слова Ajv через addKeyword для реализации собственной логики уникальности.

Пример кастомной проверки:

ajv.addKeyword({
  keyword: "uniqueById",
  validate: function (schema, data) {
    const ids = new Set();
    for (const item of data) {
      if (ids.has(item.id)) return false;
      ids.add(item.id);
    }
    return true;
  }
});

Такой подход позволяет ограничить уникальность по конкретному полю, а не по всей структуре объекта.

Производственные аспекты применения uniqueItems

При проектировании схем с uniqueItems учитываются следующие факторы:

  • размер массива напрямую влияет на стоимость проверки;
  • глубокие структуры требуют оптимизации данных до валидации;
  • частое использование может стать узким местом при массовой обработке запросов;
  • предпочтение отдаётся простым типам данных для минимизации затрат.

В высоконагруженных системах часто выполняется предварительная дедупликация на уровне бизнес-логики, а не через JSON Schema, что снижает нагрузку на валидатор Ajv.