Beispiel #1
0
TEST(FTSQuery, Phrase2) {
    FTSQuery q;
    ASSERT(
        q.parse("doing a \"phrase-test\" for fun", "english", false, TEXT_INDEX_VERSION_2).isOK());
    ASSERT_EQUALS(1U, q.getPositivePhr().size());
    ASSERT_EQUALS("phrase-test", q.getPositivePhr()[0]);
}
 TEST( FTSMatcher, Phrase2 ) {
     FTSQuery q;
     q.parse( "foo \"table top\"", "english" );
     FTSMatcher m( q,
                   FTSSpec( FTSSpec::fixSpec( BSON( "key" << BSON( "x" << "text" ) ) ) ) );
     ASSERT( m.phraseMatch( "table top",
                            BSON( "x" << BSON_ARRAY( "table top" ) ) ) );
 }
Beispiel #3
0
TEST(FTSQuery, CaseSensitiveNegativePhrases) {
    FTSQuery q;
    ASSERT(
        q.parse("doing a -\"Phrase Test\" for fun", "english", true, TEXT_INDEX_VERSION_2).isOK());

    ASSERT_EQUALS(0U, q.getPositivePhr().size());
    ASSERT_EQUALS(1U, q.getNegatedPhr().size());
    ASSERT_EQUALS("Phrase Test", q.getNegatedPhr()[0]);
}
Beispiel #4
0
        // Regression test for SERVER-11994.
        TEST( FTSMatcher, NegWild2 ) {
            FTSQuery q;
            ASSERT_OK( q.parse( "pizza -restaurant", "english", TEXT_INDEX_VERSION_2 ) );
            FTSMatcher m( q,
                          FTSSpec( FTSSpec::fixSpec( BSON( "key" << BSON( "$**" << "text" ) ) ) ) );

            ASSERT( m.hasNegativeTerm( BSON( "x" << BSON( "y" << "pizza restaurant" ) ) ) );
            ASSERT( m.hasNegativeTerm( BSON( "x" << BSON( "y" << "PIZZA RESTAURANT" ) ) ) );
        }
        TEST( FTSMatcher, NegWild1 ) {
            FTSQuery q;
            q.parse( "foo -bar", "english" );
            FTSMatcher m( q,
                          FTSSpec( FTSSpec::fixSpec( BSON( "key" << BSON( "$**" << "text" ) ) ) ) );

            ASSERT( m.hasNegativeTerm( BSON( "x" << BSON( "y" << "bar" ) ) ) );
            ASSERT( m.hasNegativeTerm( BSON( "x" << BSON( "y" << "bar" ) ) ) );
        }
Beispiel #6
0
        // Test that the matcher parses the document with the document language, not the search
        // language.
        TEST( FTSMatcher, ParsesUsingDocLanguage ) {
            FTSQuery q;
            ASSERT_OK( q.parse( "-glad", "none", TEXT_INDEX_VERSION_2 ) );
            FTSMatcher m( q,
                          FTSSpec( FTSSpec::fixSpec( BSON( "key" << BSON( "x" << "text" ) ) ) ) );

            // Even though the search language is "none", the document {x: "gladly"} should be
            // parsed using the English stemmer, and as such should match the negated term "glad".
            ASSERT( m.hasNegativeTerm( BSON( "x" << "gladly" ) ) );
        }
