关于用户地址表的设计问题 Nodejs相关

关于用户地址表的设计问题 Nodejs相关

很常见的一个需求就是用户的地址啦、收货地址啦什么的,反正就是地址存储,不知道该怎么设计是好…… 初步的打算是分为:国家、省、市、县、乡、其他详细地址 第一种方案:

id       parentid       adrid       adrname
xxxx      81          440000    广东省
yyyy     440000       440300    深圳市
zzzz     440300       440304    福田区
xxxx     440304       4403xx    xx街,xx号(详细地址)
xxxx     440304       4403yy    yy街,yy号(详细地址)

这样的设计有一个问题就是,如果一个用户的地址是 广东省,深圳市,福田区,那么用户关联地址为zzzz,但是从福田区查询到广东省又要查询两次,加载一个用户地址就要查询三次,会不会浪费?

第二种方案是:

id       province      city         area     streeno
xxxx      广东省 
yyyy      广东省       深圳市      福田区
zzzz      广东省       深圳市      福田区    xx街,xx号(详细地址)

这种方案的问题是数据冗余比较大

需求有: 1、要能够很快的进行双向查询,例如得到广东省下面所有的市,然后也能很快的根据市的到省,甚至根据区来得到哪几个省有这个区 2、文档性数据库存储格式 3、尽可能支持国际化(貌似有点儿困难) 4、用户地址嘛,当然要和用户对象关联

求指点、求意见……


5 回复

针对用户地址表的设计问题,我们可以结合上述两种方案的优点,并采用一种更为灵活和高效的方式来解决。以下是具体的设计思路:

设计思路

  1. 层次结构:使用树状结构存储地址信息,以支持快速的双向查询。
  2. 关联用户:通过外键关联用户信息,方便管理用户地址。
  3. 文档型数据库:使用MongoDB等文档型数据库存储地址信息,便于处理复杂的数据结构。

数据库设计

地址表 (address)

{
    "_id": ObjectId,
    "parentId": ObjectId, // 父级地址ID
    "level": Number, // 地址层级(如国家、省、市、区等)
    "name": String, // 地址名称
    "isoCode": String, // 国际化编码
}

用户地址关联表 (userAddress)

{
    "_id": ObjectId,
    "userId": ObjectId, // 用户ID
    "addressId": ObjectId, // 地址ID
    "streetNo": String, // 详细地址
}

示例代码

创建地址表

const mongoose = require('mongoose');

const addressSchema = new mongoose.Schema({
    parentId: { type: mongoose.Schema.Types.ObjectId, ref: 'Address' },
    level: Number,
    name: String,
    isoCode: String,
});

const Address = mongoose.model('Address', addressSchema);

module.exports = Address;

创建用户地址关联表

const mongoose = require('mongoose');
const User = require('./User'); // 假设已经定义了用户模型

const userAddressSchema = new mongoose.Schema({
    userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    addressId: { type: mongoose.Schema.Types.ObjectId, ref: 'Address' },
    streetNo: String,
});

const UserAddress = mongoose.model('UserAddress', userAddressSchema);

module.exports = UserAddress;

查询示例

获取广东省的所有城市

async function getCitiesByProvince(provinceId) {
    const cities = await Address.find({ parentId: provinceId, level: 3 });
    return cities.map(city => city.name);
}

获取某个城市的省份

async function getProvinceByCity(cityId) {
    const city = await Address.findById(cityId);
    if (city) {
        const province = await Address.findById(city.parentId);
        return province ? province.name : null;
    }
    return null;
}

通过这种方式,我们既能保持数据的灵活性和扩展性,又能保证查询效率。同时,文档型数据库如MongoDB非常适合存储复杂的数据结构,满足国际化的需求。


身份证idc,利用idc表示有意义的id。 想必知道身份证吧,在网上查询自己的idc,一个编码就能查找到自己身份证所在地的详细信息。因此你可以设置省的显示方式为axxxx,省市的显示方式为axxxxbxxxx,省市区的显示方式为axxxxbxxxxcxxxx,加上其他的东西,组成一个idc,例如a0001b0023c1010some0001,你想获取他是哪个市的,截取市代码去省市区编码表查,你想知道这个市区是哪个省,截取省市代码去省市区编码查。要支持国际化也简单了,省市区编码表寸id,name,ename不就可以了?

id             adrid       adrname                        enname
xxxx      86440000    广东省
xxxx      86440300    深圳市
xxxx      86440304    福田区
xxxx      86440304000    xx街,xx号(详细地址)
xxxx      86440304001    yy街,yy号(详细地址)

前两位国家代码,第三四为省级代码,第五六位省代码,第七八位区域代码,最后三位为街道地址,怎样?

ok,如果能满足你的需求和未来的扩展就是可以的

针对您的需求,可以设计两个集合来存储国家、省份、城市、地区信息以及用户与地址之间的关系。这里我们使用MongoDB作为文档型数据库,并使用Mongoose作为ODM(对象文档映射)。

国家、省份、城市、地区的集合设计

const mongoose = require('mongoose');

// 定义国家模型
const CountrySchema = new mongoose.Schema({
    name: { type: String, unique: true },
    provinces: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Province' }]
});

// 定义省份模型
const ProvinceSchema = new mongoose.Schema({
    countryId: { type: mongoose.Schema.Types.ObjectId, ref: 'Country' },
    name: { type: String, unique: true },
    cities: [{ type: mongoose.Schema.Types.ObjectId, ref: 'City' }]
});

// 定义城市模型
const CitySchema = new mongoose.Schema({
    provinceId: { type: mongoose.Schema.Types.ObjectId, ref: 'Province' },
    name: { type: String, unique: true },
    areas: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Area' }]
});

// 定义地区模型
const AreaSchema = new mongoose.Schema({
    cityId: { type: mongoose.Schema.Types.ObjectId, ref: 'City' },
    name: { type: String, unique: true },
    streets: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Street' }]
});

// 定义街道模型
const StreetSchema = new mongoose.Schema({
    areaId: { type: mongoose.Schema.Types.ObjectId, ref: 'Area' },
    detail: String
});

用户地址集合设计

// 用户地址模型
const UserAddressSchema = new mongoose.Schema({
    userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    streetId: { type: mongoose.Schema.Types.ObjectId, ref: 'Street' }
});

示例查询

查询广东省下面的所有城市:

async function getCitiesByProvince(provinceName) {
    const province = await Province.findOne({ name: provinceName }).populate('cities');
    return province ? province.cities.map(city => city.name) : [];
}

查询某个城市对应的省份:

async function getProvinceByCity(cityName) {
    const city = await City.findOne({ name: cityName }).populate('provinceId');
    return city ? city.provinceId.name : null;
}

根据街道查询省份:

async function getProvinceByStreet(streetName) {
    const street = await Street.findOne({ detail: streetName }).populate('areaId.cityId.provinceId');
    return street ? street.areaId.cityId.provinceId.name : null;
}

注意事项

  • 通过引用(ref)的方式构建关系,可以有效地减少数据冗余。
  • 使用Mongoose的populate()方法来获取相关联的数据。
  • 这种设计虽然稍微复杂一些,但可以很好地支持您的所有需求,包括国际化的扩展。

希望这些建议能帮助您更好地设计用户地址表!

回到顶部