Why elem_match is returning 0 elements? - ruby-on-rails

I am trying to get one record result from a collection of objects, but after following the Mongoid documentation I don't know what more to try.
I have this single element:
> contacts
=> #<Contact _id: 55ace6bc6xx, device_fields: {"app_id"=>"55ace6bc65195efc8200xxxx"}, created_at: 2015-07-20 12:17:00
UTC, updated_at: 2015-07-20 12:17:00 UTC, name_first: "Kory",
name_last: "Funk", ...>
this list of matchers:
> apps = []
> apps << App.where(id: "55ace6bc65195efc8200xxxx").first.id
=> ["55ace6bc65195efc8200xxxx"]
And this code trying to get the elements that match:
> contacts.elem_match(device_fields: {:app_id.in => apps }).to_a
=> []
> contacts.elem_match(device_fields: { "app_id": "55ace6bc65195efc8200xxxx"}).to_a
=> []
Why is it returning an empty array it there is one that matches?

According to official mongodb manual
The $elemMatch operator matches documents that contain an array field
And you are trying to use it with the hash field so you basically misunderstood this selection. So there is no object that matches.
Instead of it you should do:
contacts.where(:'device_fields.app_id'.in => apps).to_a

I can not achieve to resolve this with match_elem method, so finally I decided to make it through and. I am not very happy with this solution and I still don't understand why match_elem is not returning records, but at least I have found a solution to unblock this feature.
contacts.and(:device_fields.exists => true,
:device_fields.nin => ['', nil],
:"device_fields.app_id".in => apps).to_a