Beispiel #7
0
// Test textIndexVersion:1 query with language "invalid".  No stemming will be performed,
// and no stopword list will be used.
TEST(FTSQuery, TextIndexVersion1LanguageInvalid) {
    FTSQuery q;
    ASSERT(q.parse("the running", "invalid", false, TEXT_INDEX_VERSION_1).isOK());
    ASSERT_EQUALS(2U, q.getPositiveTerms().size());
    ASSERT_EQUALS(1, std::count(q.getPositiveTerms().begin(), q.getPositiveTerms().end(), "the"));
    ASSERT_EQUALS(1,
                  std::count(q.getPositiveTerms().begin(), q.getPositiveTerms().end(), "running"));
    ASSERT_EQUALS(0U, q.getNegatedTerms().size());
    ASSERT_EQUALS(0U, q.getPositivePhr().size());
    ASSERT_EQUALS(0U, q.getNegatedPhr().size());
}
Beispiel #8
0
TEST(FTSQuery, Basic1) {
    FTSQuery q;
    ASSERT(q.parse("this is fun", "english", false, TEXT_INDEX_VERSION_2).isOK());

    ASSERT_EQUALS(false, q.getCaseSensitive());
    ASSERT_EQUALS(1U, q.getPositiveTerms().size());
    ASSERT_EQUALS("fun", *q.getPositiveTerms().begin());
    ASSERT_EQUALS(0U, q.getNegatedTerms().size());
    ASSERT_EQUALS(0U, q.getPositivePhr().size());
    ASSERT_EQUALS(0U, q.getNegatedPhr().size());
    ASSERT_TRUE(q.getTermsForBounds() == q.getPositiveTerms());
}
Beispiel #9
0
TEST(FTSQuery, Phrase1) {
    FTSQuery q;
    ASSERT(
        q.parse("doing a \"phrase test\" for fun", "english", false, TEXT_INDEX_VERSION_2).isOK());

    ASSERT_EQUALS(3U, q.getPositiveTerms().size());
    ASSERT_EQUALS(0U, q.getNegatedTerms().size());
    ASSERT_EQUALS(1U, q.getPositivePhr().size());
    ASSERT_EQUALS(0U, q.getNegatedPhr().size());
    ASSERT_TRUE(q.getTermsForBounds() == q.getPositiveTerms());

    ASSERT_EQUALS("phrase test", q.getPositivePhr()[0]);
    ASSERT_EQUALS("fun|phrase|test||||phrase test||", q.debugString());
}
        TEST( FTSMatcher, Phrase1 ) {
            FTSQuery q;
            q.parse( "foo \"table top\"", "english" );
            FTSMatcher m( q,
                          FTSSpec( FTSSpec::fixSpec( BSON( "key" << BSON( "$**" << "text" ) ) ) ) );
            
            ASSERT( m.phraseMatch( "table top", BSON( "x" << "table top" ) ) );
            ASSERT( m.phraseMatch( "table top", BSON( "x" << " asd table top asd" ) ) );
            ASSERT( !m.phraseMatch( "table top", BSON( "x" << "tablz top" ) ) );
            ASSERT( !m.phraseMatch( "table top", BSON( "x" << " asd tablz top asd" ) ) );

            ASSERT( m.phrasesMatch( BSON( "x" << "table top" ) ) );
            ASSERT( !m.phrasesMatch( BSON( "x" << "table a top" ) ) );

        }
Beispiel #11
0
TEST(FTSQuery, CaseSensitiveNegativeTerms) {
    FTSQuery q;
    ASSERT(
        q.parse("-This -is -Negatively -miserable", "english", true, TEXT_INDEX_VERSION_2).isOK());

    ASSERT_EQUALS(0U, q.getPositiveTerms().size());
    ASSERT_EQUALS(0U, q.getTermsForBounds().size());
    ASSERT_EQUALS(2U, q.getNegatedTerms().size());
    ASSERT_EQUALS(1, std::count(q.getNegatedTerms().begin(), q.getNegatedTerms().end(), "Negat"));
    ASSERT_EQUALS(1, std::count(q.getNegatedTerms().begin(), q.getNegatedTerms().end(), "miser"));
    ASSERT_EQUALS(0U, q.getPositivePhr().size());
    ASSERT_EQUALS(0U, q.getNegatedPhr().size());
}
// Test textIndexVersion:1 query with language "eng".  "eng" uses the English stemmer, and
// no stopword list.
TEST(FTSQuery, TextIndexVersion1LanguageEng) {
    FTSQuery q;
    ASSERT(q.parse("the running", "eng", TEXT_INDEX_VERSION_1).isOK());
    ASSERT_EQUALS(2U, q.getTerms().size());
    ASSERT_EQUALS(1, std::count(q.getTerms().begin(), q.getTerms().end(), "the"));
    ASSERT_EQUALS(1, std::count(q.getTerms().begin(), q.getTerms().end(), "run"));
    ASSERT_EQUALS(0U, q.getNegatedTerms().size());
    ASSERT_EQUALS(0U, q.getPhr().size());
    ASSERT_EQUALS(0U, q.getNegatedPhr().size());
}
Beispiel #13
0
TEST(FTSQuery, Neg1) {
    FTSQuery q;
    ASSERT(q.parse("this is -really fun", "english", false, TEXT_INDEX_VERSION_2).isOK());

    ASSERT_EQUALS(1U, q.getPositiveTerms().size());
    ASSERT_EQUALS("fun", *q.getPositiveTerms().begin());
    ASSERT_EQUALS(1U, q.getNegatedTerms().size());
    ASSERT_EQUALS("realli", *q.getNegatedTerms().begin());
    ASSERT_TRUE(q.getTermsForBounds() == q.getPositiveTerms());
}
Beispiel #14
0
        TEST( FTSQuery, Phrase1 ) {
            FTSQuery q;
            ASSERT( q.parse( "doing a \"phrase test\" for fun", "english" ).isOK() );

            ASSERT_EQUALS( 3U, q.getTerms().size() );
            ASSERT_EQUALS( 0U, q.getNegatedTerms().size() );
            ASSERT_EQUALS( 1U, q.getPhr().size() );
            ASSERT_EQUALS( 0U, q.getNegatedPhr().size() );

            ASSERT_EQUALS( "phrase test", q.getPhr()[0] );
            ASSERT_EQUALS( "fun|phrase|test||||phrase test||", q.debugString() );
        }
