Проверка данных в MongoDB

MongoDBMongoDBBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом практическом занятии вы научитесь проверять типы данных в MongoDB, обрабатывать некорректные данные и предотвращать плохой ввод, чтобы сохранить целостность данных в вашей базе данных MongoDB. Вы начнете с изучения различных типов данных, поддерживаемых MongoDB, и создания примерной коллекции с определенными требованиями по типам данных. Затем вы попрактикуетесь в вставке документов с неправильными типами и узнаете, как правильно вставлять данные, соответствующие критериям проверки. В конце этого практического занятия у вас будет твердый уровень знаний о проверке данных и техниках обработки ошибок в MongoDB.

Проверка типов данных

В этом шаге вы научитесь проверять и валидировать типы данных в MongoDB, что является важным для поддержания целостности данных и предотвращения ошибок в вашей базе данных.

Понимание типов данных в MongoDB

MongoDB поддерживает различные типы данных, в том числе:

  • String
  • Integer
  • Double
  • Boolean
  • Array
  • Object
  • Date
  • ObjectId
  • Null

Пустим, мы открываем MongoDB shell и исследуем валидацию типов данных:

mongosh

Создание примерной коллекции с валидацией типа

Мы создадим коллекцию users с определенными требованиями по типам данных:

use dataValidationLab

db.createCollection("users", {
   validator: {
      $jsonSchema: {
         bsonType: "object",
         required: ["name", "age", "email"],
         properties: {
            name: {
               bsonType: "string",
               description: "должен быть строкой и обязателен"
            },
            age: {
               bsonType: "int",
               minimum: 18,
               maximum: 120,
               description: "должен быть целым числом в диапазоне от 18 до 120"
            },
            email: {
               bsonType: "string",
               pattern: "^.+@.+$",
               description: "должен быть действительным адресом электронной почты"
            }
         }
      }
   }
})

Рассмотрим валидацию подробнее:

  • bsonType: "string" гарантирует, что поле является строкой
  • bsonType: "int" гарантирует, что поле является целым числом
  • minimum и maximum задают ограничения диапазона
  • pattern проверяет формат электронной почты

Попытка вставить документы с неправильными типами

Теперь попробуем вставить документы, чтобы проверить нашу валидацию:

// Это не пройдет из-за неправильного типа возраста
db.users.insertOne({
  name: "John Doe",
  age: "25", // Строка вместо целого числа
  email: "[email protected]"
});

// Это также не пройдет из-за недействительного адреса электронной почты
db.users.insertOne({
  name: "Jane Smith",
  age: 30,
  email: "invalid-email"
});

Корректная вставка документа

Вот, как вставить действительный документ:

db.users.insertOne({
  name: "Alice Johnson",
  age: NumberInt(28),
  email: "[email protected]"
});

Обратите внимание на использование NumberInt() для явного создания целого числа.

Валидация обязательных полей

В этом шаге мы будем продолжать работу по валидации данных, сосредоточившись на том, чтобы гарантировать, что обязательные поля всегда присутствуют в документах MongoDB.

Понимание обязательных полей

Обязательные поля - это важные части информации, которые должны присутствовать в каждом документе. В предыдущем шаге мы создали схему валидации с обязательными полями. Теперь мы исследуем, как обеспечить и проверить эти требования.

Продолжим работу в MongoDB shell:

mongosh

Переключимся на нашу существующую базу данных:

use dataValidationLab

Определение строгой валидации обязательных полей

Мы изменим нашу существующую коллекцию users, чтобы обеспечить строгую валидацию обязательных полей:

db.runCommand({
  collMod: "users",
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "age", "email", "registrationDate"],
      properties: {
        name: {
          bsonType: "string",
          description: "Имя обязательно и должно быть строкой"
        },
        age: {
          bsonType: "int",
          minimum: 18,
          maximum: 120,
          description:
            "Возраст обязателен и должен быть целым числом в диапазоне от 18 до 120"
        },
        email: {
          bsonType: "string",
          pattern: "^.+@.+$",
          description:
            "Email обязателен и должен быть действительным адресом электронной почты"
        },
        registrationDate: {
          bsonType: "date",
          description: "Дата регистрации обязательна и должна быть датой"
        }
      }
    }
  },
  validationLevel: "strict"
});

Попытка вставить документы с отсутствующими полями

Попробуем вставить документы с отсутствующими обязательными полями:

// Это не пройдет из-за отсутствия registrationDate
db.users.insertOne({
  name: "Bob Wilson",
  age: NumberInt(35),
  email: "[email protected]"
});

// Это также не пройдет из-за отсутствия обязательных полей
db.users.insertOne({
  name: "Charlie Brown"
});

Корректная вставка документа

Вот, как вставить документ, который соответствует всем требованиям:

db.users.insertOne({
  name: "Emma Thompson",
  age: NumberInt(42),
  email: "[email protected]",
  registrationDate: new Date()
});

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

Чтобы понять, почему вставка не проходит, MongoDB предоставляет сообщения об ошибках валидации:

try {
  db.users.insertOne({
    name: "Incomplete User"
  });
} catch (error) {
  print("Validation Error:", error.message);
}

Это поможет вам точно понять, какие обязательные поля отсутствуют.

Обработка некорректных данных

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

Подготовка окружения

Продолжим работу в MongoDB shell:

mongosh

Переключимся на нашу существующую базу данных:

use dataValidationLab

Создание коллекции для обработки ошибок

Мы создадим новую коллекцию с более продвинутой валидацией и обработкой ошибок:

db.createCollection("products", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "price", "category"],
      properties: {
        name: {
          bsonType: "string",
          minLength: 2,
          maxLength: 100
        },
        price: {
          bsonType: ["double", "int"],
          minimum: 0,
          description: "Цена должна быть положительным числом"
        },
        category: {
          enum: ["Electronics", "Clothing", "Books", "Food"],
          description:
            "Категория должна быть одной из предопределенных значений"
        }
      }
    }
  },
  validationAction: "error"
});

Реализация механизма обработки ошибок

Мы создадим функцию для обработки вставки некорректных данных:

function safeInsertProduct(product) {
  try {
    db.products.insertOne(product);
    print("Товар успешно вставлен:", product.name);
  } catch (error) {
    print("Ошибка при вставке товара:", error.message);

    // Записать некорректные данные в отдельную коллекцию
    db.invalidProducts.insertOne({
      product: product,
      errorMessage: error.message,
      timestamp: new Date()
    });
  }
}

Тестирование обработки ошибок

Протестируем нашу обработку ошибок на различных сценариях:

// Вставка валидного товара
safeInsertProduct({
  name: "Смартфон",
  price: 599.99,
  category: "Electronics"
});

// Некорректный товар - неправильный тип цены
safeInsertProduct({
  name: "Ноутбук",
  price: "1000", // Строка вместо числа
  category: "Electronics"
});

// Некорректный товар - неправильная категория
safeInsertProduct({
  name: "Футболка",
  price: 29.99,
  category: "Accessories" // Не в предопределенных категориях
});

// Некорректный товар - короткое имя
safeInsertProduct({
  name: "A",
  price: 10,
  category: "Books"
});

Просмотр журналов некорректных данных

Проверим логи некорректных товаров:

print("Журнал некорректных товаров:");
db.invalidProducts.find();

Лучшие практики по обработке ошибок

  1. Всегда валидировать данные перед вставкой
  2. Использовать блоки try-catch
  3. Логировать некорректные данные для последующего просмотра
  4. Реализовать валидацию на уровне схемы
  5. Использовать соответствующие действия валидации

Исправление проблем с данными

В этом шаге мы научимся идентифицировать и исправлять проблемы с данными в MongoDB, сосредоточившись на стратегиях очистки и преобразования данных.

