DBIx::Class - where is this relationship being cached? - perl

I have an Order entity and an Address entity, and in my Schema::Result::Order module I have a simple belongs to relationship:
__PACKAGE__->belongs_to( "address", 'Schema::Result::Address',
{ addressid => 'addressid' });
I run this code with DBIC_TRACE=1:
my $order = $schema->resulset('Order')->find($id);
my $add1 = $order->address;
my $add2 = $order->address;
and I only see one SELECT ... FROM ADDRESS ... query, so apparently the second $order->address method is not hitting the database.
So this might be a simple question, but where is the address object getting cached? (in the $order object?)
Secondly, is this caching configurable (i.e. can I configure DBIC to not cache these relationships)?

I think the way you have your order to address relationship you will only have one address to the order:
__PACKAGE__->belongs_to( "address", 'Schema::Result::Address',
{ addressid => 'addressid' });
If your order will have many addresses you will want:
__PACKAGE__->has_many( "address", 'Schema::Result::Address',
{ addressid => 'addressid' });
Then you can retrieve the addresses a number of ways:
my $address_rs = $order->search_related('address',{});
while(my $row = $address_rs->next) {
#$row has an address record
}
I am not sure how the caching works in this situation
my $order = $schema->resulset('Order')->find($id);
my $add1 = $order->address;
my $add2 = $order->address;
But if you access your address record like this:
my $address_rs = $order->search_related('address',{});
you can control it with your query attributes:
https://metacpan.org/module/DBIx::Class::ResultSet#cache

Found the answer: it's cached in $order->{_relationship_data}->{address}.
Haven't determined if that caching can be disabled.

Related

DbiX::Class / Creating tree of test data without persisting

I'm using DBIx::Class as or mapper for my Perl project. When it comes to generating test data I'm using DBIx::Class::ResultSet::new to create new entities in memory. In order to link entities with relationships I use set_from_related. This works absolutely flawless until I try to set the value(s) for a has_many relationship. Pseudo example:
# Table 'AUTHOR' has
# one-to-one (belongs_to) relationship named 'country' to table 'COUNTRY'
# one-to-many (has_many) relationship named 'books' to table 'BOOK'
my $s = Schema::getSchema();
my $author = $s->resultset('Author')->new({ name => 'Jon Doe', year_of_birth => 1982 });
my $country = $s->resultset('Country')->new({ name => 'Germany', iso_3166_code => 'DE' });
my $book = $s->resultset('Book')->new({ title => 'A star far away', publishing_year => 2002 });
# Now let's make 'em known to each other
$author->set_from_related('country', $country);
$author->set_from_related('books', $book);
# At this point
# $author->country is defined
# $author->books->first is undef <<<---- Problem
I cannot find a suitable method in the DBIx::Class::Relationship::Base documentation. The one closest to what I need is add_to_$rel but this method creates (persists) the entities. This is not an option for me as some of the entites used in my project don't belong to me (no write permission).
Does anyone have an idea how to add entities in memory for a has_many relationship ?
That's not possible as newly created result objects don't have their primary key column(s) populated until they are persisted to the database.
What you possibly want is to use multi-create and run that inside a transaction.

Traversing hashes in Perl based on config

I need to be able to pluck specific values from data that I receive from different 3rd parties. The data can structured differently depending on the 3rd party. For example:
my $first =
{
email => "joe\#example.com",
firstname => "Joe",
lastname => "Regular",
};
my $second =
{
user => {
e-mail => "joe\#example.com",
firstName => "Joe",
lastName => "Regular",
}
};
I know what the data structure will be for each 3rd party, so I can define that as config. What I want to end up with is
my $email = _magic($first_config,$first);
my $other_email = _magic($second_config,$second);
Any ideas much appreciated.
Build a look-up table. And you can use a dispatch table, hash with values being code references, so that when a party-identification is used as the key the code for that party executes
my %get_value = ( first => \&fetch_first, second => \&fetch_second );
my $party = 'first'; # input via command-line options, STDIN ...
my $email = $get_value{$party}->();
where \&fetch_first is a reference to the subroutine fetch_first. You can also enter it directly, first => sub { ... }, suitable for simple code. See perlreftut, perlref, and perlsub.
There are many ways to carry data in your program, and so to implement the lookup itself.
Here is an illustration, built in steps. It uses the (confirmed) fact that the data is in valid Perl data structures, and for simplicity it specifies the data right in each sub.
sub fetch_first {
my $data = {
email => '...',
firstName => '...',
};
return $data->{email};
}
This only delivers the email address, but we can do better.
Once you dereference a code reference you can also pass arguments
my $first_name = $get_value{$party}->('firstName');
where the subs are now written to use this input to return the required field
sub fetch_first {
my ($query) = #_;
my $data = {
email => '...',
firstName => '...',
};
return $data->{$query};
}
A big weakness of the above is that the calling code must use valid names of keys, so it needs to know the details of implementation of what it is using.
This can be improved, for example by choosing an interface for the call which is then translated in the subs into key names (or via yet another look-up structure). Then you make calls such as
my $email = $get_value{$party}->('email'); # or: 'first', 'last'
and somewhere you have association first => 'firstName' (etc) which subs can look up.
The flexibility is greatly helped by data being set up in a consistent way. The whole thing can also be quite maintainable if the code is organized thoughtfully.
If this grows more complex the solution is to write a class. Then you can build a very nice system.

