Mongoose — Генерировать ObjectId для каждого объекта перед сохранением документа

#javascript #mongodb #mongoose

#javascript #node.js #mongoose #mongoose-схема #objectid

Вопрос:

Я хочу сгенерировать ObjectId для каждого объекта, присутствующего внутри моего массива. Дело в том, что я получаю продукты с помощью .Оператор forEach с другого сервера и помещает их внутрь моего массива без схемы, которая генерирует ObjectId….

Схема продукта:

 const productsSchema = new mongoose.Schema({

  apiKey: String,
  domain: String,
  totalcount: Number,
  totaldone: Number,
  allSKUS: Array,
  allProducts: Array,
  created_at: { type: Date },
  updated_at: { type: Date },

}, { collection: 'products', timestamps: true });

productsSchema.plugin(uniqueValidator);

const Products = mongoose.model('Products', productsSchema);

module.exports = Products;
  

Мой код:

 const newProduct = {

  apiKey: userApiProducts.apiKey,
  domain: userApiProducts.domain,
  totalcount: userApiProducts.totalcount,
  totaldone: userApiProducts.totaldone,
  allSKUS: userApiProducts.allSKUS,
  allProducts: userApiProducts.allProducts // generate ObjectID for each object that gets pushed inside the Array
};

Products.findOneAndUpdate( userApiProducts.domain, newProduct, {upsert:true} , (err, existingProducts) => {
  if (err) { return next(err); }
});
  

Вывод:

 // Please Check ADD OBJECT ID HERE comment. This is where i want to generate an unique ObjectID before I push the data. I tried with var id = mongoose.Types.ObjectId(); but i'm afraid it will not be Unique...

{
        "_id" : ObjectId("58780a2c8d94cf6a32cd7530"),
        "domain" : "http://example.com",
        "updatedAt" : ISODate("2017-01-12T23:27:15.465Z"),
        "apiKey" : "nf4fh3attn5ygkq1t",
        "totalcount" : 11,
        "totaldone" : 11,
        "allSKUS" : [
                "Primul",
                "Al doilea",
                "Al treilea"
        ],
        "allProducts" : [
            {
                // ADD OBJECT ID HERE
                "id": 1,
                "sku": "Primul",
                "name": "Primul",
                "status": 1,
                "total_images": 2,
                "media_gallery_entries": [
                    {
                        "id": 1,
                        "media_type": "image",
                        "label": null,
                        "position": 1,
                        "disabled": false,
                        "types": [
                            "image",
                            "small_image",
                            "thumbnail",
                            "swatch_image"
                        ],
                        "file": "/g/r/grafolio_angel_and_devil.png"
                    },
                    {
                        "id": 2,
                        "media_type": "image",
                        "label": null,
                        "position": 2,
                        "disabled": false,
                        "types": [],
                        "file": "/g/r/grafolio_angel_and_devil_thumbnail.jpg"
                    }
                ]
            },
            {
                // ADD OBJECT ID HERE
                "id": 3,
                "sku": "Al doilea",
                "name": "Al doilea",
                "status": 1,
                "total_images": 2,
                "media_gallery_entries": [
                    {
                        "id": 4,
                        "media_type": "image",
                        "label": null,
                        "position": 2,
                        "disabled": false,
                        "types": [],
                        "file": "/g/r/grafolio_angel_and_devil_thumbnail_1.jpg"
                    },
                    {
                        "id": 5,
                        "media_type": "image",
                        "label": null,
                        "position": 3,
                        "disabled": false,
                        "types": [],
                        "file": "/b/e/before.png"
                    }
                ]
            }, etc ......
        ],
        "__v" : 0,
        "createdAt" : ISODate("2017-01-12T22:58:52.524Z")
}
  

Есть ли какой-либо способ сделать это без необходимости выполнять тонну вызовов DB? Я не могу представить сохранение таким образом

 array.forEach((x)=> {
    Products.save({})
}) 
  

Надеюсь, кто-то уже работал над чем-то подобным и нашел идеальное решение для этого!

Ответ №1:

Если вы хотите добавлять ObjectId автоматически, вам нужно определить для него отдельную схему и установить для _id параметров схемы значение true.

