13

Is there a way to set an attribute in mongoid for case insensitive searches?

Lets say that somebody has a username: IAmGreat and I want to find the users data using their unique username without butchering it and changing it to iamgreat.

Thanks

GTDev
  • 5,488
  • 9
  • 49
  • 84

5 Answers5

50

Actually you can search case insensitive. But you have to search with an regex! Here is an example how I'm using it at http://zeit.io

User.where(email: /\A#{Regexp.escape(email)}\z/i).first

With the / you are starting and ending the regex. The i after the regex means case insensitive. \A Means the string has to start with the search string and \z means the string has to end with the search string. This is important if you are looking for an exact match.

Robert Reiz
  • 4,243
  • 2
  • 30
  • 43
6

You can even try something like:

User.where(username: /#{username}/i).first
mrudult
  • 2,480
  • 3
  • 35
  • 54
  • (1) That doesn't check case-insensitive equality (2) That (AFAIK) won't use an index to check the collection so it can be very expensive. – mu is too short Apr 13 '14 at 17:12
  • Yeah. This is 50% slower than the default query. But it does check case-insensitive equality. – mrudult Apr 13 '14 at 17:16
  • 1
    No it doesn't check equality. Your regex has no anchors. – mu is too short Apr 13 '14 at 17:18
  • Actually Mongoid **does** use indexes with regex queries, but it's limited. To take the most advantage of an index with a regex search, have the regex start at the beginning of the string, like so: `User.where(username: /^#{username}/i).first` (note the `^`) – Rob Volk Dec 22 '15 at 17:53
3

if you are using rails or mongoid you can try the ff:

@user = User.where({:username => /.*#{name}.*/i })

Emil Reña Enriquez
  • 2,929
  • 1
  • 29
  • 32
  • With this answer you might get multiple matches. That's actually not very good if you want to find a username. Assume you have 3 usernames like `bob`, `my_bob`, `bob_2`. If the input is simply `bob`, the regex above will return all 3 users because you are working with `.*`. – Robert Reiz Jan 05 '23 at 15:17
1

Why not just down a User.login.downcase (or whatever your model/attribute combination is) when making the comparison? This will leave the capitalization in the DB as-is, but downcase the field just for comparison.

jefflunt
  • 33,527
  • 7
  • 88
  • 126
  • 1
    If you store your usernames and emails downcased in the DB then is for sure the best solution. Much faster than a regex search! – Robert Reiz Jan 05 '23 at 15:20
  • @RobertReiz - sure, if that is an option for the OP. I was answering the question about how to do a comparison in cases where that may not be a choice. – jefflunt Jan 06 '23 at 16:28
0

If your application doesn't need to store user-input as case-sensitive, just convert the input to uppercase or lowercase on the way in. Example,

username = params[:username].to_s.downcase

Otherwise, if performance is an issue for you (case-insensitive regex cannot take advantage for indexes) the right way to go about it is to store a backup field for username

field :username_downcase

And then do the query:

User.where(username_downcase: params[:username].to_s.downcase)
Nizar
  • 1,429
  • 1
  • 11
  • 10