Entity Framework seeding complex objects?

I am trying to figure out how to seed a complex entity framework code first object. Currently the code I have allows me to insert simple objects but there has to be a way do more complex items, i.e. Address with an AdressType field.
context.AddressTypes.AddOrUpdate(
p => p.Name,
new AddressType { Name = "Original" },
new AddressType { Name = "Shipping" },
new AddressType { Name = "Billing" }
);
context.Addresses.AddOrUpdate(
a => a.Address1,
new Address
{
Address1 = "1234 West Main",
City = "Hannibal",
Region = "MO",
PostalCode = "12345",
Country = "USA",
Type = context.AddressTypes.Where(a=>a.Name=="Original")
});
But while I can "Find" an addresstype by id I can't do a "Where" name equals. Find would work except that I can not guarantee what the id will be with each seed and want to base it on name instead.
Any suggestions on how to do this?
TIA
Solution:
Added a reference System.Linq to the seed class. Then was able to the where clause.

How to filter records using group functionality in BreezeJs

I'm developing a client app that uses breezejs and Entity Framework 6 on the back end. I've got a statement like this:
var country = 'Mexico';
var customers = EntityQuery.from('customers')
.where('country', '==', country)
.expand('order')
I want to use There may be hundreds of orders that each customer has made. For the purposes of performance, I only want to retrieve the latest order for each customer. This will be based on the created date for the order. In SQL, I could write something like this:
SELECT c.customerId, companyName, ContactName, City, Country, max(o.OrderDate) as LatestOrder FROM Customers c
inner join Orders o on c.CustomerID = o.CustomerID
group by c.customerId, companyName, ContactName, City, Country
If this was run against the northwind database, only the most recent order row is returned for each customer.
How can I write a similar query in breeze, so that it runs on the server side and therefore returns less data to the client. I know I could handle this all on the client but writing some javascript in a querysucceeded method that could be run by the client - but that's not the goal here.
thanks
For a case like this, you should create a special endpoint method that will perform your query.
Then you can use an Entity Framework query to do what you want, using the LINQ syntax.
Here are two Web API examples:
[HttpGet]
public IQueryable<Object> CustomersLatestOrderEntities()
{
// IQueryable<Object> containing Customer and Order entity
var entities = ContextProvider.Context.Customers.Select(c => new { Customer = c, LatestOrder = c.Orders.OrderByDescending(o => o.OrderDate).FirstOrDefault() });
return entities;
}
[HttpGet]
public IQueryable<Object> CustomersLatestOrderProjections()
{
// IQueryable<Object> containing Customer and Order entity
var entities = ContextProvider.Context.Customers.Select(c => new { Customer = c, LatestOrder = c.Orders.OrderByDescending(o => o.OrderDate).FirstOrDefault() });
// IQueryable<Object> containing just data fields, no entities
var projections = entities.Select(e => new { e.Customer.CustomerID, e.Customer.ContactName, e.LatestOrder.OrderDate });
return projections;
}
Note that you have a choice here. You can return actual entities, or you can return just some data fields. Which is right for you depends upon how you are going to use them on the client. If they are just for display in a
non-editable list, you can just return the plain data (CustomersLatestOrderProjections above). If they can potentially
be edited, then return the object containing the entities (CustomersLatestOrderEntities). Breeze will merge the entities
into its cache, even though they are contained inside this anonymous object.
Either way, because it returns IQueryable, you can use the Breeze filtering syntax from the client to further qualify the query.
var projectionQuery = breeze.EntityQuery.from("CustomersLatestOrderProjections")
.skip(20)
.take(10);
var entityQuery = breeze.EntityQuery.from("CustomersLatestOrderEntities")
.where('customer.countryName', 'startsWith', 'C');
.take(10);

Can I join the column names with search_related in DBIx?

I have a DBIx Class schema where I have;
A Device that has many Interfaces
An Interface has many Rules Applied
Each Rule has many Rule Entries.
I want to search for all of the Rule Entries for a Particular device name and Rule Name.
I am still learning DBIx so I don’t know if this is even the most efficient way.
I am doing this like so;
my $rs = $self->search( { devicename => ‘DeviceA’ } )->search_related('interfaces')->search_related(’Rules’, { rulename => ‘RuleA’ } )->search_related(‘RuleEntries’, {},
{ columns => [qw/source destination port/], result_class => 'DBIx::Class::ResultClass::HashRefInflator'} );
What I am trying to do is get the ‘RuleName’ as a column of my result set.
at the moment I’m getting all of the Rule Entries for DeviceA with a RuleName on an interface called RuleA, The columns returned are
‘source destination port’.
I want this to look like
‘rulename source destination port’
As you are already restricting the rule name it doesn't make sense to query it from the database.
Besides that you should always search for objects of the type you want to get back, in your case that's rule entries:
my $rs = $schema->resultset('Rule_Entries')->search({
'rel_device.name' => 'DeviceA',
'rel_rule.name' => 'Rule',
},{
columns => [ 'rel_rule.name', 'me.source', 'me.destination', 'me.port' ],
join => { rel_rule => { rel_interface => 'rel_device' }},
});
It seems your doing something very similar what I do: storing firewall rules. You might want to have the rule directly related to the device and the interface being an optional attribute of the rule because some vendors don't have interface specific rules (Checkpoint).