8

I have a collection, from which i get particular type of users using $query
Then I need sort them according to user_id ascending and limit them to 2000

From these I need the max user_id, so I sort them in descending order and limit to 1.

But this second sort forgets the limit of 2000 and sorts over over the entire cursor from find().

Any work-around?

$cursor = $collection   ->find($query)                // too many entries
            ->sort(array('user_id'=>1))   // go ascending
            ->limit(2000)                // got our limited qouta
            ->sort(array('user_id'=>-1)) // go descending to get max
            ->limit(1);          // the one with max(user_id)
2
  • You have a semicolon on the third line. Is that a copy/paste error or is it also present in your original code? Commented Jan 20, 2013 at 10:39
  • oh yes, copy paste error, thanks for pointing out, corrected Commented Jan 20, 2013 at 10:42

5 Answers 5

7

Your cannot do a sort and then a limit and then a sort. The Cursor object is exactly that and it will not run the query until you iterate to the first result via getNext() which is either run manually or within a foreach loop not only that but sort is just a property of the object as such making two sorts just overwrites the property.

The best way to achieve what your looking for is:

$doc = $collection->find($query)->sort(array('user_id' => 1))
       ->skip(1999)->limit(1)->getNext();

That will always pick the highest user_id (which occurs at the end in this sort) of the group, which will give off the same results as doing two sorts.

Sign up to request clarification or add additional context in comments.

3 Comments

this one looked like correct. I tried collection->find->sort->skip(1999)->limit->getNext as well as collection->find->sort->skip(1999)->getNext. Both are giving same (but incorrect) answer. Will post back when i figure it out.
we can do $collection->find()->sort()->skip(1999)->getNext(); the limit(1) in between can be definitely skipped
@davneet Yea I didn't think of that, getNext() of course just gets the next document so the limit is inert there.
3

How about using skip():

$cursor = $collection   ->find($query)  
        ->sort(array('user_id'=>1))   
        ->limit(2000)                
        ->skip(1999);

3 Comments

+1 Wouldn't a limit(1) here work just as well since only the first of the docs after the 1999 skipped ones is needed?
the above code still gives me 2000 records in the cursor... that would mean ill have to iterate over all those to get to the last record
ups, sorry. find(...)->sort(...)->skip(1999)->limit(1) is probably what you need.
0

What is the reason behind sort-limit-sort-limit approach?

Can't you just do

$cursor = $collection ->find($query)
            ->sort(array('user_id'=>-1))
            ->limit(1);

EDIT: Also, only the last sort applied to the cursor has an effect. (This line is present in pymongo docs here, which makes sense.)

2 Comments

the point of first sort-limit is to get the first 2000 records out of all. the second sort-limit simply gets the max out of these 2000. Ur code will give me max out of all records from find()
Ok, now I get it. You're trying to get the 2000th document in ASC order that matches the query. You should know that when you apply sort on a cursor. No documents are retrieved (therefore sorted) until you actually iterate over that cursor. You can use skip but that becomes a slow operation. Best approach is to use range based queries in situations like this.
0

I am using the following code in and latest build:

$doc = $collection->find($query)->sort(array('user_id' => 1))
    ->skip(1999)->limit(1)->getNext();

It stops working when I use ->skip(1999)->limit(1) after sort()

The cursor $doc does give me values . I tried braking it down into this:

$doc = $collection->find($query)->sort(array('user_id' => 1))
$doc = $doc->skip(1999)->limit(1);

That worked. May be you should try that in new versions.

Comments

0

Answer by Sammaye is correct.

You can still achieve the way you wanted. You can use aggregation framework, as it executes one stage after other stage and so on.

$doc = $collection->aggregate(array(
    array(
      '$match' => $query,
    ),
    array(
      '$sort' => array(
          'user_id'=> 1
      ),
    ),
    array(
      '$limit' => 2000
    ),
    array(
      '$sort' => array(
          'user_id'=> -1
      ),
    ),
    array(
      '$limit' => 1
    ),
));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.