Выполните следующее:

  • Измените свой productsSchema как CatalogueSchema (для простоты понимания).
  • Определите новый ProductSchema для продукта (элемент allProducts)
  • В CatalogueSchema определить тип allProducts как [Product.schema] . Это автоматически добавит _id ( ObjectId ).

Кроме того, вам не нужно добавлять created_at и updated_at как часть схемы, когда вы устанавливаете параметр timestamps как true.

Схема каталога

 const Product = require('Product_Schema_Module_Path'); // Edit

const CatalogueSchema = new mongoose.Schema({

    apiKey: String,
    domain: String,
    totalcount: Number,
    totaldone: Number,
    allSKUS: Array,
    allProducts: [Product.schema]   
    // Note the change here (Array -> [Product.schema]
  // Creating a separate schema ensures automatic id (ObjectId)

}, { collection: 'catalogue', timestamps: true });

CatalogueSchema.plugin(uniqueValidator);

const Catalogue = mongoose.model('Catalogue', CatalogueSchema);
module.exports = Catalogue;
  

Схема продукта
(Новая схема для обеспечения добавления ObjectId)

 const ProductSchema = new mongoose.Schema({

    id: Number,
    sku: String,
    name: String,
    status: Number,
    total_images: Number,
    media_gallery_entries: Array

}, { _id: true, timestamps: true });  
// _id option is true by default. You can ommit it.
// If _id is set to false, it will not add ObjectId

ProductSchema.plugin(uniqueValidator);

const Product = mongoose.model('Product', ProductSchema);
module.exports = Product;
  

РЕДАКТИРОВАТЬ (сохранять товары в каталоге)

(Также обратите внимание, что вам должен потребоваться модуль ProductSchema в вашем модуле CatalogueSchema)

 // Map userApiProducts.allProducts to array of Product documents
const products = userApiProducts.allProducts.map(product => {
    return new Product(product);
})

const newProduct = {
    apiKey: userApiProducts.apiKey,
    domain: userApiProducts.domain,
    totalcount: userApiProducts.totalcount,
    totaldone: userApiProducts.totaldone,
    allSKUS: userApiProducts.allSKUS,
    allProducts: products
};

Catalogue
    .findOneAndUpdate({ domain: userApiProducts.domain }, newProduct, { upsert:true } , (err, products) => {
    // Handle error
});
  

Комментарии:

1. С помощью вашего метода я получаю [Product.schema] не определено.. Не могли бы вы, пожалуйста, показать мне, как бы вы сохранили модель ProductsSchema внутри CatalogueSchema?

2. Создавать отдельно CatalogueSchema и ProductSchema в отдельных модулях. Убедитесь, что вы импортировали ProductSchema в CatalogueSchema . Я добавил код для добавления продуктов в каталог.

3. Вау, это действительно сработало. Огромное спасибо Сантану!

4. Рад, что смог помочь!

Ответ №2:

Чтобы добавить несколько документов в Mongo, вы можете использовать db.collection.insert() :

В Mongoose вы можете использовать Model.insertMany() :

Но имейте в виду, что когда у вас есть один документ внутри другого документа в Mongoose, они на самом деле хранятся не так, как в Mongo. Mongo хранит только идентификаторы дочерних документов, а не их содержимое в родительском документе — и даже никакой информации о том, к какой коллекции принадлежат эти идентификаторы.

Когда вы используете population, Mongoose фактически извлекает соответствующие документы из базы данных в отдельных запросах к Mongo. Итак, population — это концепция Mongoose. Mongo просто хранит идентификаторы, поэтому вам нужно сначала создать документы, прежде чем вы сможете вставить идентификаторы.

То, что вы пытаетесь сделать, было бы легко без использования Mongoose. Вы можете сохранить несколько документов в одном запросе в Mongo, используя свои собственные идентификаторы, если хотите, и вы можете сохранить другой документ с массивом этих идентификаторов в другом запросе.

Конечно, как бы вы это ни делали, вы получите несогласованное состояние во время операции, потому что Mongo не поддерживает транзакции.

Комментарии:

1. Я пробовал insertMany, но он не генерирует ObjectId. Если я попробую другим способом, сгенерировав продукты в другой схеме и используя .find для запроса к ним. В пакетном процессе это будет немного медленнее, чем иметь документ внутри другого документа?