41

I'm looking for a feasible way to get the length of cursor got from MongoDB.

QIAN KEQIAO
  • 541
  • 1
  • 4
  • 7

11 Answers11

41

It's actually as simple as

len(list(cursor))

Note that it will however consume the cursor.

Skippy le Grand Gourou
  • 6,976
  • 4
  • 60
  • 76
  • 7
    as of Sept 2020, cursor.count() returns a '''DeprecationWarning: count is deprecated. Use Collection.count_documents instead.''' While count_document returns: '''AttributeError: 'Cursor' object has no attribute 'count_documents'''' So len(list(cursor)) is what works better. – Marco Di Gennaro Sep 11 '20 at 13:44
  • 1
    When you say consume, do you mean move the position of the cursor, or does not convert the whole cursor contents (which could be huge) into a list? – jtlz2 Dec 17 '21 at 11:43
  • 1
    @jtlz2 I mean that the cursor is empty afterwards. I’d assume the content is converted into a list, which indeed may not be suitable for all usecases, but I didn’t test, maybe there are some optimizations indeed. – Skippy le Grand Gourou Dec 17 '21 at 16:50
22

The cursor.count method is deprecated since pymongo 3.7.

The recommended method is to use the count_documents method of the collection.

Gabriel Fair
  • 4,081
  • 5
  • 33
  • 54
Jérôme
  • 13,328
  • 7
  • 56
  • 106
  • 6
    does this mean if you want the count before you iterate you have to query twice - once for the collection.count_documents() and another for collection.find()? – barrypicker Jul 17 '19 at 19:34
  • 3
    Yes. One needs to call `count_documents` in an independent query. `cursor.count` also emitted an independent query, so this is not really a regression. However, this can be an issue for functions that take a cursor as input and need the cursor length. Those functions must be modified to also take the total as parameter, and caller code must be modified accordingly. (It might be possible to access collection and query filters from the cursor, as those are stored as double-underscored attributes, but that sounds bad.) – Jérôme Dec 31 '19 at 12:47
  • 1
    @Jérôme, but how do you propose to get the collection from the cursor resulting from a find? – Miguel Oct 21 '21 at 11:43
  • 2
    @Miguel `cursor.collection`? – Jérôme Oct 21 '21 at 12:25
  • Sorry, I didn't understand that I can apply the filter condition with count_documents(). Now it makes sense. – Miguel Oct 21 '21 at 13:45
  • `*** AttributeError: 'Cursor' object has no attribute 'count'` – alper May 30 '22 at 22:18
19

The variant of the previous answer:

len(list(cursor.clone()))

doesn't consume cursor

Yaroslav Boichuk
  • 1,763
  • 20
  • 31
Sa Lzrnk
  • 311
  • 2
  • 6
  • When you say consume, do you mean move the position of the cursor, or does not convert the whole cursor contents (which could be huge) into a list? – jtlz2 Dec 17 '21 at 09:51
7

cursor.count()

Counts the number of documents referenced by a cursor. Append the count() method to a find() query to return the number of matching documents. The operation does not perform the query but instead counts the results that would be returned by the query.

db.collection.find(<query>).count()

https://docs.mongodb.com/manual/reference/method/db.collection.count/

Kamlesh
  • 2,032
  • 2
  • 19
  • 35
  • 12
    cursor.count() is deprecated since pymongo 3.7. See my answer. – Jérôme Jan 11 '19 at 13:32
  • 1
    `cursor.count()` is deprecated and the new `countDocuments` is more accurate but much slower [see](https://stackoverflow.com/questions/52236594/why-is-pymongo-count-documents-is-slower-than-count) – carkod Sep 30 '22 at 17:34
5

len(list(cursor.clone())) worked really well for me, does not consume the editor so it can be use straight with your variable

Kaio Santos
  • 61
  • 1
  • 4
3

According to the pymongo documentation, a Pymongo cursor, has a count method:

count(with_limit_and_skip=False)

By default this method returns the total length of the cursor, for example:

cursor.count()

If you call this method with with_limit_and_skip=True, the returned value takes limit and skip queries into account. For example, the following query will return 5 (assuming you have more than 5 documents):

cursor.limit(5).count(True)
Alex
  • 21,273
  • 10
  • 61
  • 73
3

I find that using cursor.iter().count() is a feasible way to resolve this problem

jtlz2
  • 7,700
  • 9
  • 64
  • 114
QIAN KEQIAO
  • 541
  • 1
  • 4
  • 7
  • 4
    Traceback (most recent call last): File "test.py", line 12, in print (cursor.iter().count()) AttributeError: 'Cursor' object has no attribute 'iter' – barrypicker Jul 17 '19 at 19:35
1

For some reason, some aggregations return an object that doesn't have the same methods, maybe different class, simple solution, convert the pseudo cursor to an array:

// A simple aggregation with `group`
var cursor = db.getCollection('collection').aggregate([
    {$match: {
        "property": {"$exists": true }
    }},
    {$group: { 
        _id: '$groupable',
        count: {$sum: 1}
    }},
    {$sort: {
        count: -1
    }}
]);

// Converting the "cursor" into an array
var cursor_array = cursor.toArray();

// Looping as an array using `for` instead of `while`
for (var i = 0; i < cursor_array.length; i++) {
    print(cursor_array[i]._id+'\t'+cursor_array[i].count);
}

Notice this solution is only for the shell, I don't know if this array method exists in other libraries.

Rodrigo Polo
  • 4,314
  • 2
  • 26
  • 32
1

I was able to do count it this way:

def count():
    collection = db[col_name]
    count = collection.count_documents({"visited" : True})
    
    return count
0

How about sum(1 for _ in cursor.clone()) so that you get the count but using constant memory instead of creating a new list. And you don't have to make another query to mongo with this solution.

sjamr10
  • 13
  • 1
  • 3
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 24 '23 at 02:04
0

Hello I was trying to find a solution to this and I figured it out.

#Count documents takes a filter object inside(a dict) and you need to call it
#after a Collection object not cursor.
portfolio.count_documents({})

I hope this helps