z.a.
z.a.

Reputation: 2777

Prioritize mongo search

I have a search function as below:

instance.dbInstance.collection('cars', function(error, collection) {
    collection.find({
    $and: [{

        $or: [{
            name: eval('\/' + carName + '\/i')
        }, {
            mid: eval('\/' + carName + '\/i')
        }, {
            last: eval('\/' + carName + '\/i')
        }, {
            ia: eval('\/' + carName + '\/i')
        }, {
            ea: eval('\/' + carName + '\/i')
        }, {
            carno: eval('\/' + carName + '\/i')
        }, {
            cartag: eval('\/' + carName + '\/i')
        }]
    }]
  })
})

Problem I am having is that I am not able to prioritize the results between these different attributes I give. They come all mixed in together. Is there anyway to specify what comes after another?

Upvotes: 0

Views: 342

Answers (1)

Blakes Seven
Blakes Seven

Reputation: 50406

Well the syntax you came up with for this is certainly "unique", but it essentially breaks down to doing a very basic regular expression search to see of your search term exists somewhere in one of those fields.

The use of of $regex where the string being searched for is not "anchored" to the start of the string is very inefficient, and generally needs to scan the entire table or at best the entire index where available in order to determine matches.

You need "text search", which not only makes a very simple query and is more efficient, but it allows you to assign "weights" to fields that influence the relevance score.

So set up an index as follows, with all the field names set at type "text" and then all the required "weights" for each field:

db.collection.createIndex(
    {
        "name": "text",
        "mid": "text",
        "last": "text",
        "ia": "text",
        "ea": "text",
        "carno": "text",
        "cartag": "text"
    },
    {
        "weights": {
            "name": 30,
            "mid":  25,
            "last": 20,
            "ia": 15,
            "ea": 10,
            "carno": 5,
            "cartag": 2
        }
    }
)

Then you can just issue a query like this on the collection:

db.collection.find(
   {  "$text": { "$search": carName } },
   { "score": { "$meta": "textScore" } }
).sort({ "score": { "$meta": "textScore" } })

Which not only searches over all of the fields that were set in the index, it also will assign "relevance" based on which field the term was matched in.

Assign as little or as many "weights" as you require and with any values, but "higher" will add more relevance and sort the results with more relevance first.

So being able to use an index will make this more efficient, plus it's easier to code and you can get relevance assigned to where the term was matched.

Upvotes: 1

Related Questions