Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-18830

Explain on near query with limit leaves out last search interval

    • Type: Icon: Bug Bug
    • Resolution: Done
    • Priority: Icon: Minor - P4 Minor - P4
    • 3.1.5
    • Affects Version/s: None
    • Component/s: Geo
    • None
    • Fully Compatible
    • ALL
    • Hide

      To reproduce, first generate a geo collection. The following script will generate a uniform density dataset around (10,10)

      /* Modified from SERVER-18724: */
      // generate a grid map with geoJSON format
      function generateGridMapGeoJSON(collection, x1, y1, x2, y2, indexType) {
          var step_x = (x2 - x1) / 100.0;
          var step_y = (y2 - y1) / 100.0;
       
          collection.drop();
          collection.ensureIndex({loc: indexType}, {finestIndexedLevel:30, coarsestIndexedLevel:10});
       
          for( var i = x1; i < x2; ) {
              var bulk = collection.initializeUnorderedBulkOp();
       
              for(var j = y1; j < y2; ) {
                  bulk.insert({loc: {type: "Point", coordinates: [i, j]}});
                  j = j + step_y;
              }
              bulk.execute( {w: 1});
              i = i + step_x;
          }
          collection.getDB().getLastError();
      }
       
      // define the area for the map in collection
      var x_min = 9.995;
      var x_max =  10.005;
      var y_min = 9.995;
      var y_max =  10.005;
      
      // define the area to run query from
      // leave 1/7 out on each edge to make sure query are not run out of bound
      var x_query_min = x_min * (6.0/7.0);
      var x_query_max = x_max * (6.0/7.0);
      var y_query_min = y_min * (6.0/7.0);
      var y_query_max = y_max * (6.0/7.0);
       
      // query will run from a 13x13 grid
      var x_query_step = (x_query_max - x_query_min) / 13.0;
      var y_query_step = (y_query_max - y_query_min) / 13.0;
       
      var collection = db.getCollection("geo")
      generateGridMapGeoJSON(collection,  x_min, y_min, x_max, y_max, "2dsphere");
      

      Now, run the following query:

      db.geo.find({loc: { $near: {$geometry: {type: "Point", coordinates:  [ 10.000329670329670329, 10.0009890109890109888 ] } } } } ).limit(50).explain("executionStats")
      

      In the output, you will see the following (modified for readability):

      "inputStage" : {
      	"stage" : "GEO_NEAR_2DSPHERE",
      	(...)
      	"searchIntervals" : [
      		{
      			"minDistance" : 0,
      			"maxDistance" : 13.313786309945902,
      			"maxInclusive" : false
      		},
      		{
      			"minDistance" : 13.313786309945902,
      			"maxDistance" : 39.941358929837705,
      			"maxInclusive" : false
      		}
      	],
      	"inputStages" : [
      		{
      			(...)
      		},
      		{
      			(...)
      		},
      		{
      			(...)
      		}
      	]
      }
      

      The number of inputStages is 3, but the number of displayed searchIntervals is only 2. They should be equal.

      Show
      To reproduce, first generate a geo collection. The following script will generate a uniform density dataset around (10,10) /* Modified from SERVER-18724: */ // generate a grid map with geoJSON format function generateGridMapGeoJSON(collection, x1, y1, x2, y2, indexType) { var step_x = (x2 - x1) / 100.0; var step_y = (y2 - y1) / 100.0; collection.drop(); collection.ensureIndex({loc: indexType}, {finestIndexedLevel:30, coarsestIndexedLevel:10}); for ( var i = x1; i < x2; ) { var bulk = collection.initializeUnorderedBulkOp(); for ( var j = y1; j < y2; ) { bulk.insert({loc: {type: "Point" , coordinates: [i, j]}}); j = j + step_y; } bulk.execute( {w: 1}); i = i + step_x; } collection.getDB().getLastError(); } // define the area for the map in collection var x_min = 9.995; var x_max = 10.005; var y_min = 9.995; var y_max = 10.005; // define the area to run query from // leave 1/7 out on each edge to make sure query are not run out of bound var x_query_min = x_min * (6.0/7.0); var x_query_max = x_max * (6.0/7.0); var y_query_min = y_min * (6.0/7.0); var y_query_max = y_max * (6.0/7.0); // query will run from a 13x13 grid var x_query_step = (x_query_max - x_query_min) / 13.0; var y_query_step = (y_query_max - y_query_min) / 13.0; var collection = db.getCollection( "geo" ) generateGridMapGeoJSON(collection, x_min, y_min, x_max, y_max, "2dsphere" ); Now, run the following query: db.geo.find({loc: { $near: {$geometry: {type: "Point" , coordinates: [ 10.000329670329670329, 10.0009890109890109888 ] } } } } ).limit(50).explain( "executionStats" ) In the output, you will see the following (modified for readability): "inputStage" : { "stage" : "GEO_NEAR_2DSPHERE" , (...) "searchIntervals" : [ { "minDistance" : 0, "maxDistance" : 13.313786309945902, "maxInclusive" : false }, { "minDistance" : 13.313786309945902, "maxDistance" : 39.941358929837705, "maxInclusive" : false } ], "inputStages" : [ { (...) }, { (...) }, { (...) } ] } The number of inputStages is 3, but the number of displayed searchIntervals is only 2. They should be equal.
    • RPL 5 06/26/16

      When performing an explain("executionStats") on a $near query, the searchIntervals field should contain all of the annuluses which are scanned. However, when a query is done with a limit, like the following:

      db.geo.find({loc: { $near: {$geometry: {type: "Point", coordinates:  [ 10.000329670329670329, 10.0009890109890109888 ] } } } } ).limit(50).explain("executionStats")
      

      The number of searchIntervals on the GEO_NEAR_2DSPHERE stage is one less than it should be. It should equal the number of inputStages.

      The reason this occurs is because in near.cpp, the method NearStage::advanceNext will only add to the intervalStats when the current buffer is finished. The limit will end early, before the buffer finishes. Therefore, the annulus will never be reported.

            Assignee:
            brandon.zhang Brandon Zhang
            Reporter:
            kevin.albertson@mongodb.com Kevin Albertson
            Votes:
            0 Vote for this issue
            Watchers:
            8 Start watching this issue

              Created:
              Updated:
              Resolved: