MongoDB Scala - 删除集合中的重复文档

时间:2018-03-02 17:01:29

标签: mongodb scala collections duplicates document

如果我想使用Scala删除MongoDB集合中的重复文档,那么如何去做呢?

非常确定它很简单,但我总是在Mongo Shell中找到办法。

1 个答案:

答案 0 :(得分:0)

使用MongoDB Scala driver

在此示例中,我使用的是2.1版本的Scala 2.11。

我想您要删除除了_id字段以外重复相同属性的所有文档。

我在用户集合中有四个文档。

{
  "_id": <ObjectId>,
  "name": "John",
  "surname": "Doe"
}
{
  "_id": <ObjectId>,
  "name": "John",
  "surname": "Doe"
}
{
  "_id": <ObjectId>,
  "name": "John",
  "surname": "Doe"
}
{
  "_id": <ObjectId>,
  "name": "Dione",
  "surname": "Elton"
}

在这个例子中,我们将删除名称= John和surname = Doe的三个文档中的两个,保留这些文档:

{
  "_id": <ObjectId>,
  "name": "John",
  "surname": "Doe"
}
{
  "_id": <ObjectId>,
  "name": "Dione",
  "surname": "Elton"
}

我已经使用过此代码,但它适用于此示例:

val client = MongoClient("mongodb://localhost:27017")
val database = client.getDatabase("test")
val collection = database.getCollection("users")

val future = collection.find().toFuture()
val allDocs = Await.result(future, Duration.Inf)

allDocs
  .map { d => (d.filterKeys { x => !x.equals("_id")}, d.get("_id").get ) }
  .groupBy(_._1)
  .map(_._2.map(_._2))
  .filter(_.size > 1)
  .map({ids => ids.take(ids.size - 1)})
  .flatten
  .foreach{
    id => collection.deleteOne(equal("_id", id)).subscribe(
      (dr: DeleteResult) => println(dr.getDeletedCount),
      (e: Throwable) => println(s"Error when deleting the document $id: $e")
    )
  }

三个第一行非常简单,我们连接到数据库并获取集合对象。然后,我们从集合中检索所有文档。请注意,MongoDB Scala驱动程序是异步的,因此我采用了未来的对象以等待结果,因为我需要它们继续。

现在是棘手的部分。我将逐行解释。首先,我们将每个文档映射到一个元组,其中第一个元素是没有_id字段的文档,第二个元素是_id。

  .map { d => (d.filterKeys { x => !x.equals("_id")}, d.get("_id").get ) }

一旦我们有了元组,我们可以按照没有_id字段的文档对序列进行分组。它将生成一个映射,其中键是没有_id字段的文档,值是元组序列,它们表示每个对(没有_id,_id的文档),其内容与键相同。

  .groupBy(_._1)

由于我们对_ids感兴趣,我们需要获取每个Map对象的值,并且对于值序列的每个元素,都需要_id。

  .map(_._2.map(_._2))

现在我们有一系列序列。每个序列都包含没有_id的每个唯一文档的_ids。下一步是过滤序列,以便我们只有那些大小大于1的序列。换句话说,我们正在过滤代表重复文档的_ids。

  .filter(_.size > 1)

Le取每个序列的n-1个第一个_id。它们将是要删除的重复文档。

  .map({ids => ids.take(ids.size - 1)})

让我们将序列序列展平,以便我们有一系列_ids。

  .flatten

最后,我们可以从我们的集合中删除每个_id。我已经使用foreach方法完成了它,并逐个删除文档。由于我们使用的是订阅方法,因此文档将被异步删除。

  .foreach{
    id => collection.deleteOne(equal("_id", id)).subscribe(
      (dr: DeleteResult) => println(dr.getDeletedCount),
      (e: Throwable) => println(s"Error when deleting the document $id: $e")
    )
  }

希望它有所帮助!