How do to specify a column of a distant join (several relations removed) in DBIx::Class? - perl

How do I specify in DBIx::Class the column of the third table I'm joining on? Here is my code:
my $join = $schema->resultset('Track')->search({'name' => 'Eminem'},{'join' => {'cd' => 'artist'}});
It just displays the tracks of Eminem, but I also want to display the artist name, Eminem? I cannot access name in that query because this is a ResultSet for Track, name is a column in the Artist table, the third table in the join.

I guess you want to filter your resultset based on the artist name, not track name:
my $rs = $schema->resultset('Track')->search({
'artist.name' => 'Eminem',
},
{
join => { cd => 'artist' },
});
When looping through the resultset you can access it using the relationship and column accessors:
for my $track ($rs->all) {
say $track->cd->artist->name . ' - ' . $track->name;
}

Use the relationship accessors.
for my $track (
$schema->resultset('Track')->search(
{
name => 'Eminem',
},
{
join => {cd => 'artist'},
}
)->all
) {
use Data::Printer;
p { $track->get_inflated_columns };
p $track->cd->artist->name;
}
__END__
{
cd MyDatabase::Main::Result::Cd {
internals: {
_column_data {
artist 2,
cdid 3,
title "The Marshall Mathers LP"
},
}
},
title "The Way I Am",
trackid 1
}
"Eminem"
{
cd MyDatabase::Main::Result::Cd {
internals: {
_column_data {
artist 2,
cdid 3,
title "The Marshall Mathers LP"
},
}
},
title "Stan",
trackid 2
}
"Eminem"

Related

For each object in array in perl

I am trying to loop through each object in an array in Perl and I think I am making an obvious error.
my #members_array = [
{
id => 1234,
email => 'first#example.com',
}, {
id => 4321,
email => 'second#example.com',
}
];
use Data::Dumper;
for my $member ( #members_array ) {
print Dumper( $member );
}
Expected output for first iteration
{
id => 1234,
email => 'first#example.com',
}
Actual output for first iteration
[{
'email' => 'first#example.com',
'id' => 1234
}, {
'email' => 'second#example.com',
'id' => 4321
}];
How do I loop through these elements in the array? Thanks!
[ ... ] is used to create an array reference; you need to use ( ... ) to create an array :
my #members_array = (
{
id => 1234,
email => 'first#example.com',
}, {
id => 4321,
email => 'second#example.com',
}
);
And then the rest of your code will work just fine.

DBIx::Class Get row from another table by ID

In my project I have 3 tables: Artists, Albums and Tracks
Result Artist:
...
__PACKAGE__->has_many(
'albums' => 'MYLIB::DB::Schema::Result::MyDir::Album',
{ 'foreign.artist_id' => 'self.id', },
);
...
Result Album:
...
__PACKAGE__->belongs_to(
'artist' => 'MYLIB::DB::Schema::Result::Artist',
{ 'foreign.id' => 'self.artist_id', },
);
__PACKAGE__->has_many(
'tracks' => 'MYLIB::DB::Schema::Result::MyDir::Track',
{ 'foreign.album_id' => 'self.id', },
);
...
Result Track:
__PACKAGE__->belongs_to(
'album' => 'MYLIB::DB::Schema::Result::MyDir::Album',
{ 'foreign.id' => 'self.album_id', },
);
Now I have a object $artist and I want to get a Track by ID.
Query Example:
SELECT * FROM Tracks WHERE track_id = $x
If you want to generate the SQL that you give us, then the fact that you have an artist object is irrelevant. Just get a track resultset and run find() on that.
my $track_rs = $schema->resultset('Track');
my $track = $track_rs->find($track_id);
If, for some reason, you don't have a schema object, then you can get that from your artist object.
my $schema = $artist->result_source->schema;
I assume that the track is on an album by your $artist. The query can be done by a join over the three tables. See DBIx::Class::Manual::Joining.
Here is an untested example.
my $tracks = $artist->search_related(
{
id => $my_track_id,
},
{
join => { albums => 'tracks' },
}
);
If your track is not necessarily by $artist, then it propably makes more sense to query tracksdirectly.

Build and Access a Complex Data Structure Using Perl

