void HaystackAccessMethod::searchCommand(const BSONObj& nearObj, double maxDistance,
                                             const BSONObj& search, BSONObjBuilder* result,
                                             unsigned limit) {
        Timer t;

        LOG(1) << "SEARCH near:" << nearObj << " maxDistance:" << maxDistance
               << " search: " << search << endl;
        int x, y;
        {
            BSONObjIterator i(nearObj);
            x = HaystackKeyGenerator::hashHaystackElement(i.next(), _bucketSize);
            y = HaystackKeyGenerator::hashHaystackElement(i.next(), _bucketSize);
        }
        int scale = static_cast<int>(ceil(maxDistance / _bucketSize));

        GeoHaystackSearchHopper hopper(nearObj, maxDistance, limit, _geoField);

        long long btreeMatches = 0;

        for (int a = -scale; a <= scale && !hopper.limitReached(); ++a) {
            for (int b = -scale; b <= scale && !hopper.limitReached(); ++b) {
                BSONObjBuilder bb;
                bb.append("", HaystackKeyGenerator::makeHaystackString(x + a, y + b));

                for (unsigned i = 0; i < _otherFields.size(); i++) {
                    // See if the non-geo field we're indexing on is in the provided search term.
                    BSONElement e = search.getFieldDotted(_otherFields[i]);
                    if (e.eoo())
                        bb.appendNull("");
                    else
                        bb.appendAs(e, "");
                }

                BSONObj key = bb.obj();

                unordered_set<DiskLoc, DiskLoc::Hasher> thisPass;


                scoped_ptr<Runner> runner(InternalPlanner::indexScan(_btreeState->collection(),
                                                                     _descriptor, key, key, true));
                Runner::RunnerState state;
                DiskLoc loc;
                while (Runner::RUNNER_ADVANCED == (state = runner->getNext(NULL, &loc))) {
                    if (hopper.limitReached()) { break; }
                    pair<unordered_set<DiskLoc, DiskLoc::Hasher>::iterator, bool> p
                        = thisPass.insert(loc);
                    // If a new element was inserted (haven't seen the DiskLoc before), p.second
                    // is true.
                    if (p.second) {
                        hopper.consider(loc);
                        btreeMatches++;
                    }
                }
            }
        }

        BSONArrayBuilder arr(result->subarrayStart("results"));
        int num = hopper.appendResultsTo(&arr);
        arr.done();

        {
            BSONObjBuilder b(result->subobjStart("stats"));
            b.append("time", t.millis());
            b.appendNumber("btreeMatches", btreeMatches);
            b.append("n", num);
            b.done();
        }
    }
Пример #2
0
        void searchCommand(NamespaceDetails* nsd,
                            const BSONObj& n /*near*/, double maxDistance, const BSONObj& search,
                            BSONObjBuilder& result, unsigned limit) {
            Timer t;

            LOG(1) << "SEARCH near:" << n << " maxDistance:" << maxDistance
                   << " search: " << search << endl;
            int x, y;
            {
                BSONObjIterator i(n);
                x = hash(i.next());
                y = hash(i.next());
            }
            int scale = static_cast<int>(ceil(maxDistance / _bucketSize));

            GeoHaystackSearchHopper hopper(n, maxDistance, limit, _geoField);

            long long btreeMatches = 0;

            // TODO(hk): Consider starting with a (or b)=0, then going to a=+-1, then a=+-2, etc.
            // Would want a HaystackKeyIterator or similar for this, but it'd be a nice
            // encapsulation allowing us to S2-ify this trivially/abstract the key details.
            for (int a = -scale; a <= scale && !hopper.limitReached(); ++a) {
                for (int b = -scale; b <= scale && !hopper.limitReached(); ++b) {
                    BSONObjBuilder bb;
                    bb.append("", makeString(x + a, y + b));

                    for (unsigned i = 0; i < _otherFields.size(); i++) {
                        // See if the non-geo field we're indexing on is in the provided search term.
                        BSONElement e = search.getFieldDotted(_otherFields[i]);
                        if (e.eoo())
                            bb.appendNull("");
                        else
                            bb.appendAs(e, "");
                    }

                    BSONObj key = bb.obj();

                    GEOQUADDEBUG("KEY: " << key);

                    // TODO(hk): this keeps a set of all DiskLoc seen in this pass so that we don't
                    // consider the element twice.  Do we want to instead store a hash of the set?
                    // Is this often big?
                    set<DiskLoc> thisPass;

                    // Lookup from key to key, inclusive.
                    scoped_ptr<BtreeCursor> cursor(BtreeCursor::make(nsd,
                                                                     *getDetails(),
                                                                     key,
                                                                     key,
                                                                     true,
                                                                     1));
                    while (cursor->ok() && !hopper.limitReached()) {
                        pair<set<DiskLoc>::iterator, bool> p = thisPass.insert(cursor->currLoc());
                        // If a new element was inserted (haven't seen the DiskLoc before), p.second
                        // is true.
                        if (p.second) {
                            hopper.consider(cursor->currLoc());
                            GEOQUADDEBUG("\t" << cursor->current());
                            btreeMatches++;
                        }
                        cursor->advance();
                    }
                }
            }

            BSONArrayBuilder arr(result.subarrayStart("results"));
            int num = hopper.appendResultsTo(&arr);
            arr.done();

            {
                BSONObjBuilder b(result.subobjStart("stats"));
                b.append("time", t.millis());
                b.appendNumber("btreeMatches", btreeMatches);
                b.append("n", num);
                b.done();
            }
        }