Sorting out data from SQL in Rails - sql

So I'm trying to get some specific data out of my database but I've searching online and can't find how to do this (probably because I'm searching for the wrong terms).
I start with getting all the participants with a specific id like this :
contributions = Participant.where(user_id: params[:id])
This will give me a json result like this :
0: {id_request: "1", user_id: "titivermeesch#gmail.com"}
1: {id_request: "2", user_id: "titivermeesch#gmail.com"}
So here I have all the requests (there is a Request class) that have that specific user_id.
Now I want to do this :
all = Request.where(id: id_request)
This obviously don't work but how would I get all those requests that have all those id's that come from the first database query?
So with the results above I should get Request 1 and 2, but how? Can anyone guide me?

How about
contributions = Participant.where(user_id: params[:id])
# Assuming the above is an active record query and id_request is a property of Participant
all = Request.where(id: contributions.map(&:id_request))
This is the equivalent of the SQL
select * from requests where id in (array_of_request_ids)

If You added associations in your model? it's very easy to retrieve the records
This should work:
Request.joins(:participants).where("participants.user_id = ?", params[:id])
Also you might want to read the following part (on joins)

Related

Identify Django queryset from SQL logs

I use Django 1.8.17 (I know it's not so young anymore).
I have logged slow requests on PostGres for more than one minute.
I have a lot of trouble finding the Queryset to which the SQL query listed in the logs belongs.
Is there an identifier that could be added to the Queryset to find the associated SQL query in the Logs or a trick to easily identify it?
Here is an exemple of common Queryset almost impossible to identify as I have several similars ones.
Queryset:
Video.objects.filter(status='online').order_by('created')
LOGs:
duration: 1056.540 ms statement: SELECT "video"."id", "video"."title",
"video"."description", "video"."duration", "video"."html_description",
"video"."niche_id", "video"."owner_id", "video"."views",
"video"."rating" FROM "video" WHERE "video"."status" = 'online'
ORDER BY "video"."created"
Desired LOGs:
duration: 1056.540 ms statement: SELECT "video"."id", "video"."title",
"video"."description", "video"."duration", "video"."html_description",
"video"."niche_id", "video"."owner_id", "video"."views",
"video"."rating" FROM "video" WHERE "video"."status" = 'online'
ORDER BY "video"."created" (ID=555)
Add middleware to log a warning when a query takes a long time:
class LongQueryLogMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
for q in connection.queries:
if float(q['time']) >= settings.LONG_QUERY_TIME_SEC:
logger.warning("Found long query (%s sec): %s", q['time'], q['sql'])
return response
I've made a small gist with all the code. Sorry for the indentation, GitHub keeps removing the indentation.
In the code above I only log the query, but you can add request information that will help you identify where the query comes from.
I don't know Django, so I may be off the mark, but there's a simple trick I heard from one of the people that runs RDS:
Add an identifier to your query as a comment.
So, include a UUID, ID, label, etc. to the query
-- as a comment
and that will flow through to the log. This is an easy way to tie Postgres log entries to specific methods/scripts, it sounds like it would need a bit of adaptation to be useful in your case. (If the idea applies at all.)

order_by() method not working in peewee

I am using a SQLite backend with a simple show - season - episode schema:
class Show(BaseModel):
name = CharField()
class Season(BaseModel):
show = ForeignKeyField(Show, related_name='seasons')
season_number = IntegerField()
class Episode(BaseModel):
season = ForeignKeyField(Season, related_name='episodes')
episode_number = IntegerField()
and I would need the following query :
seasons = (Season.select(Season, Episode)
.join(Episode)
.where(Season.show == SHOW_ID)
.order_by(Season.season_number.desc(), Episode.episode_number.desc())
.aggregate_rows())
SHOW_ID being the id of the show for which I want the list of seasons.
But when I iterate over the query with the following code :
for season in seasons:
for episode in season.episodes:
print(episode.episode_number)
... I get something which is not ordered at all, and which does not even follow the order I would get without using order_by(), i.e. the insertion order.
I activated the debug logs to see the outgoing query, and the query does contain the ORDER BY clause, and manually applying it returns the proper descending order.
I am new to peewee, and I have seen so many examples making use of a join() combines with an order_by(), but I can still not find out what I am doing wrong.
This was due to a bug in the processing of nested collections in the aggregate query result wrapper.
The github issue is: https://github.com/coleifer/peewee/issues/519
The fix has been merged here: https://github.com/coleifer/peewee/commit/ec0e87f1a480695d98bf1f0d7f2e63aed8dfc440
So, to get the fix you'll need to either clone master or wait til the next release which should be in the next week or two (2.4.7).

Easiest way to find a value in one of three columns?

I am using twilio to provide audio conference functionality in my rails app. When I call my conference number, twilio passes on a couple of values - including 'From' which contains the caller's phone number in international format.
I have a profile for every user in my system and in my controller I am querying the profile to provide a personalised welcome message. Every profile contains between 0 and 3 numbers (primary, secondary and cellphone) and I need to check the caller's ID against those three fields in all profiles.
When I use the console on my dev machine, the following code finds the correct profile:
Profile.find_by('+44000000000')
When I upload to heroku, I use following code instead:
name = Profile.find_by(params['From']) || 'there'
Which causes an error in my app:
2014-04-03T19:20:22.801284+00:00 app[web.1]: PG::DatatypeMismatch: ERROR: argument of WHERE must be type boolean, not type bigint
2014-04-03T19:20:22.801284+00:00 app[web.1]: LINE 1: SELECT "profiles".* FROM "profiles" WHERE (+4400000000) ...
Any suggestion how that could be solved?
Thanks!
Additional information:
I think my problem is that I don't know how to query either the whole profile or three columns at once. Right now the code:
name = Profile.find_by(params['From'])
is not correct (params['From'] contains a phone number) because I am not telling rails to query the columns primary phone number, secondary phone number and cellphone. Neither am I querying the whole profile which would also be an option.
So the question basically is:
How can I change this code:
Profile.find_by(params['From'])
so that it queries either all fields in all profiles or just the three columns with phone numbers which each profile contains?
Is there something like Profile.where(:primary_number).or.where(:secondary_number)or.where(:cellphone) => params['From']
?
I am not familiar with twilio and not sure if this helps but find and find_by_attribute_name accepts array of values as options:
name = Profile.find_by([params['From'], 'there'] )
suppose params['From'] was here , This should generate:
SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`attribute` IN ('here', 'there')
Or:
If you are trying to build dynamic matcher at run time , which is called Meta-programming , you can try this code:
name = eval("Profile.find_by_#{params['From']) || 'there'}(#rest of query params here) ")
Update
First of all, i think you are not using find_by correctly!! the correct syntax is:
Model.find_by(attribute_name: value)
#e.g
Profile.find_by(phone_number: '0123456')
Which will call where and retrive one record, but passing a value will generate a condition that always passes, for example:
Model.find_by('wrong_condition')
#will generate SQL like:
SELECT `models`.* FROM `models` WHERE ('wrong_condition') LIMIT 1
#which will return the first record in the model since there is no valid condition here
Why don't you try:
Profile.where('primary_number = ? OR secondary_number = ? OR cellphone = ?', params['From'], params['From'], params['From'])
You can write your query like:
Profile.where("primary_number = ? or secondary_number = ? or cellphone = ?", params['From'])
Just double check the syntax, but that should do it.

Magento Bulk update attributes

I am missing the SQL out of this to Bulk update attributes by SKU/UPC.
Running EE1.10 FYI
I have all the rest of the code working but I"m not sure the who/what/why of
actually updating our attributes, and haven't been able to find them, my logic
is
Open a CSV and grab all skus and associated attrib into a 2d array
Parse the SKU into an entity_id
Take the entity_id and the attribute and run updates until finished
Take the rest of the day of since its Friday
Here's my (almost finished) code, I would GREATLY appreciate some help.
/**
* FUNCTION: updateAttrib
*
* REQS: $db_magento
* Session resource
*
* REQS: entity_id
* Product entity value
*
* REQS: $attrib
* Attribute to alter
*
*/
See my response for working production code. Hope this helps someone in the Magento community.
While this may technically work, the code you have written is just about the last way you should do this.
In Magento, you really should be using the models provided by the code and not write database queries on your own.
In your case, if you need to update attributes for 1 or many products, there is a way for you to do that very quickly (and pretty safely).
If you look in: /app/code/core/Mage/Adminhtml/controllers/Catalog/Product/Action/AttributeController.php you will find that this controller is dedicated to updating multiple products quickly.
If you look in the saveAction() function you will find the following line of code:
Mage::getSingleton('catalog/product_action')
->updateAttributes($this->_getHelper()->getProductIds(), $attributesData, $storeId);
This code is responsible for updating all the product IDs you want, only the changed attributes for any single store at a time.
The first parameter is basically an array of Product IDs. If you only want to update a single product, just put it in an array.
The second parameter is an array that contains the attributes you want to update for the given products. For example if you wanted to update price to $10 and weight to 5, you would pass the following array:
array('price' => 10.00, 'weight' => 5)
Then finally, the third and final attribute is the store ID you want these updates to happen to. Most likely this number will either be 1 or 0.
I would play around with this function call and use this instead of writing and maintaining your own database queries.
General Update Query will be like:
UPDATE
catalog_product_entity_[backend_type] cpex
SET
cpex.value = ?
WHERE cpex.attribute_id = ?
AND cpex.entity_id = ?
In order to find the [backend_type] associated with the attribute:
SELECT
  backend_type
FROM
  eav_attribute
WHERE entity_type_id =
  (SELECT
    entity_type_id
  FROM
    eav_entity_type
  WHERE entity_type_code = 'catalog_product')
AND attribute_id = ?
You can get more info from the following blog article:
http://www.blog.magepsycho.com/magento-eav-structure-role-of-eav_attributes-backend_type-field/
Hope this helps you.

How to quick match an entry with the beginning of a long string?

I have a table articles (:Rails Model but I think the issue is more SQL related) which have a column name permalink. To instance, some of my permalinks :
title-of-article
great-article
great-article-about-obama
obama-stuff-about-him
I want to match a request like great-article-about-obama-random-stuff to great-article-about-obama. Is it possible to do it, avoiding killing performance ?
Thanks to all,
ps : We use Rails 3 and Postgresql (or Sqlite not decided yet for production)
EDIT
We can do something like this, but the main downside is we have to fetched every single permalinks from the table articles :
permalinks = ['title-of-article','great-article','great-article-about-obama','obama-stuff-about-him']
string_to_match = 'great-article-about-obama-random-stuf'
result = permalinks.inject('') do |matched,permalink|
matched = (string_to_match.include? permalink and permalink.size > matched.size) ? permalink : matched
end
result => 'great-article-about-obama'
I'll love to find a way to do it directly in SQL for obvious performance reason.
Unless using a text-search base technology (w/ postgres : http://www.postgresql.org/docs/8.3/static/textsearch-dictionaries.html + http://tenderlovemaking.com/2009/10/17/full-text-search-on-heroku/ or solr, indexTank) you can do it with :
request = "chien-qui-aboie"
article = nil
while !article do
article = Article.where("permalink like ?", request+"%").select(:id).first
request.gsub!(/-[^-]*$/) unless article
end
This will first look for chien-qui-aboie%, then chien-qui%, then chien%.
This will also match "chien_qui_mange" if there is an article "chien_qui_mange" but no one about "chien qui aboie"
That's not optimal because of the number of requests, but that's not that heavy if it's just a look up, and not the normal way of accessing a record.