Facebook FQL Multi.Query: Joining Result Sets - ruby

I'm using Facebook's fql.multiquery for a number of different things and I'm getting the results back - just as the doctor ordered. But I can't seem to wrap my head around how to join the results so I can give them back to the user in the way I want.
I'm using HTTParty to call this:
checkin_query = {
"user_checkins" => "SELECT timestamp, coords, tagged_uids, page_id, post_id, checkin_id, message FROM checkin WHERE author_uid= me()",
"location_names" => "SELECT name FROM page WHERE page_id IN (SELECT page_id FROM #user_checkins)",
"friends_with" => "SELECT uid, name, pic_square FROM user WHERE uid IN (select tagged_uids FROM #user_checkins)"
}
params = {:queries => checkin_query.to_json, :access_token => user.token, :format => :json}
#checkin_info = HTTParty.get "https://api.facebook.com/method/fql.multiquery", :query => params
Which gives me what I want... but it's in 3 separate arrays/hashes (?) and for some reason, my brain isn't coming to a conclusion on how to deal with it.
So basically if I wanted to show the user "OK dude, you were at *LOCATION_NAME* with these people: *FRIENDS_WITH*
How can I take those 3 separate result sets and match everything up? Is there a way to include something from a previous query to call back and match it so I can display everything? Some check-ins have no tagged_uids, so obviously just doing it in order won't work when it comes to the friends they're with.
Google searches have shown me SQL's JOIN (which seems like it may be a solution) - but isn't available in FQL.

I have no experience with either FQL or HTTParty so I might have this completely wrong.
I tend to format the sql as follows:
checkin_query = {
"user_checkins" => "
SELECT tagged_uids, page_id
FROM checkin
WHERE author_uid = me()
",
"location_names" => "
SELECT name
FROM page
WHERE page_id IN (
SELECT page_id
FROM #user_checkins
)
",
"friends_with" => "
SELECT name
FROM user
WHERE uid IN (
SELECT tagged_uids
FROM #user_checkins
)
"
}
location_names and friends_with are probably returned as arrays of hashes:
"friends_with" => [{:name => 'John'}, {:name => 'Sue'}]
Not sure what HTTParty returns, but if it is hashes of arrays of hashes you might try:
puts "OK dude, you were at #{ #checkin_info[:location_names].map { |e| e.values } } with these people: #{ #checkin_info[:friends_with].map { |e| e.values } }
To figure out what #checkin_info returns you could try this:
puts #checkin_info.to_yaml

Related

Sequel SELECT count(id) FROM tablename WHERE username = "alpha"

