如何在不知道分布的情况下将彼此靠近的项目分组?

时间:2019-01-26 11:57:16

标签: node.js mongodb aggregation-framework

我正在准备一些有关已售公寓价格的数据,以进行回归分析。一类是房屋所在的街道,但是有些街道的面积截然不同,所以我想用建筑年份和街道名称来组合一个类别。

Broadway 1910
Broadway 2001

例如,我面临的挑战是有时建设需要跨越两年时间。数据来自瑞典,以大型集中式住房项目而闻名。我想以某种方式将这些房屋归为一个时期。这是我当前的代码。我知道这不是很有效,但是只能在一个不大的数据集上运行一次。

(async () =>{
    let client;
    try {
        client = await MongoClient;
        let collection = client.db("booliscraper").collection("sold");
        let docs = await collection.find();
        await docs.forEach((sale) => {
            sale.street = sale.location.address.streetAddress.split(/[0-9]/)[0] + sale.location.namedAreas[0]
            sale.streetYear = sale.street+" "+sale.constructionYear

            log(sale);
            collection.replaceOne({_id: ObjectId(sale._id)}, doc)
        });


    client.close(); 

  } catch(err) {
    log(err)
  }
})()

1 个答案:

答案 0 :(得分:0)

正如您正确地说的那样,当您处理大量数据集时,当前代码效率很低,因此,您可以在服务器端replaceOne中多次调用服务器来执行forEach,而无需聚合查询,该查询使用$ group管道计算所需的类别字段,并将属于这些类别的文档推送到一个数组中,稍后您将使用该数组进行批量更新。

对于批量更新,可以在将具有多个updateMany操作的集合上使用bulkWrite方法。

以下操作在实践中显示了上述直觉:

(async () => {
    try {
        let client = await MongoClient;
        let collection = client.db("booliscraper").collection("sold");
        let pipeline = [
            { '$group': { 
                '_id': {
                    'street': { 
                        '$concat': [
                            { 
                                '$arrayElemAt': [
                                    { '$split': [
                                        '$location.address.streetAddress', 
                                        /[0-9]/
                                    ] },
                                    0
                                ]
                            }, 
                            {  '$arrayElemAt': [ '$location.namedAreas', 0 ] },
                        ] 
                    },
                    'streetYear': { '$concat': ['$street', ' ', '$constructionYear']  }
                },
                'ids': { '$push': '$_id' }
            } }
        ]
        let docs = await collection.aggregate(pipeline);
        let ops = docs.map(({ _id, ids }) => ({
            'updateMany': {
                'filter': { '_id': { '$in': ids } },
                'update': { '$set': { 
                    'street': _id.street, 'streetYear': _id.streetYear 
                } }
            }
        }));
        let result = await collection.bulkWrite(ops);

        log(result)

        client.close()

    } catch(err) {
        log(err)
    }
})()