Rails mongoid regex on an Integer field - ruby

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])
}

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.

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)

Related

Access value from a Netsuite hash, Enumerator

Hi I am trying to extract a value from a Netsuite hash inside custom fields, and some others, which typically look like this - `
"custbody_delivery_ticket_number"=>
{
"script_id"=>"custbody_delivery_ticket_number",
"internal_id"=>"2701",
"type"=>"platformCore:DateCustomFieldRef",
"attributes"=> {
"value"=>"123abc"
}
}` and want the value of it inside of attributes.
Have tried many different ways, but one in particular -
delivery_ticket_number: "#{netsuite_sales_orders.custom_field_list.custom_fields.select['custbody_nef_meter_ticket_number']['attributes']['value']}",
throws error for class Enumerator, NoMethodError: undefined method `[]' for #Enumerator:0x00005589ec778730 which indicates may be getting close, but doing something wrong.
If anyone has any idea how to get values from these kind of hashes?
(Am told by the system admin that it is the correct custbody identifier)
Many Thanks
Eventually fixed this, grabbing Netsuite custom fields with a select of script_id by name,and map as below:
delivery_date:netsuite_sales_order.custom_fields_list.custom_fields.select { |field| field.script_id == 'custbody_delivery_date' }.map { |field| field.value }.first
First selecting the script_id by unique name, then mapping to the value. Was able to get any custom field like this, preferable as they can move and might not have the same index if use an index to grab them, fetching an incorrect value. This way ensures getting the correct data even if the item is moved up or down in the custom fields list.
Thanks for everyones help!

How to use regex to match for number and string? [duplicate]

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])
}
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.
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)

How to query a Mongoid Regexp Field?

I want to create a regex field in my Mongoid document so that I can have a behavior something like this:
MagicalDoc.create(myregex: /abc\d+xyz/)
MagicalDoc.where(myregex: 'abc123xyz')
I'm not sure if this is possible and what kind of affect it would have. How can I achieve this sort of functionality?
Update: I've learned from the documentation that Mongoid supports Regexp fields but it does not provide an example of how to query for them.
class MagicalDoc
include Mongoid::Document
field :myregex, type: Regexp
end
I would also accept a pure MongoDB answer. I can find a way to convert it to Mongoid syntax.
Update: Thanks to SuperAce99 for helping find this solution. Pass a string to a Mongoid where function and it will create a javascript function:
search_string = 'abc123xyz'
MagicalDoc.where(%Q{ return this.myregex.test("#{search_string}") })
The %Q is a Ruby method that helps to escape quotes.
regexp is not a valid BSON type, so you'll have to figure out how Mongoid represents it to devise a proper query.
Query String using Regex
If you want to send MongoDB a regular expression and return documents MongoDB provides the $regex query operator, which allows you to return documents where a string matches your regular expression.
Query Regex using String
If you want to sent Mongo a string and return all documents that have a regular expression that matches the provided string, you'll probably need the $where operator. This allows you to run a Javascript command on each document:
db.myCollection.find( { $where: function() { return (this.credits == this.debits) } } )
You can define a function which returns True when the provided string matches the Regex stored in the document. Obviously this can't use an Index because it has to execute code for every document in the collection. These queries will be very slow.

Can I avoid transposing an array in Ruby on Rails?

I have a Rails app that has a COUNTRIES list with full country names and abbreviations created inside the Company model. The array for the COUNTRIES list is used for a select tag on the input form to store abbreviations in the DB. See below. VALID_COUNTRIES is used for validations of abbreviations in the DB. FULL_COUNTRIES is used to display the full country name from the abbreviation.
class Company < ActiveRecord::Base
COUNTRIES = [["Afghanistan","AF"],["Aland Islands","AX"],["Albania","AL"],...]
COUNTRIES_TRANSFORM = COUNTRIES.transpose
VALID_COUNTRIES = COUNTRIES_TRANSPOSE[1]
FULL_COUNTRIES = COUNTRIES_TRANSPOSE[0]
validates :country, inclusion: { in: VALID_COUNTRIES, message: "enter a valid country" }
...
end
On the form:
<%= select_tag(:country, options_for_select(Company::COUNTRIES, 'US')) %>
And to convert back the the full country name:
full_country = FULL_COUNTRIES[VALID_COUNTRIES.index(:country)]
This seems like an excellent application for a hash, except the key/value order is wrong. For the select I need:
COUNTRIES = {"Afghanistan" => "AF", "Aland Islands" => "AX", "Albania" => "AL",...}
While to take the abbreviation from the DB and display the full country name I need:
COUNTRIES = {"AF" => "Afghanistan", "AX" => "Aland Islands", "AL" => "Albania",...}
Which is a shame, because COUNTRIES.keys or COUNTRIES.values would give me the validation list (depending on which hash layout is used).
I'm relatively new to Ruby/Rails and am looking for the more Ruby-like way to solve the problem. Here are the questions:
Does the transpose occur only once, and if so, when is it executed?
Is there a way to specify the FULL_ and VALID_ lists that do not require the transpose?
Is there a better or reasonable alternate way to do this? For instance, VALID_COUNTRIES is COUNTRIES[x][1] and FULL_COUNTRIES is COUNTRIES[x][0], but VALID_ must work with the validation.
Is there a way to make a hash work with just one hash rather then one for the select_tag and one for converting the abbreviations in the DB back to full names for display?
1) Does the transpose occur only once, and if so, when is it executed?
Yes at compile time because you are assigning to constants if you want it to be evaluated every time use a lambda
FULL_COUNTRIES = lambda { COUNTRIES_TRANSPOSE[0] }
2) Is there a way to specify the FULL_ and VALID_ lists that do not require the transpose?
Yes use a map or collect (they are the same thing)
VALID_COUNTRIES = COUNTRIES.map &:first
FULL_COUNTRIES = COUNTRIES.map &:last
3) Is there a better or reasonable alternate way to do this? For instance, VALID_COUNTRIES is COUNTRIES[x][1] and FULL_COUNTRIES is COUNTRIES[x][0], but VALID_ must work with the validation.
See Above
4) Is there a way to make the hash work?
Yes I am not sure why a hash isn't working as the rails docs say options_for_select will use hash.to_a.map &:first for the options text and hash.to_a.map &:last for the options value so the first hash you give should be working if you can clarify why it is not I can help you more.

How do you stop a MongoDB search from being applied recursively to the key-value tree?

Imagine I have this object (written with Ruby literals) stored in a MongoDB:
{"tags" => ["foo", "bar"],
"jobs" => [{"title" => "Chief Donkey Wrangler", "tags" => ["donkeys"]}] }
Now, I want to search for objects based on the tags on the first level of data, not the second. I can write a query like this (using the Ruby MongoDB library):
things.find("tags" => {"$exists" => "foo"})
This will obviously match the first example, but it will also match an example like this:
{"tags" => ["baz", "bar"],
"jobs" => [{"title" => "Trainee Donkey Wrangler", "tags" => ["donkeys", "foo"]}] }
How do I ensure that I am searching only the top-level of keys? I'm interested in knowing the answer in both JavaScript, Ruby and in a language-agnostic way, as I'd like to use MongoDB as a cross-language store.
Obviously, I could pass a map-reduce function to the datastore to pick out the stuff I'm trying to get, but I'm interested to see if it is supported at a higher level (and to reduce the amount of time I spend writing JavaScript map-reduce functions!)
Actually, the query you specify won't match your second example. To match the second example, you'd do:
things.find({"jobs.tags" => "foo"})
There's no recursive application of the query selector.
You're not using $exists properly. $exists does not allow you to search for a match of a field, it just checks for the existence of such a field. I'm guessing that the Ruby MongoDB library is treating your request for 'foo' as equivalent to true, b/c $exists only accepts true/false as an argument
As #kb points out, you want to use the dot notation to reach into the objects.

Resources