为基于位置的服务设计 MongoDB 架构的最佳方法是什么
What is the best way to design MongoDB Schema for location based service
我正在寻找支持基于位置的搜索的正确数据库,发现 MongoDB 支持 GeoJSON 对象。
这是一个商店定位器应用程序,用户可以在其中环顾四周并选择离他最近的商店来订购产品。
简单供应商架构:
const VendorSchema = new Schema({
address: {
type: String,
required: [true, 'Please add address']
},
formattedAddress: {
type: String
},
location: {
type: {
type: String,
enum: ['Point']
},
// GeoJSON Points
coordinates: {
type: [Number],
index: '2dsphere'
},
formattedAddress: String,
street: String,
city: String,
state: String,
zipcode: String,
country: String
},
createdAt: {
type: Date,
default: Date.now
}
});
数据库将包含多个供应商可能销售的许多 FMCG 产品。
产品架构:
const ProductSchema = new Schema({
name: {
type: String,
required: true
},
desc: {
type: String,
required: true
},
price: Number,
createdAt: {
type: Date,
default: Date.now
}
});
A Vendor can sell multiple Products and a Product could be sold by many Vendors, there's N-to-N
relationship between Vendors & Products.
所以正在考虑创建一个新架构,供应商产品架构:
const VendorProductSchema = new Schema({
price: Number,
discountVal: Number,
vendor : {
type: ObjectId,
ref: 'Vendor'
},
createdAt: {
type: Date,
default: Date.now
}
});
这就是它变得棘手/具有挑战性的地方:
User's can either look-up for Vendor/Stores around them or can also directly search for a Product.
如果要求查找供应商,它会在特定半径范围内直接在供应商集合中查找。
Vendor.find({"location.coordinates": {$geoWithin: {$centerSphere: [[User long, User lat], 1/6378.15]}}})
但是当用户搜索产品时:
系统应该发回搜索到的产品详细信息以及附近的销售它们的供应商 - 从最近到最远,在一定半径内。
出于这个原因,我正在考虑在 VendorProduct 架构中存储产品的供应商 GeoJSON 位置详细信息。
具有 GeoJSON 详细信息的 VendorProduct 架构:
const VendorProductSchema = new Schema({
price: Number,
discountVal: Number,
vendor : {
type: ObjectId,
ref: 'Vendor'
},
location: {
type: {
type: String,
enum: ['Point']
},
// GeoJSON Points
coordinates: {
type: [Number],
index: '2dsphere'
}
},
createdAt: {
type: Date,
default: Date.now
}
});
假设用户使用关键字 ONIONS 进行搜索。它将在 Products 集合中找到,将使用 ProductID 并依次根据地理坐标在 VendorProducts 集合中过滤它们。通过这种方式,我可以提取产品信息,如描述、来自产品集合和供应商的图像、来自 VendorProducts 集合的价格信息。
这只是记住,一个产品可以由多个供应商销售。当用户使用产品名称搜索时 - 可能有 N 个不同价格的产品卖家。但是为了只找到最近的供应商,正在考虑在 VendorProductSchema 中针对产品存储供应商位置详细信息。
几个问题:
- 在这种情况下使用 MongoDB 是否正确?
- 这是正确的设计和方法吗?
- 在 MongoDB 中进行此类地理位置搜索的时间复杂度是多少?
我只知道基本的 MongoDB 但我注意到您将位置数据存储在 VendorProduct 集合中。在这种情况下,如果供应商编辑他们的坐标,则需要更新多个文档。
相反,您可以将位置保留在 Vendor 集合本身中。在查询时,您可以执行嵌套查询以获取给定名称的产品,其中供应商的坐标值小于 x、y。语法-
https://docs.mongodb.com/manual/tutorial/query-embedded-documents/
- 在这种情况下使用 MongoDB 是否正确?
是的,我不明白为什么不。 MongoDB 支持 geoJSON 并允许多种查询方式轻松查询此信息。
- 这是正确的设计和方法吗?
如果我了解你的情况:
User's can either look-up for Vendor/Stores around them or can also directly search for a product. As its a location based service - system should send Products that are around user, so was thinking of storing Vendor's GeoJSON location details in VendorProduct schema so that system can query only Products that are around user with Vendor details.
给定一个特定点(用户位置),在给定半径内找到有可用产品的供应商。
由于产品可以有多个供应商,并且供应商位于位置所在的位置,因此没有理由重复供应商的位置。如果您确实将供应商位置信息存储在两个地方,例如供应商和产品,那么您的应用程序将不再有单一的事实点。尝试使这些文档保持同步会变得一团糟。如果你想分离任何东西,我会将供应商的位置分离到一个独立的索引集合(下例中的 vendorGEO),并调整当前的供应商文档以指向这个新集合。这样就可以查询用户附近有哪些商家,然后抓取商家,然后只拉取你需要的商家,搜索他们的产品。
这是一个 mongo shell 示例,用于根据点(用户位置)的半径(圆)查找所有未排序的位置
db.vendorGEO.createIndex({location: "2dsphere"})
db.vendorGEO.find({location: {$geoWithin: {$centerSphere: [[User long, User lat], 1/6378.15]}}})
英里:1/3963.2 弧度
公里:1/6378.15 弧度
官方文档:https://docs.mongodb.com/manual/reference/operator/query/centerSphere/index.html
您可以改用多边形。如果您有一个预定义的区域,例如在城市范围内,多边形会更有用:
一个Polygon,需要四个点,一个原始起点。注意使用嵌套数组设置坐标的方式:
db.collection.find({location: {$geoWithin: {$geometry: {type: "Polygon", coordinates: [[[point 1], [point 2], [point 3], [point 4], [point 1 again]]]}}}})
为了方便存储多边形信息,将它们存储在一个单独的集合中
db.areas.insertOne({name: "Example Location", area: {type: "Polygon", coordinates: [[[longA, latA], [longB, latB], [longC, latC], [longD, latD], [longA, latA]]]}})
要查找一个点是否在多边形内,请在多边形内搜索一个点,参考定义的区域字段
db.areas.find({area: {$geoIntersects: {$geometry: {type: "Point", coordinates: [longA, latA]}}}})
- 在 MongoDB 中进行此类地理位置搜索的时间复杂度是多少
?
使用 $geoIntersects 或 $geoWithin 时,不需要索引,但建议添加索引以加快查询速度。
对于时间复杂度的任何关注,您可以通过使用 .explain()
命令深入查询 planning/optimization.
来了解幕后发生的事情
据我了解,您应该只在 Vendor Schema 中保存位置坐标,因为它们与供应商有关。您的旧 VendorProduct Schema 没问题
案例 1:用户正在寻找附近的供应商
您需要运行查询以根据用户的位置查找供应商并将供应商列表提供给用户
为此,您需要 运行
Vendor.find({"location.coordinates": {$geoWithin: {$centerSphere: [[User long, User lat], 1/6378.15]}}})
案例2:用户正在寻找商品he/she可以在附近找到
那样的话,也先通过上面的查询搜索附近的商贩(记住附近的商贩数量是有限的,否则你需要缩小搜索半径) .
一旦你得到附近的所有供应商,将他们的 ID 保存在一个数组中,我们称之为
vendorArray = [vendor_id1, vendor_id2.....]
然后在 VendorProduct 架构中搜索产品
VendorProduct.find({
vendor : {
$in: vendorArray
}
})
上述解决方案仅用于在 VendorProductSchema 中存储位置信息的替代方法
现在您需要在从 vendorProduct 集合中获取产品 ID 后获取产品详细信息,我建议您应该在 vendorProduct 中添加产品名称或您要搜索或过滤的任何其他字段
当前方法的缺点
在 VendorProduct
中存储位置坐标几乎没有缺点
- 您正在多个集合中保存冗余数据,您将
在 add/edit/delete VendorProduct 记录
的同时维护它们
- VendorProduct 集合将包含多个记录
与供应商集合相比,geojson 查询需要更多
比基本搜索更强大的计算能力。
- 当你的数据库扩展时,运行 geojson 查询更有效
在 Vendor 中有几千条记录而不是 100 条记录
VendorProduct
中的记录
我正在寻找支持基于位置的搜索的正确数据库,发现 MongoDB 支持 GeoJSON 对象。
这是一个商店定位器应用程序,用户可以在其中环顾四周并选择离他最近的商店来订购产品。
简单供应商架构:
const VendorSchema = new Schema({
address: {
type: String,
required: [true, 'Please add address']
},
formattedAddress: {
type: String
},
location: {
type: {
type: String,
enum: ['Point']
},
// GeoJSON Points
coordinates: {
type: [Number],
index: '2dsphere'
},
formattedAddress: String,
street: String,
city: String,
state: String,
zipcode: String,
country: String
},
createdAt: {
type: Date,
default: Date.now
}
});
数据库将包含多个供应商可能销售的许多 FMCG 产品。
产品架构:
const ProductSchema = new Schema({
name: {
type: String,
required: true
},
desc: {
type: String,
required: true
},
price: Number,
createdAt: {
type: Date,
default: Date.now
}
});
A Vendor can sell multiple Products and a Product could be sold by many Vendors, there's
N-to-N
relationship between Vendors & Products.
所以正在考虑创建一个新架构,供应商产品架构:
const VendorProductSchema = new Schema({
price: Number,
discountVal: Number,
vendor : {
type: ObjectId,
ref: 'Vendor'
},
createdAt: {
type: Date,
default: Date.now
}
});
这就是它变得棘手/具有挑战性的地方:
User's can either look-up for Vendor/Stores around them or can also directly search for a Product.
如果要求查找供应商,它会在特定半径范围内直接在供应商集合中查找。
Vendor.find({"location.coordinates": {$geoWithin: {$centerSphere: [[User long, User lat], 1/6378.15]}}})
但是当用户搜索产品时:
系统应该发回搜索到的产品详细信息以及附近的销售它们的供应商 - 从最近到最远,在一定半径内。
出于这个原因,我正在考虑在 VendorProduct 架构中存储产品的供应商 GeoJSON 位置详细信息。
具有 GeoJSON 详细信息的 VendorProduct 架构:
const VendorProductSchema = new Schema({
price: Number,
discountVal: Number,
vendor : {
type: ObjectId,
ref: 'Vendor'
},
location: {
type: {
type: String,
enum: ['Point']
},
// GeoJSON Points
coordinates: {
type: [Number],
index: '2dsphere'
}
},
createdAt: {
type: Date,
default: Date.now
}
});
假设用户使用关键字 ONIONS 进行搜索。它将在 Products 集合中找到,将使用 ProductID 并依次根据地理坐标在 VendorProducts 集合中过滤它们。通过这种方式,我可以提取产品信息,如描述、来自产品集合和供应商的图像、来自 VendorProducts 集合的价格信息。
这只是记住,一个产品可以由多个供应商销售。当用户使用产品名称搜索时 - 可能有 N 个不同价格的产品卖家。但是为了只找到最近的供应商,正在考虑在 VendorProductSchema 中针对产品存储供应商位置详细信息。
几个问题:
- 在这种情况下使用 MongoDB 是否正确?
- 这是正确的设计和方法吗?
- 在 MongoDB 中进行此类地理位置搜索的时间复杂度是多少?
我只知道基本的 MongoDB 但我注意到您将位置数据存储在 VendorProduct 集合中。在这种情况下,如果供应商编辑他们的坐标,则需要更新多个文档。
相反,您可以将位置保留在 Vendor 集合本身中。在查询时,您可以执行嵌套查询以获取给定名称的产品,其中供应商的坐标值小于 x、y。语法-
https://docs.mongodb.com/manual/tutorial/query-embedded-documents/
- 在这种情况下使用 MongoDB 是否正确?
是的,我不明白为什么不。 MongoDB 支持 geoJSON 并允许多种查询方式轻松查询此信息。
- 这是正确的设计和方法吗?
如果我了解你的情况:
User's can either look-up for Vendor/Stores around them or can also directly search for a product. As its a location based service - system should send Products that are around user, so was thinking of storing Vendor's GeoJSON location details in VendorProduct schema so that system can query only Products that are around user with Vendor details.
给定一个特定点(用户位置),在给定半径内找到有可用产品的供应商。
由于产品可以有多个供应商,并且供应商位于位置所在的位置,因此没有理由重复供应商的位置。如果您确实将供应商位置信息存储在两个地方,例如供应商和产品,那么您的应用程序将不再有单一的事实点。尝试使这些文档保持同步会变得一团糟。如果你想分离任何东西,我会将供应商的位置分离到一个独立的索引集合(下例中的 vendorGEO),并调整当前的供应商文档以指向这个新集合。这样就可以查询用户附近有哪些商家,然后抓取商家,然后只拉取你需要的商家,搜索他们的产品。
这是一个 mongo shell 示例,用于根据点(用户位置)的半径(圆)查找所有未排序的位置
db.vendorGEO.createIndex({location: "2dsphere"})
db.vendorGEO.find({location: {$geoWithin: {$centerSphere: [[User long, User lat], 1/6378.15]}}})
英里:1/3963.2 弧度 公里:1/6378.15 弧度
官方文档:https://docs.mongodb.com/manual/reference/operator/query/centerSphere/index.html
您可以改用多边形。如果您有一个预定义的区域,例如在城市范围内,多边形会更有用:
一个Polygon,需要四个点,一个原始起点。注意使用嵌套数组设置坐标的方式:
db.collection.find({location: {$geoWithin: {$geometry: {type: "Polygon", coordinates: [[[point 1], [point 2], [point 3], [point 4], [point 1 again]]]}}}})
为了方便存储多边形信息,将它们存储在一个单独的集合中
db.areas.insertOne({name: "Example Location", area: {type: "Polygon", coordinates: [[[longA, latA], [longB, latB], [longC, latC], [longD, latD], [longA, latA]]]}})
要查找一个点是否在多边形内,请在多边形内搜索一个点,参考定义的区域字段
db.areas.find({area: {$geoIntersects: {$geometry: {type: "Point", coordinates: [longA, latA]}}}})
- 在 MongoDB 中进行此类地理位置搜索的时间复杂度是多少 ?
使用 $geoIntersects 或 $geoWithin 时,不需要索引,但建议添加索引以加快查询速度。
对于时间复杂度的任何关注,您可以通过使用 .explain()
命令深入查询 planning/optimization.
据我了解,您应该只在 Vendor Schema 中保存位置坐标,因为它们与供应商有关。您的旧 VendorProduct Schema 没问题
案例 1:用户正在寻找附近的供应商
您需要运行查询以根据用户的位置查找供应商并将供应商列表提供给用户
为此,您需要 运行
Vendor.find({"location.coordinates": {$geoWithin: {$centerSphere: [[User long, User lat], 1/6378.15]}}})
案例2:用户正在寻找商品he/she可以在附近找到
那样的话,也先通过上面的查询搜索附近的商贩(记住附近的商贩数量是有限的,否则你需要缩小搜索半径) .
一旦你得到附近的所有供应商,将他们的 ID 保存在一个数组中,我们称之为
vendorArray = [vendor_id1, vendor_id2.....]
然后在 VendorProduct 架构中搜索产品
VendorProduct.find({
vendor : {
$in: vendorArray
}
})
上述解决方案仅用于在 VendorProductSchema 中存储位置信息的替代方法
现在您需要在从 vendorProduct 集合中获取产品 ID 后获取产品详细信息,我建议您应该在 vendorProduct 中添加产品名称或您要搜索或过滤的任何其他字段
当前方法的缺点 在 VendorProduct
中存储位置坐标几乎没有缺点- 您正在多个集合中保存冗余数据,您将 在 add/edit/delete VendorProduct 记录 的同时维护它们
- VendorProduct 集合将包含多个记录 与供应商集合相比,geojson 查询需要更多 比基本搜索更强大的计算能力。
- 当你的数据库扩展时,运行 geojson 查询更有效 在 Vendor 中有几千条记录而不是 100 条记录 VendorProduct 中的记录