使用Mongoose和Express导入国家JSON到MongoDB集合创建失败如何解决
问题排查与修复方案
核心问题汇总
- 异步操作未等待:
Country.insertMany()是异步方法,原代码未加await关键字,导致请求在数据库写入操作完成前就返回,写入逻辑未执行完成就提前结束。 - Schema类型不匹配:
latitude字段被错误定义为Date类型,实际传入的是纬度字符串,类型校验失败导致数据无法写入。timezones子文档中的gmtOffset字段被定义为String类型,但数据源中该字段是数字类型,类型校验失败。
修正后的Mongoose Schema定义
const mongoose = require('mongoose'); const { toJSON, paginate } = require('./plugins'); const countriesSchema = new mongoose.Schema( { uniqueId: { type: String, default: null }, // 如果你需要保留原数据的数字类型,可以改为 type: Number countryId: { type: String, default: null }, name: { type: String, default: null }, iso3: { type: String, default: null }, iso2: { type: String, default: null }, numeric_code: { type: String, default: null }, phone_code: { type: String, default: null }, capital: { type: String, default: null }, currency: { type: String, default: null }, currency_symbol: { type: String, default: null }, tld: { type: String, default: null }, native: { type: String, default: null }, region: { type: String, default: null }, subregion: { type: String, default: null }, timezones: [{ zoneName: { type: String, trim: true, default: null }, // 修正为Number类型匹配数据源 gmtOffset: { type: Number, trim: true, default: null }, gmtOffsetName: { type: String, trim: true, default: null }, abbreviation: { type: String, trim: true, default: null }, tzName: { type: String, trim: true, default: null }, }], // 修正为String类型匹配传入的纬度值 latitude: { type: String, default: null }, longitude: { type: String, default: null }, emoji: { type: String, default: null }, settings: { preferred_intl_destinations: [{ destinationName: { type:String, default: null }, destinationId: { type:String, default: null }, destinationImg: { imageId: { type:String, default: null }, imageUrl: { type:String, default: null } }, }], preferred_domestic_destinations: [{ destinationName: { type: String, default: null }, destinationId: { type: String, default: null }, destinationImg: { imageId: { type: String, default: null }, imageUrl: { type: String, default: null } }, }], } }, { timestamps: true, } ); countriesSchema.plugin(toJSON); countriesSchema.plugin(paginate); const Country = mongoose.model('ttc_Countries', countriesSchema); module.exports = Country;
修正后的控制器代码
const { Country } = require('../models'); // 确保已经引入axios和uuidv4 const axios = require('axios'); const { v4: uuidv4 } = require('uuid'); const addCountrytoDatabase = catchAsync(async (req, res) => { const { data: countries } = await axios.get(`https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/master/countries.json`); const countrieslist = countries.map(item => ({ uniqueId: uuidv4(), // 如果Schema中countryId改为Number类型,这里不需要转字符串 countryId: String(item.id), name: item.name, iso3: item.iso3, iso2: item.iso2, numeric_code: item.numeric_code, phone_code: item.phone_code, capital: item.capital, currency: item.currency, currency_symbol: item.currency_symbol, tld: item.tld, native: item.native, region: item.region, subregion: item.subregion, timezones: item.timezones, latitude: item.latitude, longitude: item.longitude, })); // 加await等待写入完成 const createdCountries = await Country.insertMany(countrieslist); res.status(201).send(createdCountries); });
补充说明
如果不需要保留emoji和settings相关字段,可以直接从Schema中删除对应定义,减少冗余存储。如果需要避免重复插入国家数据,可以给countryId或者iso2字段添加唯一索引。
内容的提问来源于stack exchange,提问作者Vinay Thakur
相关产品推荐
相关产品推荐

