Selecting columns from eager-loaded laravel models - laravel-5.1

I'm trying to select columns from related models, but I'm not quite sure how to do it using Eloquent (I want to avoid the DB namespace).
This is what I have so far...
$users = User::with("role", "country") -> select(["first_name", "last_name", "email_address"]);
How would I go about modifying the select list so that it also includes role.title and country.name?
So essentially, my select list would look something like this:
-> select(["first_name", "last_name", "email_address", "role.title", "country.name"]);
Many thanks!

Did you try
User::with(['role' => function ($query) {
$query->select('title');
}])->with(['country' => function ($query) {
$query->select('name');
}])->get();
or
User::with([
'role' => function ($query) {
$query->select('title');
},
'country' => function ($query) {
$query->select('name');
}
])->get();

Related

Esper access array event propertie

I want to process the following JSON message using Esper:
{
"firstname":"John",
"lastname":"Do",
"address":[{"street":"Maplestreet","number":100,"city":"New York"}]
}
When the schema look like:
create schema Address (street string, number int, city string);
create schema Person (firstname string, lastname string, address Address[]);
What is the correct way to select the event properties within the array?
I have tried:
select address[0].street from Person
select address.street from Person
select address[0] from Person
select {address.street} as street from Person
but I get null values. Is the schema wrong or am I missing something?
EDIT: When I try:
select * from Person
I get the initial JSON as result
EDIT2:
This is my Jruby code:
address_type = {
"street" => "string",
"number" => "int",
"city" => "string"
}
epService.getEPAdministrator.getConfiguration.addEventType("Address", address_type)
person_type = {
"firstname" => "string",
"lastname" => "string",
"address" => "Address[]"
"
}
epService.getEPAdministrator.getConfiguration.addEventType("Person", person_type)
Most likely the event object is not populated correctly by your application. I would suggest creates a Java class first in the same structure to test the statements. Once that works switch to the event object you used originally i.e. XML, object-array or Map or ...

Rails update multiple records find based on other id

Using Rails 3.2. As shown in the doc on update method, the update finds based on id:
update(id, attributes)
# id - This should be the id or an array of ids to be updated.
# Updates multiple records
people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
Person.update(people.keys, people.values)
What if I want to update an array found based on other columns? For example:
people = { 'cook' => { "first_name" => "David" }, 'server' => { "first_name" => "Jeremy" } }
Find people with role = cook, then update first_name = David; find people with role = server, then update first_name = jeremy.
I want it to be done in 1 query if possible, and not by SQL. Thanks.
You can Achieve this with #update_all
people = { 'cook' => { "first_name" => "David" }, 'server' => { "first_name" => "Jeremy" } }
Person.update_all(people.keys, people.values)
In that case I would write my own sql statement. I depends on which database backend you are using.
http://www.postgresql.org/docs/9.1/static/plpgsql-control-structures.html
https://dev.mysql.com/doc/refman/5.0/en/case.html
The update method doesn't execute 1 SQL query when passed an array of ids and values. If you view the source code for update, you will see it loops through the array and executes 2 queries for each record (a find and an update query) to then return an array of updated objects.
If you're happy accepting that you will need to make 2 queries per row, then you can use the following code for finding people by role.
people = { 'cook' => { "first_name" => "David" }, 'server' => { "first_name" => "Jeremy" } }
updated_people = people.keys.map.with_index do |role, index|
object = Person.where(role: role).first!
object.update(people.values[index])
object
end
Note: This code only updates the first record it finds per role because I've assumed there will only be one cook with the first name 'David'.
If you want to only use 1 SQL statement, you should look at doing it in SQL like devanand suggested.

Best way to join two different model queries in Rails?

