Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

multiple $in support SERVER-103

  • Loading branch information...
commit 61c0327ba54f44e7438346fd4df13c75b4e578c3 1 parent d73d458
Eliot authored July 07, 2009
90  db/matcher.cpp
@@ -105,9 +105,6 @@ namespace mongo {
105 105
     };
106 106
 
107 107
     JSMatcher::~JSMatcher() {
108  
-        delete in;
109  
-        delete nin;
110  
-        delete all;
111 108
         delete where;
112 109
     }
113 110
 
@@ -135,8 +132,8 @@ namespace mongo {
135 132
     /* _jsobj          - the query pattern
136 133
     */
137 134
     JSMatcher::JSMatcher(const BSONObj &_jsobj, const BSONObj &constrainIndexKey) :
138  
-            in(0), nin(0), all(0), where(0), jsobj(_jsobj), haveSize(), nRegex(0)
139  
-    {
  135
+        where(0), jsobj(_jsobj), haveSize(), all(), nRegex(0){
  136
+
140 137
         BSONObjIterator i(jsobj);
141 138
         while ( i.moreWithEOO() ) {
142 139
             BSONElement e = i.next();
@@ -189,7 +186,7 @@ namespace mongo {
189 186
                 }
190 187
                 continue;
191 188
             }
192  
-
  189
+            
193 190
             // greater than / less than...
194 191
             // e.g., e == { a : { $gt : 3 } }
195 192
             //       or
@@ -244,51 +241,19 @@ namespace mongo {
244 241
                         }
245 242
                         else if ( fn[1] == 'i' && fn[2] == 'n' && fn[3] == 0 && fe.type() == Array ) {
246 243
                             // $in
247  
-                            uassert( "only 1 $in statement per query supported", in == 0 ); // todo...
248  
-                            in = new set<BSONElement,element_lt>();
249  
-                            BSONObjIterator i(fe.embeddedObject());
250  
-                            if ( i.moreWithEOO() ) {
251  
-                                while ( 1 ) {
252  
-                                    BSONElement ie = i.next();
253  
-                                    if ( ie.eoo() )
254  
-                                        break;
255  
-                                    in->insert(ie);
256  
-                                }
257  
-                            }
258  
-                            addBasic(e, BSONObj::opIN); // e not actually used at the moment for $in
  244
+                            basics.push_back( BasicMatcher( e , BSONObj::opIN , fe.embeddedObject() ) );
259 245
                             ok = true;
260 246
                         }
261 247
                         else if ( fn[1] == 'n' && fn[2] == 'i' && fn[3] == 'n' && fn[4] == 0 && fe.type() == Array ) {
262 248
                             // $nin
263  
-                            uassert( "only 1 $nin statement per query supported", nin == 0 ); // todo...
264  
-                            nin = new set<BSONElement,element_lt>();
265  
-                            BSONObjIterator i(fe.embeddedObject());
266  
-                            if ( i.moreWithEOO() ) {
267  
-                                while ( 1 ) {
268  
-                                    BSONElement ie = i.next();
269  
-                                    if ( ie.eoo() )
270  
-                                        break;
271  
-                                    nin->insert(ie);
272  
-                                }
273  
-                            }
274  
-                            addBasic(e, BSONObj::NIN); // e not actually used at the moment for $nin
  249
+                            basics.push_back( BasicMatcher( e , BSONObj::NIN , fe.embeddedObject() ) );
275 250
                             ok = true;
276 251
                         }
277 252
                         else if ( fn[1] == 'a' && fn[2] == 'l' && fn[3] == 'l' && fn[4] == 0 && fe.type() == Array ) {
278 253
                             // $all
279  
-                            uassert( "only 1 $all statement per query supported", all == 0 ); // todo...
280  
-                            all = new set<BSONElement,element_lt>();
281  
-                            BSONObjIterator i(fe.embeddedObject());
282  
-                            if ( i.moreWithEOO() ) {
283  
-                                while ( 1 ) {
284  
-                                    BSONElement ie = i.next();
285  
-                                    if ( ie.eoo() )
286  
-                                        break;
287  
-                                    all->insert(ie);
288  
-                                }
289  
-                            }
290  
-                            addBasic(e, BSONObj::opALL); // e not actually used at the moment for $all
  254
+                            basics.push_back( BasicMatcher( e , BSONObj::opALL , fe.embeddedObject() ) );
291 255
                             ok = true;
  256
+                            all = true;
292 257
                         }
293 258
                         else if ( fn[1] == 's' && fn[2] == 'i' && fn[3] == 'z' && fn[4] == 'e' && fe.isNumber() ) {
294 259
                             shared_ptr< BSONObjBuilder > b( new BSONObjBuilder() );
@@ -309,7 +274,7 @@ namespace mongo {
309 274
                 if ( ok )
310 275
                     continue;
311 276
             }
312  
-
  277
+            
313 278
             // normal, simple case e.g. { a : "foo" }
314 279
             addBasic(e, BSONObj::Equality);
315 280
         }
@@ -317,16 +282,15 @@ namespace mongo {
317 282
         constrainIndexKey_ = constrainIndexKey;
318 283
     }
319 284
 
320  
-    inline int JSMatcher::valuesMatch(const BSONElement& l, const BSONElement& r, int op, bool *deep) {
  285
+    inline int JSMatcher::valuesMatch(const BSONElement& l, const BSONElement& r, int op, const BasicMatcher& bm, bool *deep) {
321 286
         assert( op != BSONObj::NE && op != BSONObj::NIN );
322 287
         
323 288
         if ( op == 0 )
324 289
             return l.valuesEqual(r);
325  
-
  290
+        
326 291
         if ( op == BSONObj::opIN ) {
327 292
             // { $in : [1,2,3] }
328  
-            int c = in->count(l);
329  
-            return c;
  293
+            return bm.myset->count(l);
330 294
         }
331 295
 
332 296
         if ( op == BSONObj::opSIZE ) {
@@ -352,10 +316,10 @@ namespace mongo {
352 316
                 BSONElement e = i.next();
353 317
                 if ( e.eoo() )
354 318
                     break;
355  
-                if ( all->count( e ) )
  319
+                if ( bm.myset->count( e ) )
356 320
                     matches.insert( e );
357 321
             }
358  
-            if ( all->size() == matches.size() ) {
  322
+            if ( bm.myset->size() == matches.size() ) {
359 323
                 if ( deep )
360 324
                     *deep = true;
361 325
                 return true;
@@ -373,8 +337,8 @@ namespace mongo {
373 337
         return (op & z);
374 338
     }
375 339
 
376  
-    int JSMatcher::matchesNe(const char *fieldName, const BSONElement &toMatch, const BSONObj &obj, bool *deep) {
377  
-        int ret = matchesDotted( fieldName, toMatch, obj, BSONObj::Equality, deep );
  340
+    int JSMatcher::matchesNe(const char *fieldName, const BSONElement &toMatch, const BSONObj &obj, const BasicMatcher& bm, bool *deep) {
  341
+        int ret = matchesDotted( fieldName, toMatch, obj, BSONObj::Equality, bm, deep );
378 342
         return -ret;
379 343
     }
380 344
     
@@ -392,19 +356,19 @@ namespace mongo {
392 356
          { "a.b" : 3 }             means       obj.a.b == 3
393 357
          { a : { $lt : 3 } }       means       obj.a < 3
394 358
     	 { a : { $in : [1,2] } }   means       [1,2].contains(obj.a)
395  
-
396  
-       return value
  359
+         
  360
+         return value
397 361
        -1 mismatch
398 362
         0 missing element
399 363
         1 match
400 364
     */
401  
-    int JSMatcher::matchesDotted(const char *fieldName, const BSONElement& toMatch, const BSONObj& obj, int compareOp, bool *deep, bool isArr) {
402  
-        
  365
+    int JSMatcher::matchesDotted(const char *fieldName, const BSONElement& toMatch, const BSONObj& obj, int compareOp, const BasicMatcher& bm , bool *deep, bool isArr) {
  366
+
403 367
         if ( compareOp == BSONObj::NE )
404  
-            return matchesNe( fieldName, toMatch, obj, deep );
  368
+            return matchesNe( fieldName, toMatch, obj, bm, deep );
405 369
         if ( compareOp == BSONObj::NIN ) {
406  
-            for( set<BSONElement,element_lt>::const_iterator i = nin->begin(); i != nin->end(); ++i ) {
407  
-                int ret = matchesNe( fieldName, *i, obj, deep );
  370
+            for( set<BSONElement,element_lt>::const_iterator i = bm.myset->begin(); i != bm.myset->end(); ++i ) {
  371
+                int ret = matchesNe( fieldName, *i, obj, bm, deep );
408 372
                 if ( ret != 1 )
409 373
                     return ret;
410 374
                 // code to handle 0 (missing) return value doesn't deal with nin yet
@@ -425,7 +389,7 @@ namespace mongo {
425 389
                     BSONElement z = ai.next();
426 390
                     if ( z.type() == Object ) {
427 391
                         BSONObj eo = z.embeddedObject();
428  
-                        int cmp = matchesDotted(fieldName, toMatch, eo, compareOp, deep, false);
  392
+                        int cmp = matchesDotted(fieldName, toMatch, eo, compareOp, bm, deep, false);
429 393
                         if ( cmp > 0 ) {
430 394
                             if ( deep ) *deep = true;
431 395
                             return 1;
@@ -447,20 +411,20 @@ namespace mongo {
447 411
                     return -1;
448 412
 
449 413
                 BSONObj eo = se.embeddedObject();
450  
-                return matchesDotted(p+1, toMatch, eo, compareOp, deep, se.type() == Array);
  414
+                return matchesDotted(p+1, toMatch, eo, compareOp, bm, deep, se.type() == Array);
451 415
             } else {
452 416
                 e = obj.getField(fieldName);
453 417
             }
454 418
         }
455 419
         
456 420
         if ( ( e.type() != Array || indexed || compareOp == BSONObj::opALL || compareOp == BSONObj::opSIZE ) &&
457  
-            valuesMatch(e, toMatch, compareOp, deep) ) {
  421
+            valuesMatch(e, toMatch, compareOp, bm, deep) ) {
458 422
             return 1;
459 423
         } else if ( e.type() == Array && compareOp != BSONObj::opALL && compareOp != BSONObj::opSIZE ) {
460 424
             BSONObjIterator ai(e.embeddedObject());
461 425
             while ( ai.moreWithEOO() ) {
462 426
                 BSONElement z = ai.next();
463  
-                if ( valuesMatch( z, toMatch, compareOp) ) {
  427
+                if ( valuesMatch( z, toMatch, compareOp, bm) ) {
464 428
                     if ( deep )
465 429
                         *deep = true;
466 430
                     return 1;
@@ -509,7 +473,7 @@ namespace mongo {
509 473
             BasicMatcher& bm = basics[i];
510 474
             BSONElement& m = bm.toMatch;
511 475
             // -1=mismatch. 0=missing element. 1=match
512  
-            int cmp = matchesDotted(m.fieldName(), m, jsobj, bm.compareOp, deep);
  476
+            int cmp = matchesDotted(m.fieldName(), m, jsobj, bm.compareOp, bm, deep);
513 477
             if ( cmp < 0 )
514 478
                 return false;
515 479
             if ( cmp == 0 ) {
61  db/matcher.h
@@ -24,7 +24,7 @@
24 24
 #include <pcrecpp.h>
25 25
 
26 26
 namespace mongo {
27  
-
  27
+    
28 28
     class RegexMatcher {
29 29
     public:
30 30
         const char *fieldName;
@@ -36,11 +36,44 @@ namespace mongo {
36 36
             delete re;
37 37
         }
38 38
     };
  39
+    
  40
+    struct element_lt
  41
+    {
  42
+        bool operator()(const BSONElement& l, const BSONElement& r) const
  43
+        {
  44
+            int x = (int) l.type() - (int) r.type();
  45
+            if ( x == ( NumberInt - NumberDouble ) || x == ( NumberDouble - NumberInt ) );
  46
+            else if ( x < 0 ) return true;
  47
+            else if ( x > 0 ) return false;
  48
+            return compareElementValues(l,r) < 0;
  49
+        }
  50
+    };
39 51
 
  52
+    
40 53
     class BasicMatcher {
41 54
     public:
  55
+    
  56
+        BasicMatcher(){
  57
+        }
  58
+        
  59
+        BasicMatcher( BSONElement _e , int _op ) : toMatch( _e ) , compareOp( _op ){
  60
+        }
  61
+
  62
+
  63
+        BasicMatcher( BSONElement _e , int _op , const BSONObj& array ) : toMatch( _e ) , compareOp( _op ){
  64
+            
  65
+            myset.reset( new set<BSONElement,element_lt>() );
  66
+            
  67
+            BSONObjIterator i( array );
  68
+            while ( i.more() ) {
  69
+                BSONElement ie = i.next();
  70
+                myset->insert(ie);
  71
+            }
  72
+        }
  73
+        
42 74
         BSONElement toMatch;
43 75
         int compareOp;
  76
+        shared_ptr< set<BSONElement,element_lt> > myset;
44 77
     };
45 78
 
46 79
 // SQL where clause equivalent
@@ -66,24 +99,13 @@ namespace mongo {
66 99
         int matchesDotted(
67 100
             const char *fieldName,
68 101
             const BSONElement& toMatch, const BSONObj& obj,
69  
-            int compareOp, bool *deep, bool isArr = false);
  102
+            int compareOp, const BasicMatcher& bm, bool *deep, bool isArr = false);
70 103
 
71 104
         int matchesNe(
72 105
             const char *fieldName,
73 106
             const BSONElement &toMatch, const BSONObj &obj,
74  
-            bool *deep);
  107
+            const BasicMatcher&bm, bool *deep);
75 108
         
76  
-        struct element_lt
77  
-        {
78  
-            bool operator()(const BSONElement& l, const BSONElement& r) const
79  
-            {
80  
-                int x = (int) l.type() - (int) r.type();
81  
-                if ( x == ( NumberInt - NumberDouble ) || x == ( NumberDouble - NumberInt ) );
82  
-                else if ( x < 0 ) return true;
83  
-                else if ( x > 0 ) return false;
84  
-                return compareElementValues(l,r) < 0;
85  
-            }
86  
-        };
87 109
     public:
88 110
         static int opDirection(int op) {
89 111
             return op <= BSONObj::LTE ? -1 : 1;
@@ -105,17 +127,11 @@ namespace mongo {
105 127
             // TODO May want to selectively ignore these element types based on op type.
106 128
             if ( e.type() == MinKey || e.type() == MaxKey )
107 129
                 return;
108  
-            BasicMatcher bm;
109  
-            bm.toMatch = e;
110  
-            bm.compareOp = c;
111  
-            basics.push_back(bm);
  130
+            basics.push_back( BasicMatcher( e , c ) );
112 131
         }
113 132
 
114  
-        int valuesMatch(const BSONElement& l, const BSONElement& r, int op, bool *deep=0);
  133
+        int valuesMatch(const BSONElement& l, const BSONElement& r, int op, const BasicMatcher& bm , bool *deep=0);
115 134
 
116  
-        set<BSONElement,element_lt> *in; // set if query uses $in
117  
-        set<BSONElement,element_lt> *nin; // set if query uses $nin
118  
-        set<BSONElement,element_lt> *all; // set if query uses $all
119 135
         Where *where;                    // set if query uses $where
120 136
         BSONObj jsobj;                  // the query pattern.  e.g., { name: "joe" }
121 137
         BSONObj constrainIndexKey_;
@@ -123,6 +139,7 @@ namespace mongo {
123 139
         vector<BasicMatcher> basics;
124 140
 //        int n;                           // # of basicmatcher items
125 141
         bool haveSize;
  142
+        bool all;
126 143
 
127 144
         RegexMatcher regexs[4];
128 145
         int nRegex;
33  jstests/in2.js
... ...
@@ -0,0 +1,33 @@
  1
+
  2
+t = db.in2;
  3
+
  4
+function go( name , index ){
  5
+
  6
+    t.drop();
  7
+    
  8
+    t.save( { a : 1 , b : 1 } );
  9
+    t.save( { a : 1 , b : 2 } );
  10
+    t.save( { a : 1 , b : 3 } );
  11
+    
  12
+    t.save( { a : 1 , b : 1 } );
  13
+    t.save( { a : 2 , b : 2 } );
  14
+    t.save( { a : 3 , b : 3 } );
  15
+    
  16
+    t.save( { a : 1 , b : 1 } );
  17
+    t.save( { a : 2 , b : 1 } );
  18
+    t.save( { a : 3 , b : 1 } );
  19
+
  20
+    if ( index )
  21
+        t.ensureIndex( index );
  22
+    
  23
+    assert.eq( 7 , t.find( { a : { $in : [ 1 , 2 ] } } ).count() , name + " A" );
  24
+
  25
+    assert.eq( 6 , t.find( { a : { $in : [ 1 , 2 ] } , b : { $in : [ 1 , 2 ] } } ).count() , name + " B" );
  26
+}
  27
+
  28
+go( "no index" );
  29
+go( "index on a" , { a : 1 } );
  30
+go( "index on b" , { b : 1 } );
  31
+go( "index on a&b" , { a : 1 , b : 1 } );
  32
+
  33
+

0 notes on commit 61c0327

Please sign in to comment.
Something went wrong with that request. Please try again.