Merge two ActiveRecord records into one - ruby-on-rails

So, I'm in Rails 4 and merge method will not work and cannot get this to work out.
The case is I have two different records, for instance:
Model.find_by(:id => 1) && Model.find_by(:id => 2)
And I need to merge 2 into one. Than includes all the associations. This model has at least 20 associations and integrity of the data is pretty much valuable.
Desired result would be, in the case of:
record_1 = protein: 'chicken'...
record_2 = protein: 'pig'...
result_of_merge = protein: 'chicken, pig'...
Not too many clues on the subject. Anyone has faced something similar?

Related

Rails left join with conditions

I have users, problems, and attempts which is a join table between users and problems. I'm looking to show an index of all the problems along with the current user's most recent attempt for each, if they have one.
I've tried four things to get a left join with conditions and none of them have worked.
The naive approach is something like...
#problems = Problem.enabled
#problems.each do { |prob|
prob.last_attempt = prob.attempts
.where(user_id: current_user.id)
.last
end
This gets all the problems and the attempts I want but is N+1 queries. So...
#problems = Problem.enabled
.includes(:attempts)
This does the left join (or the equivalent two queries) getting all the problems but also all the attempts, not just those for the current user. So...
#problems = Problem.enabled
.includes(:attempts)
.where(attempts: {user_id: current_user.id})
This gets only those problems that the current user has already attempted.
So...
//problem.rb
has_many :user_attempts,
-> (user) { where(user_id: user.id) },
class_name: 'Attempt'
//problem_controller.index
#problems = Problem.enabled
.includes(:user_attempts, current_user)
And this gives an error message from rails saying joins with instance
arguments are not supported.
So I'm stuck. What is the best way to do this? Is Arel the right tool? Can I skip active record and just get back a JSON blob? Am I just being dumb?
This question is quite similar to this one but I'd need a argument to the joined scope which isn't supported. And I'm hoping rails added something in last couple years.
Thanks so much for your help.
The way I solved this was to use raw sql. It's ugly and a security risk but I didn't find better.
results = Problem.connection.exec_query(%(
SELECT *
FROM problems
LEFT JOIN (
SELECT *
//etc.
)
))
And then manipulating the results array in memory.

neo4j cypher - is 'created_at' on relationships magic?

I have a query that looks something like this:
MATCH(u:USER) where u.id in {a_list}
MATCH(e:WHALE) # this is a singleton
CREATE (e)-[h:HARPOON]->(u)
SET h.a = 1, h.b = 2, h.created_at = {created_at}
So u can be multiple users. e is a singleton. Basically we're going to relate the whale to every user.
My problem is that it works fine... if I remove created_at from the query. If I leave it in, not all users are related to the whale. In fact, if I simply rename the parameter name from created_at to xcreated_at it works fine.
Is there something special about created_at?
created_at isn't special, as far as I know. It might depend on your driver, though. In the ruby neo4j gem, for instance, created_at is special, but not for any raw Cypher queries that you run.
Additionally, are you removing the parameter both from the query and from your parameter hash/map? That might cause some weirdness.
Lastly, this was probably dropped because you were making an example, but just created_at = {created_at} won't do anything. You need to specify the object which the property is being set on. I assume it's the relationship in this case so you'd want: h.created_at = {created_at}

Subtracting two queries from each other in rails

I'm new to rails, and even ruby, so I'm having some trouble figuring this out.
I want to find the difference between two queries. This particular query should return a single record, since I've set it up such that Recipe is missing one of the IDs from recipes.
Current code:
q = Recipe.all - Recipe.where(recipe_id: recipes)
Where recipes is an array of IDs.
From my limited understanding of the language, this would work if both Recipe.all and Recipe.where both returned arrays.
I've spent some time searching the web, with nothing coming up to aid me.
Other things I've tried:
q = [Recipe.all] - [Recipe.where(recipe_id: recipes)]
q = Recipe.where.not(recipe_id: recipes) # Wouldn't work because the array is the one with the extra id
Though neither proved helpful.
Try this:
q = Recipe.where('recipe_id NOT IN (?)', recipes)
Turns out I was asking the wrong question.
Since the array of IDs is the one with extra elements, not the database query, I should have been comparing the difference of it to the query.
My answer is as follows:
q = recipes - Recipe.where(recipe_id: recipes).ids
Which returns the missing IDs.
If you are using Rails 4, you can use the not query method
q = Recipe.where.not(id: recipes)
this will generator following query:
SELECT "recipes".* FROM "recipes" WHERE ("recipes"."id" NOT IN (12, 8, 11, 5, 6, 7))

How to make Active Record join return unique objects?

I have a simple query need: Find a list of users who made an order since Jan 1, 2013.
In SQL, it's a very simple query.
But I'm using Rails and Active Record.
So I wrote: User.joins(:orders).where("orders.created_at >= '2013-01-01 00:00:00'")
In our database, we have 100 orders made since 01/01/2013 by 75 users. (Some users made more than one order apparently.)
However, the expression above returns 100 users. (There must be duplicates.)
I tried User.joins(:orders).where("orders.created_at >= '2013-01-01 00:00:00'").uniq
That doesn't work either.
How can I get the 75 users who've made an order since 01/01/2013?
#dbjohn has the right idea, but I assume you want to avoid creating extra objects. Here's a slight variant on his solution, letting the database do the uniq-ing for you:
date = "2013-01-01 00:00:00"
User.joins(:orders).where("orders.created_at >= ?", date).distinct
Note that you can rearrange the order of methods to fit whatever you think is most semantic, and ActiveRecord will write the same SQL for you.
User.joins(:orders).
where("orders.created_at >= '2013-01-01 00:00:00'").
group('users.id')
group method will chain to the query and give you a list of unique records.
You can write nested query like this:
User.where(id: User.joins(:orders).where("orders.created_at >= '2013-01-01 00:00:00'").ids)
Rails has added uniq since version 3.2.1
so now you can use uniq
http://apidock.com/rails/ActiveRecord/QueryMethods/uniq

rails: get items with associated with multiple given tags

noob question here. I have a has_many :through relationship between items and tags. Finding all items associated with a given tag is simple enough:
things=Tag.find(1).items
But what if I want to find all items associated with more than one given tag? I was thinking something like:
things=Tag.find(1).items
Tag.find(2).things # wrong, but you get the idea
You can use the array union operator.
things = Tag.find(1).items | Tag.find(2).items
That will create an object for every item for both tags, which could be way too much depending on what you're trying to do. If you want something a little more scalable, you can do the lookup on the join table.
things = ItemTags.find_by_sql("
SELECT item_id, COUNT(tag_id) AS tag_count
FROM item_tags
WHERE tag_id IN (1, 2)
GROUP_BY item_id
HAVING tag_count = 2;
").map(&:item)
Just wrote that in browser, so it could be completely wrong. Also, there is probably a way to do that with activerecord finders that would be nicer that a find_by_sql.
things = Things.where(:tag => [1, 2])
things = Things.where('tag in :tags', [1, 2])
etc...

Resources