You don't need elemMatch here. It's for finding object array elements by partial matches (where you don't need full object equality, but only one or several fields)
This should work for your case.
contacts.where('device_fields.app_id' => {'$in' => apps})

Related

How to correctly merge one hash into another, replacing the first key. RoR

Im currently trying to merge two hashes, I don't really have much else to go on but this is the result I need is showed in this example;
{key_1: 'i want to replace this', key_2: 'i want to keep this'}.merge({key_1: 'new text'})
=> {key_1: 'new text', key_2: 'i want to keep this'}
Currently what I've got looks like this;
#notification.attributes.merge({body: ()}).to_json Im attempting to merge an replace the first key with the body element. What I'm really missing is the argument to perform the key replacement. If anyone has any direction, advice or even answers it would be much appreciated, thanks.
In Rails #attributes returns a hash with string keys:
irb(main):001:0> note = Notification.new(title: 'All your base are belong to us', body: 'Loren Ipsum...')
irb(main):002:0> note.attributes
=> {"id"=>nil, "title"=>"All your base are belong to us", "body"=>"Loren Ipsum...", "read_at"=>nil, "created_at"=>nil, "updated_at"=>nil}
If you want to replace a key in the hash you either need to use a hash with string keys:
irb(main):003:0> note.attributes.merge("body" => "Moahahahahahaha")
=> {"id"=>nil, "title"=>"All your base are belong to us", "body"=>"Moahahahahahaha", "read_at"=>nil, "created_at"=>nil, "updated_at"=>nil}
Or you need to change the keys of the hash to symbols which can be done with Hash#symbolize_keys:
irb(main):004:0> note.attributes.symbolize_keys.merge(body: "Moahahahahahaha")
=> {:id=>nil, :title=>"All your base are belong to us", :body=>"Moahahahahahaha", :read_at=>nil, :created_at=>nil, :updated_at=>nil}
This is a pretty common source of errors for new developers as Rails abstracts away the difference between symbol and string keys in many places through the use of ActiveSupport::HashWithIndifferentAccess or hash like objects like ActionController::Parameters that have indifferent access while Ruby itself is strict about the difference.
irb(main):008:0> { "foo" => "bar" }.merge(foo: 'baz')
=> {"foo"=>"bar", :foo=>"baz"}
irb(main):009:0> { "foo" => "bar" }.with_indifferent_access.merge(foo: 'baz')
=> {"foo"=>"baz"}
If you ever need to do this with a nested hash you can use the recursive versions deep_symbolize_keys and deep_merge.
You have to add ! in your merge operation, as when you are doing operation it is not effecting your actual object but will create new object. So your example as you said I did following to change values
{key_1: 'i want to replace this', key_2: 'i want to keep this'}.merge!({key_1: 'new text'})
# result
{:key_1=>"new text", :key_2=>"i want to keep this"}
please change following from
#notification.attributes.merge({body: ()}).to_json
to
#notification.attributes.merge!({body: ()}).to_json
Please try to change the order of master hash (the master is what you want to keep)
[9] pry(main)> a = {:key_1=>"i want to replace this", :key_2=>"i want to keep this"}
=> {:key_1=>"i want to replace this", :key_2=>"i want to keep this"}
[10] pry(main)> b = {:key_1=>"new text"}
=> {:key_1=>"new text"}
[11] pry(main)> c = b.merge(a)
=> {:key_1=>"i want to replace this", :key_2=>"i want to keep this"}
[12] pry(main)> d = a.merge(b); // <===== This is what you want.
=> {:key_1=>"new text", :key_2=>"i want to keep this"}
Hope it help. Thanks

How to access an instance relation using strings in Ruby on Rails?

Supose I have a class named Classroom and I have another class named Student.
From their relationship I can access Student by using Classroom.students which returns an array of students.
Now, taking that in consideration, if I assign 'Classroom' to a variable and call it with variable.constantize it will return the Class from where I can query normally.
eg:
[1] pry(main)> variable = 'Classroom'
[2] pry(main)> variable.constantize.students
=> [#<Student id: 1, name: 'Foo Smith'>, <Student id: 2, name: 'Bar Obama'>]
All of that works for me but I have no idea on how to access students if the key name is also stored as a string. This works if I'm trying to access a Classroom field but for relations it seems to be impossible.
eg:
[1] pry(main)> variable = 'Classroom'
[2] pry(main)> class_key = 'name'
[3] pry(main)> relation_key = 'students'
[4] pry(main)> variable.constantize.first[class_key]
=> "Happy Classroom"
[5] pry(main)> variable.constantize.first[key]
=> nil
There's also another case in which the string is a method from the class.
What I wanted to do (mock code):
[1] pry(main)> variable = 'Classroom'
[2] pry(main)> method = 'last'
[3] pry(main)> variable.constantize[method]
=> <#Classroom id: 3, name: 'Dubious Classroom'>
Any of those are feasible in Ruby?
Thanks!
Use Object.public_send, like this:
'Classroom'.constantize.public_send('students').public_send('first')
Edit: original answer suggested using send instead of public_send. The difference is that the former will call even the private methods, so it's less safe to use than the latter.

How to parse JSON with ruby classes in it?

First of all I push in my database in a field a Ruby object converted to JSON.
This object is (for the example) something like this :
{ #<Project id: 16 ...> => {#<Task id: 122 ...> => {details: {:a => 180, :b => 220}, sub: {...}}}}
And so on... This is just to show you that I have a nested hash with some objects in it.
This hash is pushed into my database in JSON with the following instruction Model.create(field: my_hash.to_json, ...). The field of the DB receiving this JSON Hash is a string.
Now when I want to retreive all of this data I just do this :
JSON.parse(Model.find(id)[:field])
But this gives me the keys that were object in strings. #<Project id: 16...> becomes "#<Project:0x000...>" and it's a string.
With this It's now impossible for me to do things such as var.keys.first[:id] #which in my example should be 16 since the key has became a string and is no longer a Ruby object.
Is it possible to retrieve my data the way it was pushed ?

Why is the "where" query in rails returning a different object?

I'm testing chats between users in my app. I'm using RSpec and FactoryGirl
The test that's not passing:
it "creates a chat if one does not exist" do
bob = create(:user, username: "bob")
dan = create(:user, username: "dan")
new_chat = Chat.create(user_id: #dan.id, chatted_user_id: bob.id)
expect(Chat.where("chatted_user_id = ?", bob.id).first).to equal(new_chat)
end
The failure message says:
Failure/Error: expect(Chat.where("chatted_user_id = ?", bob.id).first).to equal(new_chat)
expected #<Chat:70120833243920> => #<Chat id: 2, user_id: 2, chatted_user_id: 3>
got #<Chat:70120833276240> => #<Chat id: 2, user_id: 2, chatted_user_id: 3>
Compared using equal?, which compares object identity,
but expected and actual are not the same object. Use
`expect(actual).to eq(expected)` if you don't care about
object identity in this example.
Why is my query returning a different object id?
equal checks object identity. The objects you are testing are two objects (instances) referencing the same record, but they are actually different objects from a Ruby virtual machine point of view.
You should use
expect(Chat.where("chatted_user_id = ?", bob.id).first).to eq(new_chat)
To better understand the problem, look at the following example
2.0.0-p353 :001 > "foo".object_id
=> 70117320944040
2.0.0-p353 :002 > "foo".object_id
=> 70117320962820
Here I'm creating two identical strings. They are identical, but not equal because they are actually two different objects.
2.0.0-p353 :008 > "foo" == "foo"
=> true
2.0.0-p353 :009 > "foo".equal? "foo"
=> false
That's the same issue affecting your test. equal checks if two objects are actually the same at the object_id level. But what you really want to know is if they are the same record.

What does :attribute => parameter actually do?

I have a hard time understanding the form :attribute => parameter
Can anyone give me some explanations for it? Is :attribute a field (variable) belonging to the class or something else? Why we can pass this pair as one parameter to methods?
If you're referring to something like this:
some_method(:foo => "bar", :baz => "abc")
then it's just shorthand which causes ruby to convert those things into a Hash. Please note that when using this form, that the hash must be the final argument to the method in order for this to work.
Based on the explanation above, this
some_method(:foo => "bar", :baz => "abc")
is ok, but this
some_method(:foo => "bar", :baz => "abc", moo)
is not.
Though you will see this commonly in Rails, it is not a Rails specific question. It is Ruby.
The answer to your question is that it is key/value pairs in a Hash, generally passed as an argument to a method.
You will see this as well when it is being assigned to a variable directly. But let me show you a sample method, and a sample usage, so that you can put them together:
def some_method(*args, name: 'Joe', amount: 42, **other_params )
puts "#{name}, #{amount}, glob of arguments = #{args.inspect}",
"other params #{other_params}"
end
some_method(:occupation => 'programmer', :phone => '123-456-7890', name: 'Jane')
This is Ruby 2.0.0 specific in the fact that you can provide for that last argument, which provides for unnamed parameters, in practice. Using the 1.9+ syntax for a Hash in the argument list, you are allowed to provide for other unnamed "parameters" which can appear after the hash argument.
Notice that if I had used the older syntax for Hash, namely the :key => 'value' syntax, I would not be allowed (at least as of this writing) to have the **other_params argument at the end of the argument list.
You could also provide the hash using the newer syntax in the calling code, though I left it as the Hash syntax when calling some_method.
The Hash still needs to be the last provided in the calling argument list, the same as indicated in the argument list for the method definition.

Resources