Commits

Liam Staskawicz committed dbd0847

* Cursor.explain implementation and test

  • Participants
  • Parent commits c61eef5

Comments (0)

Files changed (3)

File fan/Cursor.fan

   //   return this
   // }
   
-  Map explain()
+  **
+  ** Return an explanation of how this Cursor's query will be executed,
+  ** with regard to index usage, etc.
+  **
+  Str:Obj explain()
   {
-    expopts := this.opts.dup
-    expopts["explain"] = true
-    c := Cursor(coll, this.selector, expopts)
+    eopts := this.opts.dup
+    eopts.set("explain", true).set("batchsize", -1)
+    c := Cursor(coll, this.selector, eopts)
     explanation := c.next
     c.close
     return explanation
   }
   
+  **
+  ** Returns a List with the fully retrieved contents of this Cursor
+  ** Note - if your query matches a lot of results, this could consume
+  ** quite a lot of memory.
+  **
   List toList()
   {
     objects := Map[,]
     return m
   }
   
+  private Str:Obj specialFields()
+  {
+    Str:Obj special := [:] { ordered = true }
+    if(opts.containsKey("explain"))
+      special["\$explain"] = true
+    // todo snapshot, hint and order
+    return special
+  }
+  
   private Void doQuery(Int numToRetrieve)
   {
     b := Buf() { endian = Endian.little }
     Bson.writeCStr(b.out, fullName)             // full name
     b.writeI4(opts.get("skip", 0))              // skip
     b.writeI4(numToRetrieve)                    // num to return
-    Bson.write(b.out, selector)                 // query object
-    if(opts.containsKey("fields"))              // optional fieldReturnSelector
-      Bson.write(b.out, fieldsForQuery(opts["fields"]))
+    
+    sf := specialFields()
+    if(sf.size > 0) {
+      sf["query"] = selector
+      Bson.write(b.out, sf)                     // query object
+      // no need to check for fields in this case
+    }
+    else {
+      Bson.write(b.out, selector)                 // query object
+      if(opts.containsKey("fields"))              // optional fieldReturnSelector
+        Bson.write(b.out, fieldsForQuery(opts["fields"]))
+    }
     
     s := coll.db.connection.getSocket()
     reqID := coll.db.connection.sendMsg(s.out, b.flip, MongoOp.QUERY)
     if(cmd.keys.size > 1 && !cmd.ordered)
       throw ArgErr("commands with more than one key must be ordered")
     // negative batchsize means "return the abs value specified and close the cursor" 
-    opts := [:]
-    opts["batchsize"] = -1
+    Str:Obj opts := ["batchsize": -1]
     if(admin == true) opts["admin"] = true
     return collection("\$cmd").findOne(cmd, opts)
   }

File test/CursorTest.fan

     verify(po.keys.containsAll(["field1", "field2"]))
     coll.drop
   }
+  
+  Void testExplain()
+  {
+    coll := db["explaintest"]
+    coll.drop
+    i := 0
+    10.times { coll.insert(["something":i++]) }
+    
+    e := coll.find(["something":["\$gt":2]]).explain
+    verify(e.keys.containsAll(["cursor", "startKey", "endKey", "nscanned", "millis", "n"]))
+    
+    coll.drop
+  }
 
 }