Beispiel #15
0
// Test textIndexVersion:1 query with language "english".  This invokes the standard English
// stemmer and stopword list.
TEST(FTSQuery, TextIndexVersion1LanguageEnglish) {
    FTSQuery q;
    ASSERT(q.parse("the running", "english", false, TEXT_INDEX_VERSION_1).isOK());
    ASSERT_EQUALS(1U, q.getPositiveTerms().size());
    ASSERT_EQUALS("run", *q.getPositiveTerms().begin());
    ASSERT_EQUALS(0U, q.getNegatedTerms().size());
    ASSERT_EQUALS(0U, q.getPositivePhr().size());
    ASSERT_EQUALS(0U, q.getNegatedPhr().size());
}
Beispiel #16
0
        TEST( FTSQuery, Basic1 ) {
            FTSQuery q;
            ASSERT( q.parse( "this is fun", "english" ).isOK() );

            ASSERT_EQUALS( 1U, q.getTerms().size() );
            ASSERT_EQUALS( "fun", q.getTerms()[0] );
            ASSERT_EQUALS( 0U, q.getNegatedTerms().size() );
            ASSERT_EQUALS( 0U, q.getPhr().size() );
            ASSERT_EQUALS( 0U, q.getNegatedPhr().size() );
        }
Beispiel #17
0
        TEST( FTSQuery, Neg1 ) {
            FTSQuery q;
            ASSERT( q.parse( "this is -really fun", "english" ).isOK() );

            ASSERT_EQUALS( 1U, q.getTerms().size() );
            ASSERT_EQUALS( "fun", q.getTerms()[0] );
            ASSERT_EQUALS( 1U, q.getNegatedTerms().size() );
            ASSERT_EQUALS( "realli", *q.getNegatedTerms().begin() );
        }
