title: Improve performance of MongoDB $geoIntersects query
body: <p>I'm trying to improve a MongoDB query that queries documents by a point. The documents have a simple polygon geometry with between 10 and 100 points and between 50m and 5000m across. An example document:</p>
  
  <pre><code>{
      "_id" : UUID("9175a387-4b0c-48ab-a74a-a8b200cc5285"),
      "seconds" : NumberLong(123),
      "geometry" : {
          "type" : "Polygon",
          "coordinates" : [ [ 
              [ 28.08093, -26.00869 ], 
              [ 28.09113, -26.01888 ], 
              [ 28.09486, -26.03282 ],  
              [ 28.08093, -26.00869 ] ]
          ]
      }
  }
  </code></pre>
  
  <p>The query uses <code>$geoIntersects</code> filtering by a point and orders the results by <code>seconds</code>. There is a geospatial index on geometry and seconds, and I have tested that it does get used during this query:</p>
  
  <pre><code>IXSCAN { geometry: "2dsphere", seconds: 1 }
  </code></pre>
  
  <p>As we are using the MongoDB C# driver this is not the exact query but rather what I retrieved by profiling, but an example of the query is as follows:</p>
  
  <pre><code>db.getCollection('fooCollection')
  .find({
      "geometry" : {
          "$geoIntersects" : {
              "$geometry" : {
                  "type" : "Point",
                  "coordinates" : [ 28.04, -26.19 ]
              } } } })
  .sort({ "seconds" : 1 })
  .projection({ "geometry" : 0 })
  </code></pre>
  
  <p>Since we only need geometry for querying, we also project just the id and the seconds field to reduce data I/O. The geometry is an approximation and does not have to be a perfect match. </p>
  
  <p>This query is used during an extremely performance critical task and it is a bottleneck, taking about 170ms to complete. This is nowhere near as quick as our other Mongo queries which typically complete in 20-30ms end to end. </p>
  
  <p>I don't know enough about the internals of the <code>$geoIntersects</code> query or geospatial indexes to know if there is any way I can improve the performance of this query. Some (rather time consuming) ideas that I'm not sure are worth pursuing:</p>
  
  <ul>
  <li><p>Simplify all the geometry to use fewer points.</p></li>
  <li><p>Use a simpler reference geometry than 2dsphere. A flat approximation would be close enough for our purposes.</p></li>
  <li><p>Nuclear option: Cache all of the geometry in memory and perform the collision detection ourselves. This is contingent on writing such an algorithm and index lookup as efficient as MongoDB's.</p></li>
  </ul>
  
  <p>Is there anything I'm missing with this operation? <strong>What is the best way to improve the performance of a MongoDB <code>$geoIntersects</code> query?</strong></p>
  
  <p>EDIT: Added the following explain() output as requested:</p>
  
  <pre><code>{
      "queryPlanner" : {
          "plannerVersion" : 1,
          "namespace" : "fooCollection",
          "indexFilterSet" : false,
          "parsedQuery" : {
              "geometry" : {
                  "$geoIntersects" : {
                      "$geometry" : {
                          "type" : "Point",
                          "coordinates" : [ 
                              28.04, 
                              -26.19
                          ]
                      }
                  }
              }
          },
          "winningPlan" : {
              "stage" : "FETCH",
              "filter" : {
                  "geometry" : {
                      "$geoIntersects" : {
                          "$geometry" : {
                              "type" : "Point",
                              "coordinates" : [ 
                                  28.04, 
                                  -26.19
                              ]
                          }
                      }
                  }
              },
              "inputStage" : {
                  "stage" : "IXSCAN",
                  "keyPattern" : {
                      "geometry" : "2dsphere",
                      "seconds" : 1
                  },
                  "indexName" : "Geometry_Seconds",
                  "isMultiKey" : true,
                  "multiKeyPaths" : {
                      "geometry" : [ 
                          "geometry"
                      ],
                      "seconds" : []
                  },
                  "isUnique" : false,
                  "isSparse" : false,
                  "isPartial" : false,
                  "indexVersion" : 2,
                  "direction" : "forward",
                  "indexBounds" : {
                      "geometry" : [ 
                          "[2202260217784172544, 2202260217784172544]", 
                          "[2203386117691015168, 2203386117691015168]", 
                          "[2203667592667725824, 2203667592667725824]", 
                          "[2233785415175766016, 2233785415175766016]"
                      ],
                      "seconds" : [ 
                          "[MinKey, MaxKey]"
                      ]
                  }
              }
          },
          "rejectedPlans" : []
      },
      "executionStats" : {
          "executionSuccess" : true,
          "nReturned" : 29,
          "executionTimeMillis" : 5,
          "totalKeysExamined" : 58,
          "totalDocsExamined" : 46,
          "executionStages" : {
              "stage" : "FETCH",
              "filter" : {
                  "geometry" : {
                      "$geoIntersects" : {
                          "$geometry" : {
                              "type" : "Point",
                              "coordinates" : [ 
                                  28.04, 
                                  -26.19
                              ]
                          }
                      }
                  }
              },
              "nReturned" : 29,
              "executionTimeMillisEstimate" : 0,
              "works" : 58,
              "advanced" : 29,
              "needTime" : 28,
              "needYield" : 0,
              "saveState" : 0,
              "restoreState" : 0,
              "isEOF" : 1,
              "invalidates" : 0,
              "docsExamined" : 46,
              "alreadyHasObj" : 0,
              "inputStage" : {
                  "stage" : "IXSCAN",
                  "nReturned" : 46,
                  "executionTimeMillisEstimate" : 0,
                  "works" : 58,
                  "advanced" : 46,
                  "needTime" : 11,
                  "needYield" : 0,
                  "saveState" : 0,
                  "restoreState" : 0,
                  "isEOF" : 1,
                  "invalidates" : 0,
                  "keyPattern" : {
                      "geometry" : "2dsphere",
                      "seconds" : 1
                  },
                  "indexName" : "Geometry_Seconds",
                  "isMultiKey" : true,
                  "multiKeyPaths" : {
                      "geometry" : [ 
                          "geometry"
                      ],
                      "seconds" : []
                  },
                  "isUnique" : false,
                  "isSparse" : false,
                  "isPartial" : false,
                  "indexVersion" : 2,
                  "direction" : "forward",
                  "indexBounds" : {
                      "geometry" : [ 
                          "[2202260217784172544, 2202260217784172544]", 
                          "[2203386117691015168, 2203386117691015168]", 
                          "[2203667592667725824, 2203667592667725824]", 
                          "[2233785415175766016, 2233785415175766016]"
                      ],
                      "seconds" : [ 
                          "[MinKey, MaxKey]"
                      ]
                  },
                  "keysExamined" : 58,
                  "seeks" : 12,
                  "dupsTested" : 46,
                  "dupsDropped" : 0,
                  "seenInvalidated" : 0
              }
          }
      },
      "serverInfo" : {
          "host" : "LAPTOP-F7A8SNP5",
          "port" : 27017,
          "version" : "3.6.3",
          "gitVersion" : "9586e557d54ef70f9ca4b43c26892cd55257e1a5"
      },
      "ok" : 1.0
  }
  </code></pre>