I have a large one-dimensional hash with lots of data that I need to structure in such a way that I can sort it easily into a format that is the same each time the code executes.
Original Hash Data:
{
'datetime' => 'datetime value',
'param_name' => 'param name',
'param_value' => 'param value',
'category' => 'category name'
}
Current Data Structure:
{
'datetime value' => {
'category' => {
'param_name' = > 'param name',
'param_value' => 'param value
}
}
}
I can almost build this structure in code, except for every category, there could be multiple param_names and param_values with the same key name.
The problem I have is that if there are multiple param names/values, only the last pair are saved in the new data structure.
I know that keys have to be unique, so I'm not quite sure how to resolve this as of yet.
Once the structure is built, I then need to understand how to sort the data based on datetime, then param_name so that the order is always the same in the output.
Looking at the difference between your first and second example, I think you have your structure a bit off. I think this matches more of what you want:
{
DATE => date_time_value,
PARAMETERS => {
param_name1 => parameter_value1,
param_name2 => parameter_value2
}
}
This way, the structure with data may look like this:
{
DATE_TIME => "10/31/2031 12:00am",
PARAMETERS => {
COLOR => "red",
SIZE => "Really big",
NAME => "Herman",
}
}
Usually, you think of objects having fields which contain values. Think of a row of a SQL table or a spreadsheet. You have columns with headings, and rows that contain the value.
Let's take an employee. They have a name, age, job, and a phone number:
{
NAME => "Bob Smith",
AGE => "None of your business",
JOB => "Making your life miserable",
PHONE => "555-1212"
}
Unlike a table, each entry could contain other structure. For example, people usually have more than one phone number, and we might want to store the last name separate from the first name:
{
NAME => {
FIRST => "Bob",
LAST => "Smith"
}
AGE => "None of your business",
JOB => "Making your life miserable"
PHONE => {
CELL => "555.1234",
WORK => "555.1212"
}
}
Then we have the people who have multiple phones of the same time. For example, Bob has two cell phones. In this case, we'll make each phone type field an array of values:
{
NAME => {
FIRST => "Bob",
LAST => "Smith",
}
AGE => "None of your business",
JOB => "Making your life miserable"
PHONE => {
CELL => ["555.1234", "555.4321"]
WORK => ["555.1212"]
}
}
And to initialize it:
my $person = {};
$person->{NAME}->{FIRST} = "Bob";
$person->{NAME}->{LAST} = "Smith";
$person->{AGE} = "None of your business";
$person->{JOB} = "Making your life miserable";
$person->{PHONE}->{CELL}->[0] = "555.1234";
$person->{PHONE}->{CELL}->[1] = "555.4321";
$person->{PHONE}->{WORK}->[0] = ""555.1212";
I think it seems appropriate to have a params hash where the keys are all of the names and the values are the actual values. It seems like that is what you want.
my %hash = {
'datetime value' => {
'category' => {
'params' => {
'param-name1' => 'param-value1',
'param-name2' => 'param-value2',
'param-name3' => 'param-value3',
etc..
}
}
}
}
After this restructuring it should be pretty easy to sort based on whatever you would like.
alphabetically by key:
my #alphabetic_keys = sort { $hash{$a} cmp $hash{$b} } keys %{ $hash{params} };
length by key:
my #by_length_keys = sort { length($a) <=> length($b) } keys %{ $hash{params} };
Assuming category names are unique, I would suggest the following data structure:
{
'datetime value 1' => {
'category name 1' => {
'param name 1' = > [param value1, param value2, ...],
'param name 2' = > [param value3, param value4, ...],
etc...
},
'category name 2' => {
'param...' => [ value... ]
},
'datetime value 2' => {
etc...
}
}

Reversing a multi-value hash in perl

I have a hash that looks like this:
{ bmw => { id => 1, color => brown } }
{ mercedes => { id => 2, color => black } }
I want to reverse this hash in perl so that I just get a mapping of id => name_of_car. Do I need to use the reverse function somehow?
Note: I can always iterate through the original hash and assign the keys and values accordingly in the new hash, nut I wanted to know if there was a slicker way.
Don't need a slick way:
my %h = (
bmw => { id => 1, color => "brown" } ,
mercedes => { id => 2, color => "black" }
);
my %j = map { ($h{$_}{id} => $_) } keys %h;
for (keys %j) {
print "$_ $j{$_}\n";
}
Output:
$ ./silly.pl
1 bmw
2 mercedes
What you've posted is not valid perl, but I think I take your meaning. One slick way to do this would be with a hash slice and a map.
my %hash = (
bmw => { id => 1, color => 'brown' },
mercedes => { id => 2, color => 'black' },
);
my %new_hash;
#new_hash{ map { $_->{id} } values %hash } = keys %hash;

How can I store a list of references in Perl?

I would like to create a list of records. I can add one record just fine:
my $records = [
{ ID => 5342755,
NAME => 'Me',
} ,
];
When I try to add another record, the other records disappear:
$records = [ {
ID => 1212121,
} ];
What is the problem and how can I resolve this?
The problem is you are overwritting the value of $record so that there is only ever one value in the array. Perhaps try the following instead:
my $records = [
{ ID => 5342755,
NAME => 'Me',
} ,
];
push #$records, {
ID => 1212121,
};
You override your variable...
When you declare:
my $records = [date structure here];
you really declare an array ref, if you are a newbie then try (more intuitive)
my #records = (
{
ID => 54321,
NAME => 'bar',
} ,
);
push #records, {ID => 12345, NAME => 'foo'};
print $records[1]->{NAME};
That would print 'foo'