Beispiel #18
0
TEST(FTSQuery, Mix1) {
    FTSQuery q;
    ASSERT(
        q.parse("\"industry\" -Melbourne -Physics", "english", false, TEXT_INDEX_VERSION_2).isOK());
    ASSERT_EQUALS("industri||melbourn|physic||industry||", q.debugString());
}
Beispiel #19
0
TEST(FTSQuery, NegPhrase1) {
    FTSQuery q;
    ASSERT(
        q.parse("doing a -\"phrase test\" for fun", "english", false, TEXT_INDEX_VERSION_2).isOK());
    ASSERT_EQUALS("fun||||||phrase test", q.debugString());
}
Beispiel #20
0
    PlanStage* buildStages(const string& ns, const QuerySolutionNode* root, WorkingSet* ws) {
        if (STAGE_COLLSCAN == root->getType()) {
            const CollectionScanNode* csn = static_cast<const CollectionScanNode*>(root);
            CollectionScanParams params;
            params.ns = csn->name;
            params.tailable = csn->tailable;
            params.direction = (csn->direction == 1) ? CollectionScanParams::FORWARD
                                                     : CollectionScanParams::BACKWARD;
            return new CollectionScan(params, ws, csn->filter.get());
        }
        else if (STAGE_IXSCAN == root->getType()) {
            const IndexScanNode* ixn = static_cast<const IndexScanNode*>(root);
            //
            // XXX XXX
            // Given that this grabs data from the catalog, we must do this inside of a lock.
            // We should change this to take a (ns, index key pattern) pair so that the params
            // don't involve any on-disk data, just descriptions thereof.
            // XXX XXX
            //
            IndexScanParams params;
            NamespaceDetails* nsd = nsdetails(ns.c_str());
            if (NULL == nsd) {
                warning() << "Can't ixscan null ns " << ns << endl;
                return NULL;
            }
            int idxNo = nsd->findIndexByKeyPattern(ixn->indexKeyPattern);
            if (-1 == idxNo) {
                warning() << "Can't find idx " << ixn->indexKeyPattern.toString()
                          << "in ns " << ns << endl;
                return NULL;
            }
            params.descriptor = CatalogHack::getDescriptor(nsd, idxNo);
            params.bounds = ixn->bounds;
            params.direction = ixn->direction;
            params.limit = ixn->limit;
            return new IndexScan(params, ws, ixn->filter.get());
        }
        else if (STAGE_FETCH == root->getType()) {
            const FetchNode* fn = static_cast<const FetchNode*>(root);
            PlanStage* childStage = buildStages(ns, fn->child.get(), ws);
            if (NULL == childStage) { return NULL; }
            return new FetchStage(ws, childStage, fn->filter.get());
        }
        else if (STAGE_SORT == root->getType()) {
            const SortNode* sn = static_cast<const SortNode*>(root);
            PlanStage* childStage = buildStages(ns, sn->child.get(), ws);
            if (NULL == childStage) { return NULL; }
            SortStageParams params;
            params.pattern = sn->pattern;
            return new SortStage(params, ws, childStage);
        }
        else if (STAGE_PROJECTION == root->getType()) {
            const ProjectionNode* pn = static_cast<const ProjectionNode*>(root);
            PlanStage* childStage = buildStages(ns, pn->child.get(), ws);
            if (NULL == childStage) { return NULL; }
            return new ProjectionStage(pn->projection, ws, childStage, NULL);
        }
        else if (STAGE_LIMIT == root->getType()) {
            const LimitNode* ln = static_cast<const LimitNode*>(root);
            PlanStage* childStage = buildStages(ns, ln->child.get(), ws);
            if (NULL == childStage) { return NULL; }
            return new LimitStage(ln->limit, ws, childStage);
        }
        else if (STAGE_SKIP == root->getType()) {
            const SkipNode* sn = static_cast<const SkipNode*>(root);
            PlanStage* childStage = buildStages(ns, sn->child.get(), ws);
            if (NULL == childStage) { return NULL; }
            return new SkipStage(sn->skip, ws, childStage);
        }
        else if (STAGE_AND_HASH == root->getType()) {
            const AndHashNode* ahn = static_cast<const AndHashNode*>(root);
            auto_ptr<AndHashStage> ret(new AndHashStage(ws, ahn->filter.get()));
            for (size_t i = 0; i < ahn->children.size(); ++i) {
                PlanStage* childStage = buildStages(ns, ahn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_OR == root->getType()) {
            const OrNode * orn = static_cast<const OrNode*>(root);
            auto_ptr<OrStage> ret(new OrStage(ws, orn->dedup, orn->filter.get()));
            for (size_t i = 0; i < orn->children.size(); ++i) {
                PlanStage* childStage = buildStages(ns, orn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_AND_SORTED == root->getType()) {
            const AndSortedNode* asn = static_cast<const AndSortedNode*>(root);
            auto_ptr<AndSortedStage> ret(new AndSortedStage(ws, asn->filter.get()));
            for (size_t i = 0; i < asn->children.size(); ++i) {
                PlanStage* childStage = buildStages(ns, asn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_SORT_MERGE == root->getType()) {
            const MergeSortNode* msn = static_cast<const MergeSortNode*>(root);
            MergeSortStageParams params;
            params.dedup = msn->dedup;
            params.pattern = msn->sort;
            auto_ptr<MergeSortStage> ret(new MergeSortStage(params, ws));
            for (size_t i = 0; i < msn->children.size(); ++i) {
                PlanStage* childStage = buildStages(ns, msn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_GEO_2D == root->getType()) {
            const Geo2DNode* node = static_cast<const Geo2DNode*>(root);
            TwoDParams params;
            params.gq = node->gq;
            params.filter = node->filter.get();
            params.indexKeyPattern = node->indexKeyPattern;
            params.ns = ns;
            return new TwoD(params, ws);
        }
        else if (STAGE_GEO_NEAR_2D == root->getType()) {
            const GeoNear2DNode* node = static_cast<const GeoNear2DNode*>(root);
            TwoDNearParams params;
            params.nearQuery = node->nq;
            params.ns = ns;
            params.indexKeyPattern = node->indexKeyPattern;
            params.filter = node->filter.get();
            params.numWanted = node->numWanted;
            // XXX XXX where do we grab this from??  the near query...modify geo parser... :(
            params.uniqueDocs = false;
            // XXX XXX where do we grab this from??  the near query...modify geo parser... :(
            return new TwoDNear(params, ws);
        }
        else if (STAGE_GEO_NEAR_2DSPHERE == root->getType()) {
            const GeoNear2DSphereNode* node = static_cast<const GeoNear2DSphereNode*>(root);
            return new S2NearStage(ns, node->indexKeyPattern, node->nq, node->baseBounds,
                                   node->filter.get(), ws);
        }
        else if (STAGE_TEXT == root->getType()) {
            const TextNode* node = static_cast<const TextNode*>(root);

            NamespaceDetails* nsd = nsdetails(ns.c_str());
            if (NULL == nsd) { return NULL; }
            vector<int> idxMatches;
            nsd->findIndexByType("text", idxMatches);
            if (1 != idxMatches.size()) { return NULL; }
            IndexDescriptor* index = CatalogHack::getDescriptor(nsd, idxMatches[0]);
            auto_ptr<FTSAccessMethod> fam(new FTSAccessMethod(index));
            TextStageParams params(fam->getSpec());

            params.ns = ns;
            params.index = index;
            params.spec = fam->getSpec();
            params.limit = node->_numWanted;
            Status s = fam->getSpec().getIndexPrefix(BSONObj(), &params.indexPrefix);
            if (!s.isOK()) { return NULL; }

            string language = ("" == node->_language
                               ? fam->getSpec().defaultLanguage()
                               : node->_language);

            FTSQuery ftsq;
            Status parseStatus = ftsq.parse(node->_query, language);
            if (!parseStatus.isOK()) { return NULL; }
            params.query = ftsq;

            return new TextStage(params, ws, node->_filter.get());
        }
        else {
            stringstream ss;
            root->appendToString(&ss, 0);
            warning() << "Could not build exec tree for node " << ss.str() << endl;
            return NULL;
        }
    }
Beispiel #21
0
 TEST( FTSQuery, Phrase2 ) {
     FTSQuery q;
     ASSERT( q.parse( "doing a \"phrase-test\" for fun", "english" ).isOK() );
     ASSERT_EQUALS( 1U, q.getPhr().size() );
     ASSERT_EQUALS( "phrase-test", q.getPhr()[0] );
 }
Beispiel #22
0
TEST(FTSQuery, ParsePunctuation) {
    FTSQuery q;
    ASSERT(q.parse("hello.world", "english", false, TEXT_INDEX_VERSION_2).isOK());

    ASSERT_EQUALS(false, q.getCaseSensitive());
    ASSERT_EQUALS(2U, q.getPositiveTerms().size());
    ASSERT_EQUALS("hello", *q.getPositiveTerms().begin());
    ASSERT_EQUALS("world", *(--q.getPositiveTerms().end()));
    ASSERT_EQUALS(0U, q.getNegatedTerms().size());
    ASSERT_EQUALS(0U, q.getPositivePhr().size());
    ASSERT_EQUALS(0U, q.getNegatedPhr().size());
    ASSERT_TRUE(q.getTermsForBounds() == q.getPositiveTerms());
}
Beispiel #23
0
 TEST( FTSQuery, NegPhrase1 ) {
     FTSQuery q;
     ASSERT( q.parse( "doing a -\"phrase test\" for fun", "english" ).isOK() );
     ASSERT_EQUALS( "fun||||||phrase test", q.debugString() );
 }
Beispiel #24
0
TEST(FTSQuery, CaseSensitiveOption) {
    FTSQuery q;
    ASSERT(q.parse("this is fun", "english", true, TEXT_INDEX_VERSION_2).isOK());
    ASSERT_EQUALS(true, q.getCaseSensitive());
}
Beispiel #25
0
    PlanStage* buildStages(const QuerySolution& qsol, const QuerySolutionNode* root, WorkingSet* ws, bool use_chronos = false) {
        if (STAGE_COLLSCAN == root->getType()) {
            const CollectionScanNode* csn = static_cast<const CollectionScanNode*>(root);
            CollectionScanParams params;
            params.ns = csn->name;
            params.tailable = csn->tailable;
            params.direction = (csn->direction == 1) ? CollectionScanParams::FORWARD
                                                     : CollectionScanParams::BACKWARD;
            params.maxScan = csn->maxScan;
            return new CollectionScan(params, ws, csn->filter.get(), use_chronos);
        }
        else if (STAGE_IXSCAN == root->getType()) {
            const IndexScanNode* ixn = static_cast<const IndexScanNode*>(root);
            //
            // XXX XXX
            // Given that this grabs data from the catalog, we must do this inside of a lock.
            // We should change this to take a (ns, index key pattern) pair so that the params
            // don't involve any on-disk data, just descriptions thereof.
            // XXX XXX
            //
            Database* db = cc().database();
            Collection* collection = db ? db->getCollection(qsol.ns) : NULL;
            if (NULL == collection) {
                warning() << "Can't ixscan null ns " << qsol.ns << endl;
                return NULL;
            }
            NamespaceDetails* nsd = collection->details();
            int idxNo = nsd->findIndexByKeyPattern(ixn->indexKeyPattern);
            if (-1 == idxNo) {
                warning() << "Can't find idx " << ixn->indexKeyPattern.toString()
                          << "in ns " << qsol.ns << endl;
                return NULL;
            }
            IndexScanParams params;
            params.descriptor = collection->getIndexCatalog()->getDescriptor( idxNo );
            params.bounds = ixn->bounds;
            params.direction = ixn->direction;
            params.limit = ixn->limit;
            params.maxScan = ixn->maxScan;
            params.addKeyMetadata = ixn->addKeyMetadata;
            params.ns = qsol.ns;
            return new IndexScan(params, ws, ixn->filter.get(), use_chronos);
        }
        else if (STAGE_FETCH == root->getType()) {
            const FetchNode* fn = static_cast<const FetchNode*>(root);
            PlanStage* childStage = buildStages(qsol, fn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new FetchStage(ws, childStage, fn->filter.get());
        }
        else if (STAGE_SORT == root->getType()) {
            const SortNode* sn = static_cast<const SortNode*>(root);
            PlanStage* childStage = buildStages(qsol, sn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            SortStageParams params;
            params.pattern = sn->pattern;
            params.query = sn->query;
            params.limit = sn->limit;
            return new SortStage(params, ws, childStage);
        }
        else if (STAGE_PROJECTION == root->getType()) {
            const ProjectionNode* pn = static_cast<const ProjectionNode*>(root);
            PlanStage* childStage = buildStages(qsol, pn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new ProjectionStage(pn->projection, pn->fullExpression, ws, childStage);
        }
        else if (STAGE_LIMIT == root->getType()) {
            const LimitNode* ln = static_cast<const LimitNode*>(root);
            PlanStage* childStage = buildStages(qsol, ln->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new LimitStage(ln->limit, ws, childStage);
        }
        else if (STAGE_SKIP == root->getType()) {
            const SkipNode* sn = static_cast<const SkipNode*>(root);
            PlanStage* childStage = buildStages(qsol, sn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new SkipStage(sn->skip, ws, childStage);
        }
        else if (STAGE_AND_HASH == root->getType()) {
            const AndHashNode* ahn = static_cast<const AndHashNode*>(root);
            auto_ptr<AndHashStage> ret(new AndHashStage(ws, ahn->filter.get()));
            for (size_t i = 0; i < ahn->children.size(); ++i) {
                PlanStage* childStage = buildStages(qsol, ahn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_OR == root->getType()) {
            const OrNode * orn = static_cast<const OrNode*>(root);
            auto_ptr<OrStage> ret(new OrStage(ws, orn->dedup, orn->filter.get()));
            for (size_t i = 0; i < orn->children.size(); ++i) {
                PlanStage* childStage = buildStages(qsol, orn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_AND_SORTED == root->getType()) {
            const AndSortedNode* asn = static_cast<const AndSortedNode*>(root);
            auto_ptr<AndSortedStage> ret(new AndSortedStage(ws, asn->filter.get()));
            for (size_t i = 0; i < asn->children.size(); ++i) {
                PlanStage* childStage = buildStages(qsol, asn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_SORT_MERGE == root->getType()) {
            const MergeSortNode* msn = static_cast<const MergeSortNode*>(root);
            MergeSortStageParams params;
            params.dedup = msn->dedup;
            params.pattern = msn->sort;
            auto_ptr<MergeSortStage> ret(new MergeSortStage(params, ws));
            for (size_t i = 0; i < msn->children.size(); ++i) {
                PlanStage* childStage = buildStages(qsol, msn->children[i], ws);
                if (NULL == childStage) { return NULL; }
                ret->addChild(childStage);
            }
            return ret.release();
        }
        else if (STAGE_GEO_2D == root->getType()) {
            const Geo2DNode* node = static_cast<const Geo2DNode*>(root);
            TwoDParams params;
            params.gq = node->gq;
            params.filter = node->filter.get();
            params.indexKeyPattern = node->indexKeyPattern;
            params.ns = qsol.ns;
            return new TwoD(params, ws);
        }
        else if (STAGE_GEO_NEAR_2D == root->getType()) {
            const GeoNear2DNode* node = static_cast<const GeoNear2DNode*>(root);
            TwoDNearParams params;
            params.nearQuery = node->nq;
            params.ns = qsol.ns;
            params.indexKeyPattern = node->indexKeyPattern;
            params.filter = node->filter.get();
            params.numWanted = node->numWanted;
            params.addPointMeta = node->addPointMeta;
            params.addDistMeta = node->addDistMeta;
            return new TwoDNear(params, ws);
        }
        else if (STAGE_GEO_NEAR_2DSPHERE == root->getType()) {
            const GeoNear2DSphereNode* node = static_cast<const GeoNear2DSphereNode*>(root);
            S2NearParams params;
            params.ns = qsol.ns;
            params.indexKeyPattern = node->indexKeyPattern;
            params.nearQuery = node->nq;
            params.baseBounds = node->baseBounds;
            params.filter = node->filter.get();
            params.addPointMeta = node->addPointMeta;
            params.addDistMeta = node->addDistMeta;
            return new S2NearStage(params, ws);
        }
        else if (STAGE_TEXT == root->getType()) {
            const TextNode* node = static_cast<const TextNode*>(root);

            Database* db = cc().database();
            Collection* collection = db ? db->getCollection(qsol.ns) : NULL;
            if (NULL == collection) {
                warning() << "null collection for text?";
                return NULL;
            }
            vector<int> idxMatches;
            collection->details()->findIndexByType("text", idxMatches);
            if (1 != idxMatches.size()) {
                warning() << "more than one text idx?";
                return NULL;
            }
            IndexDescriptor* index = collection->getIndexCatalog()->getDescriptor(idxMatches[0]);
            const FTSAccessMethod* fam =
                static_cast<FTSAccessMethod*>( collection->getIndexCatalog()->getIndex( index ) );
            TextStageParams params(fam->getSpec());

            params.ns = qsol.ns;
            params.index = index;
            params.spec = fam->getSpec();
            // XXX change getIndexPrefix to not look at BSONObj
            Status s = fam->getSpec().getIndexPrefix(qsol.filterData, &params.indexPrefix);
            if (!s.isOK()) {
                warning() << "can't get text index prefix??";
                return NULL;
            }

            string language = ("" == node->_language
                               ? fam->getSpec().defaultLanguage().str()
                               : node->_language);

            FTSQuery ftsq;
            Status parseStatus = ftsq.parse(node->_query, language);
            if (!parseStatus.isOK()) {
                warning() << "cant parse fts query";
                return NULL;
            }
            params.query = ftsq;

            return new TextStage(params, ws, node->filter.get());
        }
        else if (STAGE_SHARDING_FILTER == root->getType()) {
            const ShardingFilterNode* fn = static_cast<const ShardingFilterNode*>(root);
            PlanStage* childStage = buildStages(qsol, fn->children[0], ws);
            if (NULL == childStage) { return NULL; }
            return new ShardFilterStage(shardingState.getCollectionMetadata(qsol.ns), ws, childStage);
        }
        else {
            stringstream ss;
            root->appendToString(&ss, 0);
            warning() << "Could not build exec tree for node " << ss.str() << endl;
            return NULL;
        }
    }
Beispiel #26
0
TEST(FTSQuery, CaseSensitivePositiveTerms) {
    FTSQuery q;
    ASSERT(q.parse("This is Positively fun", "english", true, TEXT_INDEX_VERSION_2).isOK());

    ASSERT_EQUALS(2U, q.getTermsForBounds().size());
    ASSERT_EQUALS(1,
                  std::count(q.getTermsForBounds().begin(), q.getTermsForBounds().end(), "posit"));
    ASSERT_EQUALS(1, std::count(q.getTermsForBounds().begin(), q.getTermsForBounds().end(), "fun"));
    ASSERT_EQUALS(2U, q.getPositiveTerms().size());
    ASSERT_EQUALS(1, std::count(q.getPositiveTerms().begin(), q.getPositiveTerms().end(), "Posit"));
    ASSERT_EQUALS(1, std::count(q.getPositiveTerms().begin(), q.getPositiveTerms().end(), "fun"));
    ASSERT_EQUALS(0U, q.getNegatedTerms().size());
    ASSERT_EQUALS(0U, q.getPositivePhr().size());
    ASSERT_EQUALS(0U, q.getNegatedPhr().size());
}
Beispiel #27
0
 FTSMatcher::FTSMatcher( const FTSQuery& query, const FTSSpec& spec )
     : _query( query ),
       _spec( spec ),
       _stemmer( query.getLanguage() ){
 }
        /*
         * Runs the command object cmdobj on the db with name dbname and puts result in result.
         * @param dbname, name of db
         * @param cmdobj, object that contains entire command
         * @param options
         * @param errmsg, reference to error message
         * @param result, reference to builder for result
         * @param fromRepl
         * @return true if successful, false otherwise
         */
        bool FTSCommand::_run(const string& dbname,
                              BSONObj& cmdObj,
                              int cmdOptions,
                              const string& ns,
                              const string& searchString,
                              string language, // "" for not-set
                              int limit,
                              BSONObj& filter,
                              BSONObj& projection,
                              string& errmsg,
                              BSONObjBuilder& result ) {

            Timer comm;

            scoped_ptr<Projection> pr;
            if ( !projection.isEmpty() ) {
                pr.reset( new Projection() );
                pr->init( projection );
            }

            // priority queue for results
            Results results;

            Database* db = cc().database();
            Collection* collection = db->getCollection( ns );

            if ( !collection ) {
                errmsg = "can't find ns";
                return false;
            }

            vector<int> idxMatches;
            collection->details()->findIndexByType( INDEX_NAME, idxMatches );
            if ( idxMatches.size() == 0 ) {
                errmsg = str::stream() << "no text index for: " << ns;
                return false;
            }
            if ( idxMatches.size() > 1 ) {
                errmsg = str::stream() << "too many text indexes for: " << ns;
                return false;
            }

            BSONObj indexPrefix;

            IndexDescriptor* descriptor = collection->getIndexCatalog()->getDescriptor(idxMatches[0]);
            auto_ptr<FTSAccessMethod> fam(new FTSAccessMethod(descriptor));
            if ( language == "" ) {
                language = fam->getSpec().defaultLanguage().str();
            }
            Status s = fam->getSpec().getIndexPrefix( filter, &indexPrefix );
            if ( !s.isOK() ) {
                errmsg = s.toString();
                return false;
            }


            FTSQuery query;
            if ( !query.parse( searchString, language ).isOK() ) {
                errmsg = "can't parse search";
                return false;
            }
            result.append( "queryDebugString", query.debugString() );
            result.append( "language", language );

            FTSSearch search(descriptor, fam->getSpec(), indexPrefix, query, filter );
            search.go( &results, limit );

            // grab underlying container inside priority queue
            vector<ScoredLocation> r( results.dangerous() );

            // sort results by score (not always in correct order, especially w.r.t. multiterm)
            sort( r.begin(), r.end() );

            // build the results bson array shown to user
            BSONArrayBuilder a( result.subarrayStart( "results" ) );

            int tempSize = 1024 * 1024; // leave a mb for other things
            long long numReturned = 0;

            for ( unsigned n = 0; n < r.size(); n++ ) {
                BSONObj obj = BSONObj::make(r[n].rec);
                BSONObj toSendBack = obj;

                if ( pr ) {
                    toSendBack = pr->transform(obj);
                }

                if ( ( tempSize + toSendBack.objsize() ) >= BSONObjMaxUserSize ) {
                    break;
                }

                BSONObjBuilder x( a.subobjStart() );
                x.append( "score" , r[n].score );
                x.append( "obj", toSendBack );

                BSONObj xobj = x.done();
                tempSize += xobj.objsize();

                numReturned++;
            }

            a.done();

            // returns some stats to the user
            BSONObjBuilder bb( result.subobjStart( "stats" ) );
            bb.appendNumber( "nscanned" , search.getKeysLookedAt() );
            bb.appendNumber( "nscannedObjects" , search.getObjLookedAt() );
            bb.appendNumber( "n" , numReturned );
            bb.appendNumber( "nfound" , r.size() );
            bb.append( "timeMicros", (int)comm.micros() );
            bb.done();

            return true;
        }