I want to search records on a Table based on an array of foreign key ids and trigger error if one of the ids doesn't exist, e.g.
pry(main)> Person.find([1, 2, 2002])
ActiveRecord::RecordNotFound: Couldn't find all Person
with 'id': (1, 2, 2002) [WHERE "persons"."deleted_at" IS
NULL] (found 2 results, but was looking for 3).
The problem is, the array of ids is not id, but parent_id which is a foreign key in Person. I want something like this, but it has deprecated:
Person.find(:all, :conditions => { :friends => ["Bob", "Steve", "Fred"] }
If I do like this,
Person.where(friends: ["Bob", "Steve", "Fred"])
it will just return 2 results instead of triggering error if Fred isn't found.
Thanks
You can use find_by method for column specific search.
Person.find_by(friends: ["Bob", "Steve", "Fred"])
Or
Person.find_by_friends(["Bob", "Steve", "Fred"])
Related
Let's say I have a ActiveRecord_AssociationRelation named joined obtained from joining 2 tables users and posts.
I would like to select the ids from both the user and the post and place them turn them to a hash by calling as_json.
Basically I would like to do something like this:
joined.select('users.id','posts.id')
# joined.select('posts.id','users.id')
joined.as_json
The code above returns only the last id argument mentioned in the select method( the post id in the first case and the user id in the commented case).
I would like to know if it's possible to get both ids or if Rails's Convention over Configuration principles restrict us to get both in this way( so that we might return the :user_id column instead for example)?
Just use:
joined.pluck('users.id','posts.id')
it will return something like that:
=> [[1, 97], [2, 97], [3, 97]]
Could do something like this:
joined.to_a.map{ |j| { user_id: j['users.id'], post_id: j['posts.id']} }
What I wrong with me or this query?
I have a Shop model with an opening days column, which is an array of integers (something you can do with Postgres):
days= [1,1,1,1,0,0,0]
When I query:
shops = Shop.where('days[0] = 1')
I get an empty ActiveRecord Relation.
=> #<ActiveRecord::Relation []>
When I take a shop with this kind of array…
shop = Shop.first
=> #<Shop id: 215, days: [1, 1, 1, 1, 0, 0, 0],…
If I do
shop.days[0]
I get
=> 1
I really don't get it.
By default PostgreSQL uses a one-based numbering convention for arrays, that is, an array of n elements starts with array[1] and ends with array[n].
— Source
It's just your example. Your index is out of bounds, so it doesn't match any records, days[0] is NULL. Everywhere. Fire up rails db and figure:
SELECT * FROM shops WHERE days[0] IS NULL;
But what's with that "by default"? Is it possible to define array bounds on schema level so this never becomes an issue?
Well... it's Rails' fault, I'm afraid. If it's even considered an "issue" at all. In order for an array to be zero-indexed it should be saved as such in SQL. I tried that in SQL, it works:
INSERT INTO shops
(days, created_at, updated_at)
values('[0:2]={1, 1, 0}', current_timestamp, current_timestamp);
Unfortunately, Rails loses bounds for some reason:
Shop.create(days: '[0:3]={6, 7, 8, 9}')
> INSERT ... [["days", "{6,7,8,9}"], ...]
In my Rails project with mongodb, I retrieve user ids from twitter which I want to store in my User model. The plan is to upsert the collection of Users with the retrieved user ids I have in an array, and set every new created document _id to the corresponding user id from the array.
So when I do something like this:
Tweep.collection.find( _id: 1234567 ).modify( { "$set" => {a: true}, "$unset" => {c: ""} }, {upsert: true})enter code here
The result is like expected: <Tweep _id: 1234567, a(active): true, c(candidate_value): nil>
Now I want to do the same, but only passing an array of ids to upsert my collection on Users:
Tweep.collection.find(_id: {"$in" =>[123124,223553,6343643,23423]}, c: { "$exists" => true }).modify( { "$set" => {p: true}, "$inc" => {c: 1} }, {upsert: true})
The result is some newly created documents, but without the desired values as _id e.g. _id: 123124:
<Tweep _id: 5244501325fed0cfd2c1a615, a(active): nil, c(candidate_value): 1>
instead of:
<Tweep _id: 123124, a(active): nil, c(candidate_value): 1>
How can I make mongodb use the user id in my array to be the id for the field _id?
Any help is highly appreciated.
That is not possible. From the documentation:
If upsert is set to true and if no document matches the query
criteria, update() inserts a single document.
Then again, you write:
The result is some newly created documents
I hope that it's really only one document. With a query that contains only _id : {$in : [...]}, it wouldn't even insert a single document on my machine (MongoDB 2.5.0). It only inserts if I add more criteria. Could you check that again?
It's hard to come up with a meaningful definition of how this should behave. Combining $in can be tricky. Let's say you have two documents:
{ _id : 1, name : "John" }
{ _id : 2, name : "Jane" }
and you call
db.update({"_id" : {$in : [1,2]}, "name" : "Max"}, { ... }, { upsert: true});
What is supposed to happen? There is no document that has _id 1 or 2 and name equals "Max", so the upsert would have to insert... well, what? Maybe two documents with the name Max? If the array were larger, we'd create a ton of 'Max's which probably wasn't our intention (the semantics would be: "update one or insert a thousand", which is odd). So let's say we only insert one. But now, which _id to choose? And, of course, there is the problem that _id is a unique key, so in the example, both would fail.
I store an array of Section ids as integers. event.sections #=> ["1","115","130"]
There is no Events has_many Sections relationship. Maybe this is a problem. I only need id's from Section and nothing else, so I have the array of integers stored as a serialized string in Postgres.
I can do something like this, which returns an array of events:
Event.upcoming.select { |m| m.sections.include? #section.id.to_s}
Is there a way to query this to get back an ActiveRecord::Relation?
edit-----
My earlier select query is not correct, because if #section.id = "1" then it will match and select events with these id's "1", "10", "21", "100"
This is the proper select statement:
Event.upcoming.select {|e| ( e.newsletters.split(",").flatten.grep /^#{#section.id.to_s}$/ ).presence }
I have users which has first_name and last_name fields and i need to do a ruby find all the users that have duplicate accounts based on first and last names. For example i want to have a find that will search through all the other users and find if any have the same name and email. I was thinking a nested loop like this
User.all.each do |user|
//maybe another loop to search through all the users and maybe if a match occurs put that user in an array
end
Is there a better way
You could go a long way toward narrowing down your search by finding out what the duplicated data is in the first place. For example, say you want to find each combination of first name and email that is used more than once.
User.find(:all, :group => [:first, :email], :having => "count(*) > 1" )
That will return an array containing one of each of the duplicated records. From that, say one of the returned users had "Fred" and "fred#example.com" then you could search for only Users having those values to find all of the affected users.
The return from that find will be something like the following. Note that the array only contains a single record from each set of duplicated users.
[#<User id: 3, first: "foo", last: "barney", email: "foo#example.com", created_at: "2010-12-30 17:14:43", updated_at: "2010-12-30 17:14:43">,
#<User id: 5, first: "foo1", last: "baasdasdr", email: "abc#example.com", created_at: "2010-12-30 17:20:49", updated_at: "2010-12-30 17:20:49">]
For example, the first element in that array shows one user with "foo" and "foo#example.com". The rest of them can be pulled out of the database as needed with a find.
> User.find(:all, :conditions => {:email => "foo#example.com", :first => "foo"})
=> [#<User id: 1, first: "foo", last: "bar", email: "foo#example.com", created_at: "2010-12-30 17:14:28", updated_at: "2010-12-30 17:14:28">,
#<User id: 3, first: "foo", last: "barney", email: "foo#example.com", created_at: "2010-12-30 17:14:43", updated_at: "2010-12-30 17:14:43">]
And it also seems like you'll want to add some better validation to your code to prevent duplicates in the future.
Edit:
If you need to use the big hammer of find_by_sql, because Rails 2.2 and earlier didn't support :having with find, the following should work and give you the same array that I described above.
User.find_by_sql("select * from users group by first,email having count(*) > 1")
After some googling, I ended up with this:
ActiveRecord::Base.connection.execute(<<-SQL).to_a
SELECT
variants.id, variants.variant_no, variants.state
FROM variants INNER JOIN (
SELECT
variant_no, state, COUNT(1) AS count
FROM variants
GROUP BY
variant_no, state HAVING COUNT(1) > 1
) tt ON
variants.variant_no = tt.variant_no
AND variants.state IS NOT DISTINCT FROM tt.state;
SQL
Note that part that says IS NOT DISTINCT FROM, this is to help deal with NULL values, which can't be compared with equals sign in postgres.
If you are going the route of #hakunin and creating a query manually, you may wish to use the following:
ActiveRecord::Base.connection.exec_quey(<<-SQL).to_a
SELECT
variants.id, variants.variant_no, variants.state
FROM variants INNER JOIN (
SELECT
variant_no, state, COUNT(1) AS count
FROM variants
GROUP BY
variant_no, state HAVING COUNT(1) > 1
) tt ON
variants.variant_no = tt.variant_no
AND variants.state IS NOT DISTINCT FROM tt.state;
SQL
The change is replacing connection.execute(<<-SQL)
with connection.exec_query(<<-SQL)
There can be a problem with memory leakage using execute
Plead read Clarify DataBaseStatements#execute to get an in depth understanding of the problem.