Как получить доступ к полям MongoDB, в именах которых содержатся точки? У меня есть поле с названием ‘No. of items’, но MongoDB интерпретирует точку как разделитель полей, воспринимая его как ‘No’ -> ‘of items’. Я пробовал использовать скобочную нотацию, но это не работает. Вот мой текущий код:
key = `mappedTaxonomies.${orgId}.attributes.${column}.value`;
Как правильно получить доступ к полю ‘No. of items’ в его исходном виде?
MongoDB интерпретирует точки в именах полей как разделители для нотации с точками, что вызывает проблему, с которой вы сталкиваетесь с полем ‘No. of items’. Хотя скобочная нотация работает с объектами JavaScript, она не решает проблему интерпретации MongoDB внутренней нотации с точками. Для правильного доступа к полям с точками в именах необходимо использовать операторы конвейера агрегации MongoDB или альтернативные подходы.
Содержание
- Понимание проблемы
- Решение 1: Использование операторов агрегации
- Решение 2: Кодирование имен полей
- Решение 3: Альтернативная структура запросов
- Лучшие практики
- Полный пример
Понимание проблемы
Когда у вас есть имя поля, содержащее точку, например ‘No. of items’, парсер нотации с точками MongoDB рассматривает точку как разделитель между вложенными путями полей. Это означает, что mappedTaxonomies.${orgId}.attributes.${column}.value, где ${column} равно ‘No. of items’, интерпретируется как доступ к вложенной структуре полей, а не к одному полю с точкой в имени.
Согласно документации MongoDB, использование имен полей, содержащих точки, не рекомендуется и может вызывать непредвиденное поведение в запросах и обновлениях.
Проблема возникает из-за того, что парсер запросов MongoDB ожидает, что точки будут представлять разделители путей полей, а не буквальные символы в именах полей.
Решение 1: Использование операторов агрегации
Для MongoDB версии 4.4 и выше вы можете использовать операторы агрегации $getField и $setField для доступа к полям с точками в именах:
const result = await collection.aggregate([
{
$match: {
"mappedTaxonomies.orgId.attributes": { $exists: true }
}
},
{
$addFields: {
fieldValue: {
$getField: {
field: "mappedTaxonomies.orgId.attributes.No. of items.value",
input: "$mappedTaxonomies.orgId.attributes"
}
}
}
}
]).toArray();
Или использование $literal для обработки имени поля с точками:
const result = await collection.aggregate([
{
$addFields: {
fieldValue: {
$getField: {
field: { $literal: "No. of items.value" },
input: "$mappedTaxonomies.orgId.attributes"
}
}
}
}
]).toArray();
В документации MongoDB specifically упоминается использование
$getField,$setFieldи$literalдля обработки имен полей, содержащих точки и знаки доллара.
Решение 2: Кодирование имен полей
Практический подход - кодировать точки в именах полей при сохранении и декодировать их при доступе:
// При сохранении данных кодируем точки
function encodeFieldName(fieldName) {
return fieldName.replace(/\./g, 'ENCODE_DOT');
}
// При доступе к данным декодируем точки
function decodeFieldName(fieldName) {
return fieldName.replace(/ENCODE_DOT/g, '.');
}
// Использование:
const encodedColumn = encodeFieldName(column); // 'NoENCODE_DOTofENCODE_DOTitems'
const key = `mappedTaxonomies.${orgId}.attributes.${encodedColumn}.value`;
// После извлечения при необходимости декодируем
const decodedField = decodeFieldName(retrievedField);
Этот подход, упомянутый в обсуждениях Stack Overflow, заменяет точки временным заполнителем, который не будет мешать парсингу MongoDB.
Решение 3: Альтернативная структура запросов
Вы можете перестроить модель данных, чтобы избежать проблемы altogether:
// Вместо вложенной структуры с точками, используйте плоскую структуру
const query = {
"mappedTaxonomies.orgId.attributes": {
$elemMatch: {
"fieldName": "No. of items",
"value": { $exists: true }
}
}
};
const result = await collection.find(query).toArray();
Или используйте другой разделитель в именах полей:
// Замените точки на подчеркивания или другие символы
const column = column.replace(/\./g, '_'); // 'No_of_items'
const key = `mappedTaxonomies.${orgId}.attributes.${column}.value`;
Лучшие практики
-
Избегайте точек в именах полей: Самое простое решение - избегать использования точек в именах полей при проектировании базы данных.
-
Используйте кодирование для существующих данных: Если вам приходится работать с существующими данными, содержащими точки, используйте кодирование/декодирование.
-
Воспользуйтесь операторами агрегации: Для MongoDB 4.4+ используйте
$getFieldи связанные операторы для надежного доступа. -
Документируйте ваш подход: Убедитесь, что ваша команда понимает выбранный метод обработки имен полей, содержащих точки.
-
Рассмотрите миграцию данных: Для долгосрочных решений рассмотрите возможность миграции данных для использования альтернативных имен полей.
Полный пример
Вот полный пример, показывающий, как получить доступ к вашему полю ‘No. of items’:
const MongoClient = require('mongodb').MongoClient;
async function getItemsField(orgId) {
const client = await MongoClient.connect('mongodb://localhost:27017');
const db = client.db('yourDatabase');
const collection = db.collection('yourCollection');
// Метод 1: Использование операторов агрегации (MongoDB 4.4+)
const result1 = await collection.aggregate([
{
$match: {
"mappedTaxonomies": { $elemMatch: { orgId: orgId } }
}
},
{
$addFields: {
fieldValue: {
$getField: {
field: { $literal: "No. of items.value" },
input: { $arrayElemAt: ["$mappedTaxonomies.orgId.attributes", 0] }
}
}
}
}
]).toArray();
// Метод 2: Использование кодированных имен полей
const encodedFieldName = "NoENCODE_DOTofENCODE_DOTitems";
const result2 = await collection.find({
[`mappedTaxonomies.${orgId}.attributes.${encodedFieldName}.value`]: { $exists: true }
}).toArray();
client.close();
return { aggregationResult: result1, encodedResult: result2 };
}
// Использование
getItemsField('yourOrgId').then(console.log);
Этот подход дает вам несколько вариантов в зависимости от версии MongoDB и требований к структуре данных. Метод агрегации является наиболее надежным для текущих версий MongoDB, в то время как кодирование обеспечивает хороший обходной путь для существующих схем данных.