关于用户地址表的设计问题 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、用户地址嘛,当然要和用户对象关联
求指点、求意见……
针对用户地址表的设计问题,我们可以结合上述两种方案的优点,并采用一种更为灵活和高效的方式来解决。以下是具体的设计思路:
设计思路
- 层次结构:使用树状结构存储地址信息,以支持快速的双向查询。
- 关联用户:通过外键关联用户信息,方便管理用户地址。
- 文档型数据库:使用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()
方法来获取相关联的数据。 - 这种设计虽然稍微复杂一些,但可以很好地支持您的所有需求,包括国际化的扩展。
希望这些建议能帮助您更好地设计用户地址表!