【问题标题】:Mongoose 5.3.8 - Can't Extract Geo Keys with MultiLineString and $pushMongoose 5.3.8 - 无法使用 MultiLineString 和 $push 提取地理键
【发布时间】:2019-04-14 20:12:37
【问题描述】:

我正在尝试将坐标对 $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,

我的坐标是有效的 [long, lat] 格式,正如 mongo 所期望的那样。 这里有什么我遗漏的吗?


这是我的 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

在 shell 连接上运行 db.routes.getIndexes() 提供以下内容。

 [
            {
                    "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
            }
    ]

【问题讨论】:

  • 您能否显示与错误中给出的_id 匹配的实际文档。 ObjectId('5be90afce2b53a23e0f83bda')。错误消息表明数据未按您预期的那样存储。错误消息中的"Point" 似乎也支持了这一点。
  • @NeilLunn 绝对。我在上面进行了一些编辑,显示了RouteSchema 的存储方式,坐标参数是我试图将$push 放入的嵌套数组。非常感谢。
  • 好的,但这不是您被要求进行的编辑。您的错误消息显示rrmsg": "Can't extract geo keys: { _id: ObjectId('5be90afce2b53a23e0f83bda'),所以我要求您显示由给定_id 值标识的该文档。通过显示该文档或未删节的错误消息,因为这就是问题所在。就像它说的无效 GeoJSON 结构一样。
  • 另请注意,您拥有{ route: { type, coordinates, _id } },因为您为自动包含_id 的子文档定义了Schema,这也是无效的。您应该在架构定义选项中包含 { _id: false } 或简单地内联字段定义,因为这不是单独的架构定义的真正用途。 可能是这里的实际问题,因为错误可能是指_id within route 子文档,但提供的整个文档实际上与错误匹配给定的很清楚。
  • @NeilLunn 是的,当然,我很抱歉。最初提供的数据位于测试堆栈上,并且已被清除。我已经用准确的设置重新创建了错误,并根据您的说明替换了结果信息,感谢。

标签: node.js mongodb mongoose geojson multilinestring


【解决方案1】:

所以这个问题确实是可重现的,它归结为架构定义。就在这里:

coordinates: {
  type: [[[Number]]],
  required: true
}

这是您努力指定文档MultiLineString 格式所需的嵌套数组“环”的地方。这似乎是正确的,插入新文档不是问题,但请注意 MongoDB 想要的符号:

{ $push: { 'route.coordinates.0': [-123.3701134, 48.4467389] } }

所以“模式”有一个“双数组嵌套”,如 [[[Number]]],但 MongoDB 只想要 coordinates.0 而不是 coordinates.0.0 才能开心。这是 mongoose 感到困惑并“重写”更新语句的地方:

{ '$push': { 'route.coordinates.0': [ [ -123.3701134 ], [ 48.4467389 ] ] } }

所以这是错误的,并且是错误的根源:

错误消息: '无法提取地理键:{ _id: ObjectId(\'5be9f3915fc64e3548603766\'), route: { type: "MultiLineString", 坐标: [ [ [ -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.467389]]]],名称:“一条路线”,__v:0 } Point 只能包含数字元素',

将架构更改为“不那么具体”会阻止猫鼬“修改”语句:

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();
  }

})()

以及来自更正架构的“正确”输出:

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
}

【讨论】:

  • 太棒了,感谢 Niel 提供的见解,它非常有效。感谢您今天提供的所有帮助。
猜你喜欢
  • 1970-01-01
  • 2020-07-31
  • 2020-07-20
  • 2019-04-06
  • 1970-01-01
  • 2015-05-25
  • 2017-04-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多