I can't seem to get this query right. Sequel implementation of:
SELECT count(id) FROM users WHERE username = "alpha" AND status = "new"
Here's what I have so far:
db = Sequel.connect('postgres://me:pw#0.0.0.0:5432/dbname)
u = db[:users]
puts "Total users: #{u.count}" # this is correct
puts db.where(:username => "alpha", :status => "new").count
I've tried various direct SQL and that doesn't seem to work either. It smells like this is remedial, but the connectivity is fine, and I can replicate the exact SQL which doesn't come back the same.
You forgot to select the table. You want this:
db[:users].where(username:'alpha', status:'new').count
For SQLite3, this produces the query:
SELECT count(*) AS 'count'
FROM `users`
WHERE ((`username` = 'alpha')
AND (`status` = 'new'))
LIMIT 1
The error message you should have been getting was:
NoMethodError: undefined method `where' for #<Sequel::SQLite::Database: ...>
If you saw this error and read it, it should have let you know that calling db.where was not right.
In addition to making this work with the native Sequel DSL, you can also always run raw sql commands in Sequel.

Search table fields along with parent table using Foreign Key

I have 4 search fields in my index page of Employee table - id, due_date, employee_full_name, company_name.
Here, in search_parameters, I am checking if search parameters are present
search_parameters =
{:id => params[:id], :due_date => params[:due_date],
:employee_full_name => params[:employee_full_name]}
.select { |key,value| value.present?}
I am searching the columns of Employee table and it is working fine
#employees_search = Employee
.where(search_parameters, params[:id].to_i, params[:due_date],
params[:employee_full_name])
Here I am searching 'company_name' from Company table with company_id as the foreign key. This is also working fine.
#company_search = Employee
.where(company_id: Company
.where(company_name: params[:company_name]))
But I need to combine the above 2 queries so that user can search all 4 fields together. Somewhat like the below code.
#employees_search = Employee
.where(search_parameters, params[:id].to_i, params[:due_date],
params[:employee_full_name],
company_id: Company
.where(company_name: params[:company_name]))
the above query just gives search result of employee table and not the combined result. What is wrong with the queries?
Try this query using the active record querying using the or query by giving all the fields,
#employees_search = Employee
.where("id = ? or due_date = ? or employee_full_name = ?
or company_id in (?)", params[:id],params[:due_date],params[:employee_full_name],
Company.where('company_name = ?',params[:company_name]).pluck(:id))
Search all the fields in the Employee table using the '= ?' syntax and when coming to the company_id there may be multiple companies with the same name so use in (?) syntax, even if it works for single company.
This will give you all the employees with the values from the params and also including the employees who belong to that company.
This may solve your issue.
using #sravan answer, I arrived to following query, which worked perfectly.
search_parameters =
{:id => params[:id], :due_date => params[:due_date],
:employee_full_name => params[:employee_full_name],
:company_id => Company
.where('company_name = ?', params[:company_name])
.pluck(:id)}
.select { |key,value| value.present? }
#employees_search = Employee
.where(search_parameters, params[:id].to_i, params[:due_date],
params[:employee_full_name],:company_id => Company
.where('company_name = ?',params[:company])
.pluck(:id)
)

How to use Magentor gem

I am trying to use the Magentor gem. The documentation is very weak. I succeeded in calling Magento::Category.info(1).
But I failed to call Magento::Category.create(args).
The method definition is like the following.
# catalog_category.create
# Create new category and return its id.
#
# Return: int
#
# Arguments:
#
# int $parentId - ID of parent category
# array $categoryData - category data ( array(’attribute_code’⇒‘attribute_value’ )
# mixed $storeView - store view ID or code (optional)
def create(attributes)
id = commit("create", attributes)
record = new(attributes)
record.id = id
record
end
Here's what I tried.(parent id is 1)
args = [1, {:name => 'Cars', :description => 'Great Cars', :is_active => '1', :url_key => 'cars'}]
category_id = Magento::Category.create(args)
exception: 1 -> SQLSTATE[21000]: Cardinality violation: 1241 Operand should contain 1 column(s)
Can anybody provide an example of calling the method?
I contacted the gem developer and got the following reply. A nice guy.
Hi Sam,
Sorry about the sparse documentation. We had created this library very quickly and only used a small subset of the api in the project we were working on.
It looks like the call for create in the library does not pass through data correctly. Here is a workaround:
parent_id = 1
attributes = {
:url_key=>"cars",
:description=>"Great Cars",
:name=>"Cars",
:is_active=>"1",
:available_sort_by => "Name",
:default_sort_by => "Name",
:include_in_menu => '1'
}
category_id = Magento::Category.commit("create", parent_id, attributes)
I'll also commit a fix to github that takes the parent_id correctly.
Thanks,
-preston

How to find records that have duplicate data using Active Record

What is the best way to find records with duplicate values in a column using ruby and the new Activerecord?
Translating #TuteC into ActiveRecord:
sql = 'SELECT id,
COUNT(id) as quantity
FROM types
GROUP BY name
HAVING quantity > 1'
#=>
Type.select("id, count(id) as quantity")
.group(:name)
.having("quantity > 1")
Here's how I solved it with the AREL helpers, and no custom SQL:
Person.select("COUNT(last_name) as total, last_name")
.group(:last_name)
.having("COUNT(last_name) > 1")
.order(:last_name)
.map{|p| {p.last_name => p.total} }
Really, it's just a nicer way to write the SQL. This finds all records that have duplicate last_name values, and tells you how many and what the last names are in a nice hash.
I was beating my head against this problem with a 2016 stack (Rails 4.2, Ruby 2.2), and got what I wanted with this:
> Model.select([:thing]).group(:thing).having("count(thing) > 1").all.size
=> {"name1"=>5, "name2"=>4, "name3"=>3, "name4"=>2, "name5"=>2}
With custom SQL, this finds types with same values for name:
sql = 'SELECT id, COUNT(id) as quantity FROM types
GROUP BY name HAVING quantity > 1'
repeated = ActiveRecord::Base.connection.execute(sql)
In Rails 2.x, select is a private method of AR class. Just use find():
klass.find(:all,
:select => "id, count(the_col) as num",
:conditions => ["extra conditions here"],
:group => 'the_col',
:having => "num > 1")
Here is a solution that extends the other answers to show how to find and iterate through the records grouped by the duplicate field:
duplicate_values = Model.group(:field).having(Model.arel_table[:field].count.gt(1)).count.keys
Model.where(field: duplicate_values).group_by(&:field).each do |value, records|
puts "The records with ids #{records.map(&:id).to_sentence} have field set to #{value}"
end
It seems a shame this has to be done with two queries but this answer confirms this approach.

conditional include in linq to entities?

I felt like the following should be possible I'm just not sure what approach to take.
What I'd like to do is use the include method to shape my results, ie define how far along the object graph to traverse. but... I'd like that traversal to be conditional.
something like...
dealerships
.include( d => d.parts.where(p => p.price < 100.00))
.include( d => d.parts.suppliers.where(s => s.country == "brazil"));
I understand that this is not valid linq, in fact, that it is horribly wrong, but essentially I'm looking for some way to build an expression tree that will return shaped results, equivalent to...
select *
from dealerships as d
outer join parts as p on d.dealerid = p.dealerid
and p.price < 100.00
outer join suppliers as s on p.partid = s.partid
and s.country = 'brazil'
with an emphasis on the join conditions.
I feel like this would be fairly straight forward with esql but my preference would be to build expression trees on the fly.
as always, grateful for any advice or guidance
This should do the trick:
using (TestEntities db = new TestEntities())
{
var query = from d in db.Dealership
select new
{
Dealer = d,
Parts = d.Part.Where
(
p => p.Price < 100.0
&& p.Supplier.Country == "Brazil"
),
Suppliers = d.Part.Select(p => p.Supplier)
};
var dealers = query.ToArray().Select(o => o.Dealer);
foreach (var dealer in dealers)
{
Console.WriteLine(dealer.Name);
foreach (var part in dealer.Part)
{
Console.WriteLine(" " + part.PartId + ", " + part.Price);
Console.WriteLine
(
" "
+ part.Supplier.Name
+ ", "
+ part.Supplier.Country
);
}
}
}
This code will give you a list of Dealerships each containing a filtered list of parts. Each part references a Supplier. The interesting part is that you have to create the anonymous types in the select in the way shown. Otherwise the Part property of the Dealership objects will be empty.
Also, you have to execute the SQL statement before selecting the dealers from the query. Otherwise the Part property of the dealers will again be empty. That is why I put the ToArray() call in the following line:
var dealers = query.ToArray().Select(o => o.Dealer);
But I agree with Darren that this may not be what the users of your library are expecting.
Are you sure this is what you want? The only reason I ask is, once you add the filter on Parts off of Dealerships, your results are no longer Dealerships. You're dealing in special objects that are, for the most part, very close to Dealerships (with the same properties), but the meaning of the "Parts" property is different. Instead of being a relationship between Dealerships and Parts, it's a filtered relationship.
Or to put it another way, if I pull a dealership out of your results and passed to a method I wrote, and then in my method I call:
var count = dealership.Parts.Count();
I'm expecting to get the parts, not the filtered parts from Brazil where the price is less than $100.
If you don't use the dealership object to pass the filtered data, it becomes very easy. It becomes as simple as:
var query = from d in dealerships
select new { DealershipName = d.Name,
CheapBrazilProducts = dealership.Parts.Where(d => d.parts.Any(p => p.price < 100.00) || d.parts.suppliers.Any(s => s.country == "brazil")) };
If I just had to get the filtered sets like you asked, I'd probably use the technique I mentioned above, and then use a tool like Automapper to copy the filtered results from my anonymous class to the real class. It's not incredibly elegant, but it should work.
I hope that helps! It was an interesting problem.
I know this can work with one single Include. Never test with two includes, but worth the try:
dealerships
.Include( d => d.parts)
.Include( d => d.parts.suppliers)
.Where(d => d.parts.All(p => p.price < 100.00) && d.parts.suppliers.All(s => s.country == "brazil"))
Am I missing something, or aren't you just looking for the Any keyword?
var query = dealerships.Where(d => d.parts.Any(p => p.price < 100.00) ||
d.parts.suppliers.Any(s => s.country == "brazil"));
Yes that's what I wanted to do I think the next realease of Data Services will have the possiblity to do just that LINQ to REST queries that would be great in the mean time I just switched to load the inverse and Include the related entity that will be loaded multiple times but in theory it just have to load once in the first Include like in this code
return this.Context.SearchHistories.Include("Handle")
.Where(sh => sh.SearchTerm.Contains(searchTerm) && sh.Timestamp > minDate && sh.Timestamp < maxDate);
before I tried to load for any Handle the searchHistories that matched the logic but don't know how using the Include logic you posted so in the mean time I think a reverse lookup would be a not so dirty solution

Resources