13

Say I have a collection of users and want to implement autocomplete on the usernames of those users. I looked at the mongodb docs and $regex seems to be one way to do this. Is there a better way? By better I mean more performant/better practice.

3 Answers 3

12

As suggested by @Thilo, you can use several ideas including prefixing.

The most important thing is to have very quick request (because you want autocomplete to feel instaneous). So you have to use query which will use properly indexes.

With regexp : use /^prefix/ (the important thing is the ^ to specify the beginning of line which is mandatory to make the query use index).

The range query is good too : { $gt : 'jhc', $lt: 'jhd' } }

More complicated but faster : you can store prefix-trees in mongo (aka tries) with entries like :

 {usrPrefix : "anna", compl : ["annaconda", "annabelle", "annather"]}
 {usrPrefix : "ann", compl : ["anne", "annaconda", "annabelle", "annather"]}

This last solution is very fast (if indexes on compl of course) but not space efficient at all. You know the trade-off you have too choose.

Sign up to request clarification or add additional context in comments.

3 Comments

Excellent answer. Hadn't though of completion with tries. Personally I've never had an 'instantaneous' feeling with using Regexes in Mongo. This should do the trick to make it much faster!
Indeed, regexp in mongo are not really well implemented. However, when you want something fluid you do not think to query database in live, latency is simply too high. A proper way to implement auto-completion would be to load asynchronously some usual completions and complete as time (and user input) goes on.
I tried both Regex and range query, and I got a better result with Regex and index
7

We do it using regex and it's fast as long as you have an index and you use /^value/

Be aware you can't use the case insensitive option with an index, so you may want to store a lower case version of your string as another field in your document and use that for the autocomplete.

I've done tests with 3 million+ documents and it still appears instantaneous.

1 Comment

I tried with IgnoreCase on .net driver and it seems it works fine.
3

If you are looking for prefixes, you could use a range query (not sure about the exact syntax):

db.users.find({'username': { $gt : 'jhc', $lt: 'jhd' } } )

And you want an index on the username field.

2 Comments

Could you explain how the range query works in this example? for example if I have "cats" in my collection how would "ca" return the correct term.
You could search for everything between "ca" and "cb". ca < cats < cb

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.