Подготовка окружения

Продолжим работу в MongoDB shell:

mongosh

Переключимся на нашу существующую базу данных:

use dataValidationLab

Создание коллекции для очистки данных

Мы создадим коллекцию с потенциально проблематическими данными:

db.createCollection("customers", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "email", "age"],
      properties: {
        name: {
          bsonType: "string",
          minLength: 2
        },
        email: {
          bsonType: "string",
          pattern: "^.+@.+$"
        },
        age: {
          bsonType: "int",
          minimum: 18,
          maximum: 120
        }
      }
    }
  }
});

// Вставим несколько примеров данных с потенциальными проблемами
db.customers.insertMany([
  { name: "John Doe", email: "john@example", age: "35" },
  { name: "A", email: "invalid-email", age: 15 },
  { name: "Jane Smith", email: "[email protected]", age: 25 }
]);

Функция очистки данных

Создадим функцию для очистки и валидации данных о клиентах:

function cleanCustomerData() {
  // Найдем и исправим невалидные документы
  const invalidCustomers = db.customers.find({
    $or: [
      { age: { $type: "string" } },
      { email: { $not: /^.+@.+$/ } },
      {
        name: { $exists: true, $expr: { $lt: [{ $strLenBytes: "$name" }, 2] } }
      }
    ]
  });

  invalidCustomers.forEach((customer) => {
    // Исправим возраст: преобразуем в целое число
    const fixedAge =
      typeof customer.age === "string"
        ? parseInt(customer.age)
        : customer.age < 18
          ? 18
          : customer.age;

    // Исправим email: добавим домен, если он отсутствует
    const fixedEmail = customer.email.includes("@")
      ? customer.email
      : `${customer.email}@example.com`;

    // Исправим имя: дополним короткие имена
    const fixedName =
      customer.name.length < 2 ? customer.name.padEnd(2, "X") : customer.name;

    // Обновим документ с исправленными данными
    db.customers.updateOne(
      { _id: customer._id },
      {
        $set: {
          age: NumberInt(fixedAge),
          email: fixedEmail,
          name: fixedName
        }
      }
    );

    print(`Исправлен клиент: ${customer.name}`);
  });

  // Проверим очищенные данные
  print("Проверка очищенных данных:");
  db.customers.find().forEach(printjson);
}

// Запустим функцию очистки
cleanCustomerData();

Валидация данных после очистки

Проверим, соответствуют ли очищенные данные нашим критериям валидации:

function validateCustomers() {
  const invalidCustomersCount = db.customers
    .find({
      $or: [
        { age: { $not: { $type: "int" } } },
        { age: { $lt: 18, $gt: 120 } },
        { email: { $not: /^.+@.+$/ } },
        {
          name: {
            $exists: true,
            $expr: { $lt: [{ $strLenBytes: "$name" }, 2] }
          }
        }
      ]
    })
    .count();

  print(`Количество оставшихся невалидных клиентов: ${invalidCustomersCount}`);
  return invalidCustomersCount === 0;
}

validateCustomers();

Лучшие практики по исправлению данных

  1. Всегда валидируйте данные перед и после преобразования
  2. Используйте преобразование типов осторожно
  3. Задайте значения по умолчанию или минимальные значения для критических полей
  4. Логируйте изменения данных для аудита
  5. Создайте резервную копию перед крупной очисткой данных

Предотвращение плохих входных данных

В этом финальном шаге мы исследуем продвинутые методы предотвращения плохих входных данных и повышения целостности данных в MongoDB с помощью комплексных стратегий валидации.

Подготовка окружения

Продолжим работу в MongoDB shell:

mongosh

Переключимся на нашу существующую базу данных:

use dataValidationLab

Создание надежной схемы валидации

Мы создадим комплексную схему валидации для коллекции'registrations':