I have two different models that I need to join together in a selection query and list on a page. They share all of attributes that I'll need to reference in the view (created_at, updated_at, name, etc), and I want them in order of creation. I'm wondering what the most efficient way to do this is? I was thinking of performing the selection query on each object individually and adding the relevant parts into a common array but that seems inefficient.
For example if my models were Dogs and Cats and I wanted a list of all dogs and cats of age 5, I was thinking something like
#pets = []
dogs = Dogs.where(:age => 5)
cats = Cats.where(:age => 5)
dogs.each do |dog|
hash = {"id" => dog.id, "name" => dog.name, "created_at" => dog.created_at }
#pets.push(hash)
end
cats.each do |cat|
hash = {"id" => cat.id, "name" => cat.name, "created_at" => cat.created_at }
#pets.push(hash)
end
But is that the best way to do it? also, I'm not sure how to sort the finished array in this example according to date created...
try this
dogs = Dogs.where(:age => 5)
cats = Cats.where(:age => 5)
#pets = (dogs + cats).sort_by { |pet| pet.created_at }
OR if you want your hashes still, use map to create the array of hashes
dogs = Dogs.where(:age => 5)
cats = Cats.where(:age => 5)
#pets = (dogs + cats).sort_by do |pet|
pet.created_at
end.map do |pet|
{ "id" => dog.id, "name" => pet.name, "created_at" => pet.created_at }
end

Mongoid Group By or MongoDb group by in rails

I have a mongo table that has statistical data like the following....
course_id
status which is a string, played or completed
and timestamp information using Mongoid's Timestamping feature
so my class is as follows...
class Statistic
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
field :course_id, type: Integer
field :status, type: String # currently this is either play or complete
I want to get a daily count of total # of plays for a course. So for example...
8/1/12 had 2 plays, 8/2/12 had 6 plays. Etc. I would therefore be using the created_at timestamp field, with course_id and action. The issue is I don't see a group by method in Mongoid. I believe mongodb has one now, but I'm unsure of how that would be done in rails 3.
I could run through the table using each, and hack together some map or hash in rails with incrementation, but what if the course has 1 million views, retrieving and iterating over a million records could be messy. Is there a clean way to do this?
As mentioned in comments you can use map/reduce for this purpose. So you could define the following method in your model ( http://mongoid.org/en/mongoid/docs/querying.html#map_reduce )
def self.today
map = %Q{
function() {
emit(this.course_id, {count: 1})
}
}
reduce = %Q{
function(key, values) {
var result = {count: 0};
values.forEach(function(value) {
result.count += value.count;
});
return result;
}
}
self.where(:created_at.gt => Date.today, status: "played").
map_reduce(map, reduce).out(inline: true)
end
which would result in following result:
[{"_id"=>1.0, "value"=>{"count"=>2.0}}, {"_id"=>2.0, "value"=>{"count"=>1.0}}]
where _id is the course_id and count is the number of plays.
There is also dedicated group method in MongoDB but I am not sure how to get to the bare mongodb collection in Mongoid 3. I did not have a chance to dive into code that much yet.
You may wonder why I emit a document {count: 1} as it does not matter that much and I could have just emitted empty document or anything and then always add 1 to the result.count for every value. The thing is that reduce is not called if only one emit has been done for particular key (in my example course_id has been played only once) so it is better to emit documents in the same format as result.
Using Mongoid
stages = [{
"$group" => { "_id" => { "date_column_name"=>"$created_at" }},
"plays_count" => { "$sum" => 1 }
}]
#array_of_objects = ModelName.collection.aggregate(stages, {:allow_disk_use => true})
OR
stages = [{
"$group" => {
"_id" => {
"year" => { "$year" => "$created_at" },
"month" => { "$month" => "$created_at" },
"day" => { "$dayOfMonth" => "$created_at" }
}
},
"plays_count" => { "$sum" => 1 }
}]
#array_of_objects = ModelName.collection.aggregate(stages, {:allow_disk_use => true})
Follow the links below to group by using mongoid
https://taimoorchangaizpucitian.wordpress.com/2016/01/08/mongoid-group-by-query/
https://docs.mongodb.org/v3.0/reference/operator/aggregation/group/

Hash table in Rails

I have the following hash table:
COUNTRIES = {
'France' => 'FR',
'German' => 'GE',
'United Kingdom' => 'UK'
}
I have it in my model and use it in my views so the countries are displayed as a select box. Now I have one view where I want all those values plus one more value "Europe" => 'EU' to be shown.
Meaning I would have:
COUNTRIES = {
'Europe' => 'EU',
'France' => 'FR',
'German' => 'GE',
'United Kingdom' => 'UK'
}
Now I can create a new hash table but I dont want to repeat the same values in a new table.
So, how can I re-use the same table, adding one more value just for a particular view?
All ideas are welcome.
customCountries = COUNTRIES.clone
customCountries['Europe'] = 'EU'
Try this
custom = {'Europe' => 'EU'}.merge(COUNTRIES)
"Europe".to_country!

Resources