ram
ram

Reputation: 690

Mongodb query combining $text search with $regex

My dilemma is that mongodb $text searches must match an exact word: e.g. if trying to match a post with 'testing123' a search for 'test' will not match, but using $regex will match. Now, I want to make use of indexes too, but I also want partial matches.

My thinking is that I could combine them with an $or operator, but it is not working. Is this possible? Each query alone inside the $or work, but when combining them I get no matches.

If this is not possible, I have found a pretty good solution, here , but I would like the combined $or to work if possible, but any other suggestions are welcome.

const posts = await Post.find({
  name: { $regex: 'foo', $options: 'i' },
  $or: [
    { $text: { $search: text, $caseSensitive: false } },
    { text: { $regex: text, $options: 'i' } },
  ],
});

Upvotes: 0

Views: 1314

Answers (1)

D. SM
D. SM

Reputation: 14530

One way of doing this is to downcase the text into another field, then use $regex search on that field.


You have text that you want to search for any substring case insensitively:

MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.insert({foo:'hello world TESTING123'})
WriteResult({ "nInserted" : 1 })

Step 1: add another field which stores the text in lower case.

MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.insert({foo:'hello world TESTING123',foo_lower:'hello world testing123'})

Step 2: add index.

MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.createIndex({foo_lower:1})
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 2,
    "numIndexesAfter" : 3,
    "commitQuorum" : "votingMembers",
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1597711723, 7),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1597711723, 7)
}

Step 3: downcase the query to "testing123"

Step 4: use $regex.

MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.find({foo_lower:{$regex:'testing123'}})
{ "_id" : ObjectId("5f3b2498f885e53d90f30979"), "foo" : "hello world TESTING123", "foo_lower" : "hello world testing123" }
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.find({foo_lower:{$regex:'testing123'}}).explain()
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.foo",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "foo_lower" : {
                "$regex" : "testing123"
            }
        },
        "queryHash" : "0D14CC56",
        "planCacheKey" : "1974A2D4",
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "filter" : {
                    "foo_lower" : {
                        "$regex" : "testing123"
                    }
                },
                "keyPattern" : {
                    "foo_lower" : 1
                },
                "indexName" : "foo_lower_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "foo_lower" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "foo_lower" : [
                        "[\"\", {})",
                        "[/testing123/, /testing123/]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "serverInfo" : {
        "host" : "serene",
        "port" : 14420,
        "version" : "4.4.0",
        "gitVersion" : "563487e100c4215e2dce98d0af2a6a5a2d67c5cf"
    },
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1597711761, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1597711761, 1)
}

Upvotes: 1

Related Questions