db.createCollection("registrations", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["username", "email", "password", "registrationDate"],
      properties: {
        username: {
          bsonType: "string",
          minLength: 3,
          maxLength: 20,
          pattern: "^[a-zA-Z0-9_]+$",
          description:
            "Имя пользователя должно быть алфавитно-цифровым, от 3 до 20 символов"
        },
        email: {
          bsonType: "string",
          pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
          description: "Должно быть действительным адресом электронной почты"
        },
        password: {
          bsonType: "string",
          minLength: 8,
          description: "Пароль должен быть не менее 8 символов",
          pattern:
            "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,}$"
        },
        registrationDate: {
          bsonType: "date",
          description: "Дата регистрации должна быть действительной датой"
        }
      }
    }
  },
  validationAction: "error",
  validationLevel: "strict"
});

Реализация промежуточного слоя предотвращения входных данных

Создадим функцию для валидации входных данных перед вставкой:

function safeRegister(userData) {
  // Очистим и проверим входные данные
  const sanitizedData = {
    username: userData.username.trim().toLowerCase(),
    email: userData.email.trim().toLowerCase(),
    password: userData.password,
    registrationDate: new Date()
  };

  // Дополнительные пользовательские проверки
  if (sanitizedData.username.length < 3) {
    throw new Error("Имя пользователя слишком короткое");
  }

  if (db.registrations.findOne({ username: sanitizedData.username })) {
    throw new Error("Имя пользователя уже существует");
  }

  if (db.registrations.findOne({ email: sanitizedData.email })) {
    throw new Error("Email уже зарегистрирован");
  }

  try {
    // Попытаемся вставить с использованием валидации MongoDB
    db.registrations.insertOne(sanitizedData);
    print("Регистрация успешна:", sanitizedData.username);
  } catch (error) {
    print("Регистрация не удалась:", error.message);

    // Запишем неудачные попытки регистрации
    db.registrationAttempts.insertOne({
      attemptData: userData,
      errorMessage: error.message,
      timestamp: new Date()
    });
  }
}

// Проверим предотвращение входных данных
function testRegistrations() {
  const testCases = [
    // Валидная регистрация
    {
      username: "john_doe",
      email: "[email protected]",
      password: "Strong!Pass123"
    },

    // Невалидные случаи
    { username: "ab", email: "invalid-email", password: "short" },
    { username: "john_doe!", email: "[email protected]", password: "WeakPass" },
    {
      username: "special_chars!",
      email: "invalid@email",
      password: "NoSpecialChar123"
    }
  ];

  testCases.forEach((testCase) => {
    print("\nПроверка регистрации:");
    printjson(testCase);
    safeRegister(testCase);
  });
}

// Запустим тесты регистрации
testRegistrations();

Просмотр попыток регистрации

Проверим логированные попытки регистрации:

print("Неудачные попытки регистрации:");
db.registrationAttempts.find();

Лучшие практики по предотвращению входных данных

  1. Используйте комплексную валидацию схемы
  2. Реализуйте очистку входных данных на стороне сервера
  3. Используйте регулярные выражения для строгой валидации
  4. Логируйте и отслеживайте неудачные попытки ввода
  5. Реализуйте дополнительную пользовательскую логику валидации

Резюме

В этом практическом занятии вы узнали, как проверять и валидировать типы данных в MongoDB, чтобы поддерживать целостность данных и предотвратить ошибки в вашей базе данных. Вы ознакомились с различными типами данных, поддерживаемыми MongoDB, включая строку, целое число, дробное число, логическое значение, массив, объект, дату, ObjectId и null. Вы создали примерную коллекцию users с конкретными требованиями по типам данных, например, убедившись, что имя является строкой, возраст - целым числом в диапазоне от 18 до 120, а email - действительным адресом электронной почты. Вы также узнали, как обрабатывать некорректные данные, пытаясь вставить документы с неправильными типами и понимая сообщения об ошибках.

Наконец, вы практиковали вставку документов с правильными типами данных, чтобы успешно добавить их в коллекцию users.