Repository: Refty/mongo-thingy

Issue:
Implement Cursor.delete()
So that we can remove a bunch of documents that exist from a cursor.

This is useful when we want to reuse a cursor rather than writing a new `remove` query.

For example:

```python
class Foo(Thingy):
    def get_bars(self):
        return Bar.find({"foo_id": self.id})

    def delete_bars(self):
        return self.get_bars().delete()
```


Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/50582048/badge)](https://coveralls.io/builds/50582048)

Coverage remained the same at 100.0% when pulling **006c516e73e98e2e4c6168d7665f3a64aa376700 on cursor-delete** into **23231d9c34d9425e02b97de78787f466538d42c9 on master**.


Patch:
diff --git a/mongo_thingy/cursor.py b/mongo_thingy/cursor.py
index 9539b7f..9636fa9 100644
--- a/mongo_thingy/cursor.py
+++ b/mongo_thingy/cursor.py
@@ -94,6 +94,10 @@ class Cursor(BaseCursor):
         document = self.delegate.__getitem__(index)
         return self.bind(document)
 
+    def delete(self):
+        ids = self.distinct("_id")
+        return self.thingy_cls.collection.delete_many({"_id": {"$in": ids}})
+
     def first(self):
         try:
             document = self.delegate.clone().limit(-1).next()
@@ -110,6 +114,10 @@ class AsyncCursor(BaseCursor):
         async for document in self.delegate:
             yield self.bind(document)
 
+    async def delete(self):
+        ids = await self.distinct("_id")
+        return await self.thingy_cls.collection.delete_many({"_id": {"$in": ids}})
+
     async def first(self):
         try:
             document = await self.delegate.clone().limit(-1).next()


Test patch:
diff --git a/tests/cursor.py b/tests/cursor.py
index 695e287..4a60f35 100644
--- a/tests/cursor.py
+++ b/tests/cursor.py
@@ -1,3 +1,5 @@
+import pytest
+
 from mongo_thingy import Thingy
 from mongo_thingy.cursor import (_Proxy, _BindingProxy, _ChainingProxy,
                                  AsyncCursor, Cursor,)
@@ -262,3 +264,36 @@ async def test_async_cursor_view(thingy_cls, collection):
 
     async for dictionnary in Foo.find().view("empty"):
         assert dictionnary == {}
+
+
+@pytest.mark.ignore_backends("montydb")
+def test_cursor_delete(thingy_cls, collection):
+    class Foo(thingy_cls):
+        _collection = collection
+
+    collection.insert_many([{"bar": "baz"},
+                            {"bar": "qux"}])
+    Foo.find().delete()
+    assert collection.count_documents({}) == 0
+
+    collection.insert_many([{"bar": "baz"},
+                            {"bar": "qux"}])
+    Foo.find({"bar": "baz"}).delete()
+    assert collection.count_documents({}) == 1
+    assert Foo.find_one().bar == "qux"
+
+
+async def test_async_cursor_delete(thingy_cls, collection):
+    class Foo(thingy_cls):
+        _collection = collection
+
+    await collection.insert_many([{"bar": "baz"},
+                                  {"bar": "qux"}])
+    await Foo.find().delete()
+    assert await collection.count_documents({}) == 0
+
+    await collection.insert_many([{"bar": "baz"},
+                                  {"bar": "qux"}])
+    await Foo.find({"bar": "baz"}).delete()
+    assert await collection.count_documents({}) == 1
+    assert (await Foo.find_one()).bar == "qux"