1

I have some IDs 214001, 214002, 215001, etc...

From a searchbar, I want autocompletion with the ID

"214" should trigger autocompletion for IDs 214001, 214002

Apparently, I can't just do a

scope :by_number, ->(number){
    where(:number => /#{number.to_i}/i)
}

with mongoid. Anyone know a working way of matching a mongoid Integer field with a regex ?

This question had some clue, but how can I do this inside Rails ?

EDIT : The context is to be able to find a project by its integer ID or its short description :

scope :by_intitule, ->(regex){ 
    where(:intitule => /#{Regexp.escape(regex)}/i)
}
# TODO : Not working !!!!
scope :by_number, ->(numero){
    where(:number => /#{number.to_i}/i)
}
scope :by_name, ->(regex){ 
    any_of([by_number(regex).selector, by_intitule(regex).selector])
}
Community
  • 1
  • 1
Cyril Duchon-Doris
  • 12,964
  • 9
  • 77
  • 164
  • This number represents a project number. It is incremented by one for every new project, therefore I needed an integer behaviour. And I want my project managers to be able to find a project "by its number" – Cyril Duchon-Doris Mar 01 '15 at 01:03
  • After reading more I think I understand what you mean. In my case, I can afford some poor performance solutions. We're only talking about ~50-100 projects per year so it should be fine. – Cyril Duchon-Doris Mar 01 '15 at 01:25

2 Answers2

2

The MongoDB solution from the linked question would be:

db.models.find({ $where: '/^124/.test(this.number)' })

Things that you hand to find map pretty much one-to-one to Mongoid:

where(:$where => "/^#{numero.to_i}/.test(this.number)")

The to_i call should make string interpolation okay for this limited case.

Keep in mind that this is a pretty horrific thing to do to your database: it can't use indexes, it will scan every single document in the collection, ...

You might be better off using a string field so that you can do normal regex matching. I'm pretty sure MongoDB will be able to use an index if you anchor your regex at the beginning too. If you really need it to be a number inside the database then you could always store it as both an Integer and a String field:

field :number,   :type => Integer
field :number_s, :type => String

and then have some hooks to keep :number_s up to date as :number changes. If you did this, your pattern matching scope would look at :number_s. Precomputing and duplicating data like this is pretty common with MongoDB so you shouldn't feel bad about it.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • Thank you, I'll leave a TODO in my code so I remember to do something clean like what you suggest. – Cyril Duchon-Doris Mar 04 '15 at 19:46
  • Should I just override the `number=` method so as to update both `:number` and `:number_s` ? Am I missing some hooks if I do only this ? (eg. is Model.update_attributes using the `field=` methods) – Cyril Duchon-Doris Mar 04 '15 at 19:58
  • `update_attributes` should call the `field=` method. I'd probably use a `before_validation` hook to set `number_s` and maybe even add a validation to make sure that `number` and `number_s` agree with each other. – mu is too short Mar 04 '15 at 20:06
0

The way to do a $where in mongoid is using Criteria#for_js

Something like this

Model.for_js("new RegExp(number).test(this.int_field)", number: 763)
Ismael Abreu
  • 16,443
  • 6
  • 61
  • 75
  • This doesn't seem to work. Running `Project.for_js("/numero/.test(this.int_field)", numero: 214).any?` should return true because I have a model with numero 214001, yet it returns `false` – Cyril Duchon-Doris Mar 01 '15 at 01:18