1
votes

screen shot addedI've two entities named Expenses and ExpensesCategory the Expenses has a relationship with CategoryExpenses with following attributes- name-expenseCategory, Destination - ExpensesCategory, inverse - expense. the ExpensesCategory has an attribute named expenseCategory of string type and Expenses has an attribute named amount. Now i want to fetch expenses Grouped by expenseCategory with sum on amount. here is my code

NSSortDescriptor *sortTitle =[NSSortDescriptor sortDescriptorWithKey:@"addedTimestamp"
                                                               ascending:YES];
    NSArray *sortDescriptors = [NSArray arrayWithObjects:sortTitle, nil];
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Expense"
                                              inManagedObjectContext:context];

    NSPredicate *predicate;
    predicate =[NSPredicate predicateWithFormat:@"(addedTimestamp <= %@) AND (addedTimestamp >= %@)",currentDate, endDate];
        NSAttributeDescription* amtDescription = [entity.attributesByName objectForKey:@"amount"];
        NSRelationshipDescription* nameDesc = [entity.relationshipsByName objectForKey:@"expenseCategory"];

        NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: @"amount"];
        NSExpression *countExpression = [NSExpression expressionForFunction: @"sum:"
                                                                  arguments: [NSArray arrayWithObject:keyPathExpression]];
        NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
        [expressionDescription setName: @"Sum"];
        [expressionDescription setExpression: countExpression];
        [expressionDescription setExpressionResultType: NSDecimalAttributeType];

        [fetchRequest setPropertiesToFetch:[NSArray arrayWithObjects:amtDescription, expressionDescription, nameDesc, nil]];
        [fetchRequest setPropertiesToGroupBy:[NSArray arrayWithObject:nameDesc]];
        [fetchRequest setResultType:NSDictionaryResultType];

        [fetchRequest setEntity:entity];
        [fetchRequest setPredicate:predicate];
        [fetchRequest setSortDescriptors:sortDescriptors];
    [context executeFetchRequest:fetchRequest error:&error];

But when i run i got Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attribute/relationship description names passed to setPropertiesToFetch: must match name on fetch entity

Any help would be appreciated.

4
I'm still facing the same problem .. i really need your help guys. - Bharat
If you post a screenshot from the Xcode model editor showing the object graph, that would be helpful. Click "editing style" at bottom of editor. Example: techotopia.com/images/d/dc/Xcode_4.2_core_data_relationship.jpg - quellish
@quellish screen shot added in the question, please have a look. - Bharat
setPropertiesToFetch can only take property names (strings) or property descriptions that belong to Expenses. Shouldn't the "expense" relationship of ExpenseCategory be a to-many given what you are trying to do? I would assume a category can have many (expense) members, while an expense might have one (or more) categories. - quellish
Yes you are right a category can have many expenses and an expense can point to only one category, actually i'm working on an existing project and core data was designed by some one else.Better if you could post your answer including what is wrong in core data and code. - Bharat

4 Answers

5
votes

Here is the answer to your question.

NSEntityDescription *entity = [NSEntityDescription entityForName:@"ExpenseCategory" inManagedObjectContext:context];

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];

NSError *error;

fetchRequest.predicate =[NSPredicate predicateWithFormat:@"(addedTimestamp <= %@) AND (addedTimestamp >= %@)",currentDate, endDate];

NSSortDescriptor *sortTitle =[NSSortDescriptor sortDescriptorWithKey:@"addedTimestamp" ascending:YES];
fetchRequest.sortDescriptors  = [NSArray arrayWithObjects:sortTitle, nil];

NSExpressionDescription *ex = [[NSExpressionDescription alloc] init];
[ex setExpression:[NSExpression expressionWithFormat:@"@sum.expense.amount"]];
[ex setName:@"Sum"];
[ex setExpressionResultType:NSDecimalAttributeType];

[fetchRequest setPropertiesToFetch:[NSArray arrayWithObjects:@"expenseCategory", ex, nil]];
[fetchRequest setPropertiesToGroupBy:[NSArray arrayWithObject:@"expenseCategory"]];
[fetchRequest setResultType:NSDictionaryResultType ];

// Execute the fetch.
NSArray *matchingObjects = [context executeFetchRequest:fetchRequest error:&error];
if ([matchingObjects lastObject]) {
    for (id expenseCategory in matchingObjects) {
        NSLog(@"%@", expenseCategory);
    }
}

If you are wanting to filter by Expense addedTime and not the ExpenseCategory addedTime, then change the predicate to:

fetchRequest.predicate =[NSPredicate predicateWithFormat:@"(expense.addedTimestamp <= %@) AND (expense.addedTimestamp >= %@)",currentDate, endDate];
2
votes

Whilst answering another question I discovered the cause of the specific error highlighted in this question ("Attribute/relationship description names passed to setPropertiesToFetch: must match name on fetch entity"). I am adding this answer to assist those seeking a solution for that error.

The reason for this error is that the fetchRequest's propertiesToFetch is being set BEFORE the fetchRequest's entity. Changing the order of the code should resolve the problem:

    [fetchRequest setEntity:entity];
    [fetchRequest setPropertiesToFetch:[NSArray arrayWithObjects:amtDescription, expressionDescription, nameDesc, nil]];
    [fetchRequest setPropertiesToGroupBy:[NSArray arrayWithObject:nameDesc]];
    [fetchRequest setResultType:NSDictionaryResultType];

The Apple documentation is clear that attributes and relationships included in the propertiesToFetch must match a property name:

"The name of an attribute or relationship description must match the name of a description on the fetch request’s entity."

But it took me a long time to realise that this test is undertaken as soon as you set propertiesToFetch (rather than when the fetch is executed) hence the need for the entity to be set first.

1
votes

You are passing |expressionDescription|, which is an NSExpressionDescription, to setPropertiesToFetch:; setPropertiesToFetch: accepts an array of ONLY NSPropertyDescriptions, and so is throwing an exception.

Both NSExpressionDescriptions and NSPropertyDescriptions ARE allowed in setPropertiesToGroupBy: however.

0
votes

You should have a look at pre-fetching technique. In this url you can find an example between Employee and Department.

Hope that helps!