DataMapper first_or_create always set certain field? - ruby

I'm using the following with datamapper to create/get a new user from my db:
user = User.first_or_create({:id => data['id']})
This gets the user with id = data['id'] or creates it if it doesn't already exist.
I want to know how to set other attributes/fields of the returned object regardless of whether it is a new record or existing?
Is the only way to do this to then call user.update({:field => value ...}) or is there a better way to achieve this?

Well, you could write it as one line:
(User.first_or_create(:id => data['id'])).update(:field => value)
with hashes for the parameters if you wish (or if you need to specify more than one); however, it's worth noting that this will only work if the model as specified by the first_or_create is valid. If :name were a required field, for instance, then this wouldn't work:
(User.first_or_create({:id => data['id'], :name => "Morse"})).update(:name => "Lewis")
as the creation in the first part would fail.
You could get around this by specifying the parameters needed for a new record with something like
(User.first_or_create({:id => data['id'], :name => "Morse"}, {:name => "Lewis"})).update(:name => "Lewis")
but this is unusually painful, and is difficult to read.
Also note that using first_or_create with an :id will attempt to create a model with that specific :id, if such a record doesn't exist. This might not be what you want.
Alternatively, you can use first_or_new. You can't call update on an object created using this, however, as the record won't exist (although I believe this might have worked in previous versions of DataMapper).

Just for anyone coming across this answer, User.first_or_create({:id => data['id']}) does NOT "get the user with id = data['id'] or creates it if it doesn't already exist." It actually gets the first record in the table and changes its id t0 data['id'].
To actually get the first record with that id, or create it if it doesn't exist, you need to use a where clause:
User.where(id: data['id]).first_or_create

Related

Optimizing Postgres queries using Sequel gem

I have a Ruby script using the Sequel gem to access my Postgres database.
At the moment, here are my calls:
#db[:items].insert_conflict.insert(:sku => sku, :name => itemName)
dbItem = Item.where(:sku => sku).first
dbPrice = Price.create(:price => foundPrice, :quantity => quantity, :status => status)
dbItem.add_price(dbPrice)
store.add_price(dbPrice)
Unfortunately, insert_conflict.insert returns either the key if it is inserted, and nil if it exists.. otherwise I'd be able to use the one call (to get the actual object that was inserted or already existed, see line 2). Is there any other way to reduce this to one call?
As for the last three calls, I believe since I'm adding relationships between three different schemas, there's no way to reduce this. But I'm including it just in case.

What is the difference between activerecord's attribute and read_attribute?

I'm able to run both and it returns the same vale:
user = User.new(name:'John')
user.attributes['first_name']
=> 'John'
user.read_attribute('first_name')
=> 'John'
Is one more performant than the other? Are there cases where I would use one over the other?
Thanks!
attributes returns a hash of all attributes for the user and ['first_name'] just accesses the specified parameter of the hash, whereas read_attribute just returns the single parameter asked for. You don't really need either of those methods to access the name as this can be done which makes the code a lot cleaner:
user = User.new(name:'John')
user.name
=> 'John'

How to assign values for update_attributes in rails?

I have a noob question about assigning values using update_attributes.
In the exam controller, a new exam record is saved and then an patient record is retrieved which matches some of the new exam fields. This part works fine.
#exam.save
#patient = Patient.joins(:charts).where(:dob => #exam.patient_dob).where(:charts => { :provider_id => #exam.provider_id, :patient_mrn => #exam.patient_mrn })
Then i try to update the new #exam record with a value from the #patient record using the following which crashes and burns......
#exam.update_attributes(:patient_id, #patient.id)
How have i gone so far astray?
You're updating a single value there, not multiple values, so update_attribute would be more appropriate. update_attributes takes a hash of values to update.
See http://apidock.com/rails/ActiveRecord/Base/update_attribute and http://apidock.com/rails/ActiveRecord/Persistence/update_attributes

ActiveRecord: Find through multiple instances

Say I have the following in my controller:
#category1
#category2
and I want to find all stores associated with those two categories...
#stores = #category1.stores + #category2.stores
this does work, but unfortunately returns an unaltered Array, rather than a AR::Base Array, and as such, I can't do things like pagination, scope, etc...
It seems to me like there's a built-in way of finding through multiple instance association... isn't there?
##stores = #category1.stores + #category2.stores
#if you want to call API methods you can just add conditions with the category id
#stores = Store.find(:all, :conditions => ['category_id=?', a || b])
With ActiveRecord, whenever you're finding a set of unique model objects, calling find on that model is usually your best bet.
Then all you need to do is constrain the join table with the categories you care about.
#stores = Store.all(:joins => :categories,
:conditions => ['category_stores.category_id in (?)', [#category1.id, #category2.id]])

Retrieving array of ids in Mongoid

how do you retrieve an array of IDs in Mongoid?
arr=["id1","id2"]
User.where(:id=>arr)
You can do this easily if you are retrieving another attribute
User.where(:nickname.in=>["kk","ll"])
But I am wondering how to do this in mongoid -> this should be a very simple and common operation
Remember that the ID is stored as :_id and not :id . There is an id helper method, but when you do queries, you should use :_id:
User.where(:_id.in => arr)
Often I find it useful to get a list of ids to do complex queries, so I do something like:
user_ids = User.only(:_id).where(:foo => :bar).distinct(:_id)
Post.where(:user_id.in => user_ids)
Or simply:
arr = ['id1', 'id2', 'id3']
User.find(arr)
The above method suggested by browsersenior doesn't seem to work anymore, at least for me. What I do is:
User.criteria.id(arr)
user_ids = User.only(:_id).where(:foo => :bar).map(&:_id)
Post.where(:user_id.in => user_ids)
The solution above works fine when amount of users is small. But it will require a lot of memory while there are thousands of users.
User.only(:_id).where(:foo => :bar).map(&:_id)
will create a list of User objects with nil in each field except id.
The solution (for mongoid 2.5):
User.collection.master.where(:foo => :bar).to_a.map {|o| o['_id']}

Resources