Mongoose 5.3.8 - 无法使用 MultiLineString 和 $push 提取地理键
Mongoose 5.3.8 - Can't Extract Geo Keys with MultiLineString and $push
我正在尝试将一个坐标对 $push 到一个文档中,该文档包含一个嵌套如下的 GeoJSON MultiLineString:[[[Number]]]
当我像这样找到OneAndUpdate时:
req.body.point = [-123.347, 48.43649]
Route.findOneAndUpdate({name: req.body.name},
{'$push': { "route.coordinates.0": req.body.point}}, {new: true})
.then(doc => {
return res.send(doc)
}).catch(err => {
res.send(err)
})
我收到错误消息:
errmsg": "Can't extract geo keys: { _id: ObjectId('5be9eef393311d2d2c6130fd').......
Point must only contain numeric elements",
"code": 16755,
我的坐标是 mongo 期望的有效 [long, lat] 格式。
我在这里遗漏了什么吗?
这是我的 MultiLineString 架构:
const MultiLineStringSchema = new Schema({
type: {
type: String,
enum: ['MultiLineString'],
required: true,
default: 'MultiLineString'
},
coordinates: {
type: [[[Number]]],
required: true
}
});
这是我的路线架构:
var { MultiLineStringSchema } = require('./GeoJson-Schemas')
const RouteSchema = new mongoose.Schema({
name: String,
route: {
type: MultiLineStringSchema,
required: true
}
});
RouteSchema.index({ route: "2dsphere" });
编辑 2:
这是存储的导致错误持续存在的 Route 文档。我用这个文档重新创建了错误,并更新了上面错误消息中匹配的 _id。
{
"_id": {
"$oid": "5be9eef393311d2d2c6130fd"
},
"name": "A Route",
"route": {
"type": "MultiLineString",
"coordinates": [
[
[
-123.3867645,
48.4813423
],
[
-123.3785248,
48.4592626
],
[
-123.3766365,
48.4527165
],
[
-123.3756924,
48.4523749
],
[
-123.3722591,
48.4549366
],
[
-123.3704567,
48.4559612
]
]
],
"_id": {
"$oid": "5be9eef393311d2d2c6130fe"
}
},
"__v": 0
}
向前迈出一步,我已将 {_id: false} 选项添加到 MultiLineString 子文档并重新初始化了一个新集合。像这样存储新文档后:
{
"_id": "5be9f076e1caaa23682d80de",
"name": "A Route",
"route": {
"type": "MultiLineString",
"coordinates": [
[
[
-123.3867645,
48.4813423
],
[
-123.3785248,
48.4592626
],
[
-123.3766365,
48.4527165
],
[
-123.3756924,
48.4523749
],
[
-123.3722591,
48.4549366
],
[
-123.3704567,
48.4559612
]
]
]
},
"__v": 0
}
我尝试使用准确的语法查找 OneAndUpdate:
Route.findOneAndUpdate({name},
{'$push': { "route.coordinates.0": point}}, {new: true})
.then(doc => {
return res.send(doc)
}).catch(err => {
res.send(err)
})
并收到同样的错误:
"errmsg": "Can't extract geo keys: { _id: ObjectId('5be9f076e1caaa23682d80de')...
Point must only contain numeric elements",
"code": 16755,
编辑 3:
再次前进我已经更新了 RouteSchema 删除 子文档 完全像这样:
const RouteSchema = new mongoose.Schema({
name: String,
route: {
type: {
type: String,
enum: ['MultiLineString'],
required: true,
default: 'MultiLineString'
},
coordinates: {
type: [[[Number]]],
required: true
}
}
});
我这样存储文档:
{
"_id": {
"$oid": "5be9f3915fc64e3548603766"
},
"route": {
"type": "MultiLineString",
"coordinates": [
[
[
-123.3867645,
48.4813423
],
[
-123.3785248,
48.4592626
],
[
-123.3766365,
48.4527165
],
[
-123.3756924,
48.4523749
],
[
-123.3722591,
48.4549366
],
[
-123.3704567,
48.4559612
]
]
]
},
"name": "A Route",
"__v": 0
}
并再次使用完全相同的语法进行 FindOneAndUpdate,并收到相同的错误。
Route.findOneAndUpdate({name},
{'$push': { "route.coordinates.0": point}}, {new: true})
.then(doc => {
return res.send(doc)
}).catch(err => {
res.send(err)
})
"errmsg": "Can't extract geo keys: { _id: ObjectId('5be9f3915fc64e3548603766')
Point must only contain numeric elements",
"code": 16755,
编辑 4:
mLab hosted instance mongo version 3.6.6 indexes on Route collection PHOTO
运行 db.routes.getIndexes() ON shell 连接提供以下内容。
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "testbench.routes"
},
{
"v" : 2,
"key" : {
"route" : "2dsphere"
},
"name" : "route_2dsphere",
"ns" : "testbench.routes",
"background" : true,
"2dsphereIndexVersion" : 3
}
]
所以这个问题确实是可重现的,它归结为架构定义。就在这里:
coordinates: {
type: [[[Number]]],
required: true
}
这是您努力指定文档 MultiLineString
格式所需的嵌套数组 "rings" 的地方。它可能看起来是正确的,插入新文档不是问题,但要注意这里的符号 MongoDB:
{ $push: { 'route.coordinates.0': [-123.3701134, 48.4467389] } }
所以 "schema" 有一个 "double array nest" 和 [[[Number]]]
一样,但是 MongoDB 只想要 coordinates.0
而不是 coordinates.0.0
快乐的。这是 mongoose 感到困惑的地方,"rewrites" 更新语句:
{ '$push': { 'route.coordinates.0': [ [ -123.3701134 ], [ 48.4467389 ] ] } }
所以那是错误的,并且是错误的来源:
errmsg:
'Can\'t extract geo keys: { _id: ObjectId(\'5be9f3915fc64e3548603766\'), route: { type: "MultiLineString", coordinates: [ [ [ -123.3867645, 48.4813423 ], [ -123.3785248, 48.4592626 ], [ -123.3766365, 48.4527165 ], [ -123.3756924, 48.4523749 ], [ -123.3722591, 48.4549366 ], [ -123.3704567, 48.4559612 ], [ [ -123.3701134 ], [ 48.4467389 ] ] ] ] }, name: "A Route", __v: 0 } Point must only contain numeric elements',
将架构更改为 "so specific" 从 "mangling" 语句停止 mongoose:
coordinates: {
type: [] // Just expect an array without any other definition
required: true
}
然后更新按预期进行。
对于完整的可复制列表:
const { Schema, Types: { ObjectId } } = mongoose = require('mongoose');
// Sample connection
const uri = 'mongodb://localhost:27017/geostring';
const opts = { useNewUrlParser: true };
// Sensible defaults
mongoose.Promise = global.Promise;
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
mongoose.set('debug', true);
// Schema defs
const routeSchema = new Schema({
name: String,
route: {
type: {
type: String,
enum: ['MultiLineString'],
required: true,
default: 'MultiLineString'
},
coordinates: {
type: [],
// type: [[[Number]]], // this has a problem
required: true
}
}
});
routeSchema.index({ route: '2dsphere' });
const Route = mongoose.model('Route', routeSchema);
// log helper
const log = data => console.log(JSON.stringify(data, undefined, 2));
// Main
(async function() {
try {
const conn = await mongoose.connect(uri, opts);
// clean data from all models
await Promise.all(
Object.entries(conn.models).map(([k,m]) => m.deleteMany())
);
// Insert data
await Route.create({
"_id": new ObjectId("5be9f3915fc64e3548603766"),
"name": "A Route",
"route": {
"type": "MultiLineString",
"coordinates": [
[
[
-123.3867645,
48.4813423
],
[
-123.3785248,
48.4592626
],
[
-123.3766365,
48.4527165
],
[
-123.3756924,
48.4523749
],
[
-123.3722591,
48.4549366
],
[
-123.3704567,
48.4559612
]
]
]
}
});
// Test operation
let result = await Route.findOneAndUpdate(
{ name: 'A Route' },
{ $push: { 'route.coordinates.0': [-123.3701134, 48.4467389] } },
{ new: true }
);
log(result);
} catch(e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})()
以及更正模式的 "correct" 输出:
Mongoose: routes.createIndex({ route: '2dsphere' }, { background: true })
Mongoose: routes.deleteMany({}, {})
Mongoose: routes.insertOne({ route: { type: 'MultiLineString', coordinates: [ [ [ -123.3867645, 48.4813423 ], [ -123.3785248, 48.4592626 ], [ -123.3766365, 48.4527165 ], [ -123.3756924, 48.4523749 ], [ -123.3722591, 48.4549366 ], [ -123.3704567, 48.4559612 ] ] ] }, _id: ObjectId("5be9f3915fc64e3548603766"), name: 'A Route', __v: 0 })
Mongoose: routes.findOneAndUpdate({ name: 'A Route' }, { '$push': { 'route.coordinates.0': [ -123.3701134, 48.4467389 ] } }, { upsert: false, remove: false, projection: {}, returnOriginal: false })
{
"route": {
"type": "MultiLineString",
"coordinates": [
[
[
-123.3867645,
48.4813423
],
[
-123.3785248,
48.4592626
],
[
-123.3766365,
48.4527165
],
[
-123.3756924,
48.4523749
],
[
-123.3722591,
48.4549366
],
[
-123.3704567,
48.4559612
],
[
-123.3701134,
48.4467389
]
]
]
},
"_id": "5be9f3915fc64e3548603766",
"name": "A Route",
"__v": 0
}
我正在尝试将一个坐标对 $push 到一个文档中,该文档包含一个嵌套如下的 GeoJSON MultiLineString:[[[Number]]]
当我像这样找到OneAndUpdate时:
req.body.point = [-123.347, 48.43649]
Route.findOneAndUpdate({name: req.body.name},
{'$push': { "route.coordinates.0": req.body.point}}, {new: true})
.then(doc => {
return res.send(doc)
}).catch(err => {
res.send(err)
})
我收到错误消息:
errmsg": "Can't extract geo keys: { _id: ObjectId('5be9eef393311d2d2c6130fd').......
Point must only contain numeric elements",
"code": 16755,
我的坐标是 mongo 期望的有效 [long, lat] 格式。 我在这里遗漏了什么吗?
这是我的 MultiLineString 架构:
const MultiLineStringSchema = new Schema({
type: {
type: String,
enum: ['MultiLineString'],
required: true,
default: 'MultiLineString'
},
coordinates: {
type: [[[Number]]],
required: true
}
});
这是我的路线架构:
var { MultiLineStringSchema } = require('./GeoJson-Schemas')
const RouteSchema = new mongoose.Schema({
name: String,
route: {
type: MultiLineStringSchema,
required: true
}
});
RouteSchema.index({ route: "2dsphere" });
编辑 2: 这是存储的导致错误持续存在的 Route 文档。我用这个文档重新创建了错误,并更新了上面错误消息中匹配的 _id。
{
"_id": {
"$oid": "5be9eef393311d2d2c6130fd"
},
"name": "A Route",
"route": {
"type": "MultiLineString",
"coordinates": [
[
[
-123.3867645,
48.4813423
],
[
-123.3785248,
48.4592626
],
[
-123.3766365,
48.4527165
],
[
-123.3756924,
48.4523749
],
[
-123.3722591,
48.4549366
],
[
-123.3704567,
48.4559612
]
]
],
"_id": {
"$oid": "5be9eef393311d2d2c6130fe"
}
},
"__v": 0
}
向前迈出一步,我已将 {_id: false} 选项添加到 MultiLineString 子文档并重新初始化了一个新集合。像这样存储新文档后:
{
"_id": "5be9f076e1caaa23682d80de",
"name": "A Route",
"route": {
"type": "MultiLineString",
"coordinates": [
[
[
-123.3867645,
48.4813423
],
[
-123.3785248,
48.4592626
],
[
-123.3766365,
48.4527165
],
[
-123.3756924,
48.4523749
],
[
-123.3722591,
48.4549366
],
[
-123.3704567,
48.4559612
]
]
]
},
"__v": 0
}
我尝试使用准确的语法查找 OneAndUpdate:
Route.findOneAndUpdate({name},
{'$push': { "route.coordinates.0": point}}, {new: true})
.then(doc => {
return res.send(doc)
}).catch(err => {
res.send(err)
})
并收到同样的错误:
"errmsg": "Can't extract geo keys: { _id: ObjectId('5be9f076e1caaa23682d80de')...
Point must only contain numeric elements",
"code": 16755,
编辑 3:
再次前进我已经更新了 RouteSchema 删除 子文档 完全像这样:
const RouteSchema = new mongoose.Schema({
name: String,
route: {
type: {
type: String,
enum: ['MultiLineString'],
required: true,
default: 'MultiLineString'
},
coordinates: {
type: [[[Number]]],
required: true
}
}
});
我这样存储文档:
{
"_id": {
"$oid": "5be9f3915fc64e3548603766"
},
"route": {
"type": "MultiLineString",
"coordinates": [
[
[
-123.3867645,
48.4813423
],
[
-123.3785248,
48.4592626
],
[
-123.3766365,
48.4527165
],
[
-123.3756924,
48.4523749
],
[
-123.3722591,
48.4549366
],
[
-123.3704567,
48.4559612
]
]
]
},
"name": "A Route",
"__v": 0
}
并再次使用完全相同的语法进行 FindOneAndUpdate,并收到相同的错误。
Route.findOneAndUpdate({name},
{'$push': { "route.coordinates.0": point}}, {new: true})
.then(doc => {
return res.send(doc)
}).catch(err => {
res.send(err)
})
"errmsg": "Can't extract geo keys: { _id: ObjectId('5be9f3915fc64e3548603766')
Point must only contain numeric elements",
"code": 16755,
编辑 4:
mLab hosted instance mongo version 3.6.6 indexes on Route collection PHOTO
运行 db.routes.getIndexes() ON shell 连接提供以下内容。
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "testbench.routes"
},
{
"v" : 2,
"key" : {
"route" : "2dsphere"
},
"name" : "route_2dsphere",
"ns" : "testbench.routes",
"background" : true,
"2dsphereIndexVersion" : 3
}
]
所以这个问题确实是可重现的,它归结为架构定义。就在这里:
coordinates: {
type: [[[Number]]],
required: true
}
这是您努力指定文档 MultiLineString
格式所需的嵌套数组 "rings" 的地方。它可能看起来是正确的,插入新文档不是问题,但要注意这里的符号 MongoDB:
{ $push: { 'route.coordinates.0': [-123.3701134, 48.4467389] } }
所以 "schema" 有一个 "double array nest" 和 [[[Number]]]
一样,但是 MongoDB 只想要 coordinates.0
而不是 coordinates.0.0
快乐的。这是 mongoose 感到困惑的地方,"rewrites" 更新语句:
{ '$push': { 'route.coordinates.0': [ [ -123.3701134 ], [ 48.4467389 ] ] } }
所以那是错误的,并且是错误的来源:
errmsg: 'Can\'t extract geo keys: { _id: ObjectId(\'5be9f3915fc64e3548603766\'), route: { type: "MultiLineString", coordinates: [ [ [ -123.3867645, 48.4813423 ], [ -123.3785248, 48.4592626 ], [ -123.3766365, 48.4527165 ], [ -123.3756924, 48.4523749 ], [ -123.3722591, 48.4549366 ], [ -123.3704567, 48.4559612 ], [ [ -123.3701134 ], [ 48.4467389 ] ] ] ] }, name: "A Route", __v: 0 } Point must only contain numeric elements',
将架构更改为 "so specific" 从 "mangling" 语句停止 mongoose:
coordinates: {
type: [] // Just expect an array without any other definition
required: true
}
然后更新按预期进行。
对于完整的可复制列表:
const { Schema, Types: { ObjectId } } = mongoose = require('mongoose');
// Sample connection
const uri = 'mongodb://localhost:27017/geostring';
const opts = { useNewUrlParser: true };
// Sensible defaults
mongoose.Promise = global.Promise;
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
mongoose.set('debug', true);
// Schema defs
const routeSchema = new Schema({
name: String,
route: {
type: {
type: String,
enum: ['MultiLineString'],
required: true,
default: 'MultiLineString'
},
coordinates: {
type: [],
// type: [[[Number]]], // this has a problem
required: true
}
}
});
routeSchema.index({ route: '2dsphere' });
const Route = mongoose.model('Route', routeSchema);
// log helper
const log = data => console.log(JSON.stringify(data, undefined, 2));
// Main
(async function() {
try {
const conn = await mongoose.connect(uri, opts);
// clean data from all models
await Promise.all(
Object.entries(conn.models).map(([k,m]) => m.deleteMany())
);
// Insert data
await Route.create({
"_id": new ObjectId("5be9f3915fc64e3548603766"),
"name": "A Route",
"route": {
"type": "MultiLineString",
"coordinates": [
[
[
-123.3867645,
48.4813423
],
[
-123.3785248,
48.4592626
],
[
-123.3766365,
48.4527165
],
[
-123.3756924,
48.4523749
],
[
-123.3722591,
48.4549366
],
[
-123.3704567,
48.4559612
]
]
]
}
});
// Test operation
let result = await Route.findOneAndUpdate(
{ name: 'A Route' },
{ $push: { 'route.coordinates.0': [-123.3701134, 48.4467389] } },
{ new: true }
);
log(result);
} catch(e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})()
以及更正模式的 "correct" 输出:
Mongoose: routes.createIndex({ route: '2dsphere' }, { background: true })
Mongoose: routes.deleteMany({}, {})
Mongoose: routes.insertOne({ route: { type: 'MultiLineString', coordinates: [ [ [ -123.3867645, 48.4813423 ], [ -123.3785248, 48.4592626 ], [ -123.3766365, 48.4527165 ], [ -123.3756924, 48.4523749 ], [ -123.3722591, 48.4549366 ], [ -123.3704567, 48.4559612 ] ] ] }, _id: ObjectId("5be9f3915fc64e3548603766"), name: 'A Route', __v: 0 })
Mongoose: routes.findOneAndUpdate({ name: 'A Route' }, { '$push': { 'route.coordinates.0': [ -123.3701134, 48.4467389 ] } }, { upsert: false, remove: false, projection: {}, returnOriginal: false })
{
"route": {
"type": "MultiLineString",
"coordinates": [
[
[
-123.3867645,
48.4813423
],
[
-123.3785248,
48.4592626
],
[
-123.3766365,
48.4527165
],
[
-123.3756924,
48.4523749
],
[
-123.3722591,
48.4549366
],
[
-123.3704567,
48.4559612
],
[
-123.3701134,
48.4467389
]
]
]
},
"_id": "5be9f3915fc64e3548603766",
"name": "A Route",
"__v": 0
}