views:

52

answers:

2

Hello,

I am new to Core Data and am stumbling on the right way to set up a query.

My managed object model includes the classes Apartment, Lease, and what I would otherwise call a join table called ApartmentLease. This looks like:

/-----------------\      /-----------------\      /-----------------\
| Apartment       |      | ApartmentLease  |      | Lease           |
|-----------------|      |-----------------|      |-----------------|
| number          |      | startOn         |      | occupantName    |
|-----------------|      | endOn           |      | monthlyRate     |
| apartmentLeases |-\    |-----------------|      | occupantPhone   |
\-----------------/  \->>| apartment       |      |-----------------|
                         | lease           |<<----|apartmentLeases  |
                         \-----------------/      \-----------------/

So what I have tried to model is that a Lease can have many Apartments and each Apartment in the lease has its own start and ending dates.

So my question is: Given a range of dates, how can I query the Apartment object and get back an array of all the Leases that occupied the Apartment during that date range?

Thanks and sorry for my horrible ASCII art!

Bert

A: 

Instead of querying the Apartment entity, query your ApartmentLease entity.

[NSPredicate predicateWithFormat:@"startOn <= %@ && endOn >= %@", referenceDate, referenceDate];

Then once you have the array of ApartmentLease entities, you can retrieve the apartment entities via:

NSArray *apartments = [apartmentLeaseArray valueForKeyPath:@"@distinctUnionOfObjects.apartment"];

Now you have an array of Apartment entities that match your search criteria.

update

If you already have a reference to the apartment then just get the set of apartment leases and filter against that set for a lease that includes the date in question. There is no reason to go through a NSFetchRequest if you already have the apartment. Think of Core Data as an object graph, not a database. All of the relationships are already pre-populated, you don't need to fetch them, just walk the object graph.

Marcus S. Zarra
This makes sense, so given an Apartment if I want to see if it was leased within the given date range would I just check the resulting array of Apartment entities and see if it contains the specific Apartment I'm looking for? Wouldn't this also be a lot of overhead if this app needed to scale?
Bert Sala
A: 

Marcus S. Zarra answer is the correct one for you actual question but you might want to reconsider your data model. You definitely don't want to try to think of Core Data in terms of relational databases.

In a good data model, each entity should model a real world object, event or condition. In this case, your ApartmentLease entity doesn't actually represent anything real, its just there to make the code work. That's a red flag.

You really only need Apartment and Lease entities. The Lease entity should accurately model the a real lease agreement. Since a real lease has its own start and end dates, that data should be stored in the attributes of the lease entity and not another entity.

You really only need:

Apartment {
    number:int;
    leases<--(optional,cascade)-->>lease.apartment
} 

Lease{
    startOn:date;
    endOn:date;
    occupantName:string;
    monthlyRate:float;
    occupantPhone:string
    apartment<<--(required,nullify)->Apartment.leases
}

Then you just take Marcus's predicate and apply it to the Apartment.leases to find the leases in the date range.

NSManageObject *anApartment= // which every apartment whose leases you want to examine
NSPredicate *rangePred=[NSPredicate predicateWithFormat:@"startOn <= %@ && endOn >= %@", startReferenceDate, endReferenceDate];
NSSet *leases=[anAparment valueForkey:@"leases"];
NSSet *leasesInDateRange=[leases filteredSetUsingPredicate:rangePred];

Remember that these entities are actually objects at runtime so if you create customer NSManagedObject subclasses, you can embed logic in them. For example, you could create transient bool attributes for the lease entity of: inForce, expired. Then have custom getters like so:

-(BOOL) inForce{
    BOOL isInForce;
    NSDate *today=[NSDate now];
    if (self.starOn<today<self.endOn){
        isInForce=YES;
    }else{
        isInForce=NO;
    }
    return isInForce;
}

Then you can fetch with predicates that look for all leases that are inForce, expired or pending or you can fetch all apartments whose leases are in one of those states.

TechZen
Thank you so much! I appreciate the detail of your response, and completely agree that models should be as simple as possible. But this model would not suffice if a lease was for more than one apartment. This occasionally happens and having one lease that includes more than one apartment seems to track with reality more than setting up a separate lease for each apartment. What do you think?
Bert Sala
Your model should duplicate reality. If a single lease can cover multiple apartments, then you should have the `apartment` relationship be `apartments` in a to-many to the `Apartment` entity.
TechZen