I have a Entity called deal and deal has a property called date which is the time this deal object inserted into the store.
and one day may have several deals.
So I want count some data group by day, I want fetch dayand countofsomething
like:
2013-06-03 3
2013-06-02 4
and I don't want to use sectionPath because it only put deals into section.
I know I can have this done by have another property(type:string) like dayOfTheDate which is like 2013-06-03 in each object.
btw, transient property don't seem to work in this situation
Could you understand what I am looking for?
Comment here so I can provide more detail
Thanks all of you.
Sample of how I did it when I counted number of same notes
NSEntityDescription* entity = [NSEntityDescription entityForName:#"Assets"
inManagedObjectContext:[appDelegate managedObjectContext]];
NSAttributeDescription* statusDesc = [entity.attributesByName objectForKey:#"notes"];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: #"assetUrl"]; // Does not really matter
NSExpression *countExpression = [NSExpression expressionForFunction: #"count:"
arguments: [NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: #"count"];
[expressionDescription setExpression: countExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
[searchFetchRequest setPropertiesToFetch:[NSArray arrayWithObjects:statusDesc,expressionDescription, nil]];
[searchFetchRequest setPropertiesToGroupBy:[NSArray arrayWithObject:statusDesc]];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timestamp" ascending:NO];
[searchFetchRequest setSortDescriptors:#[sortDescriptor]];
[searchFetchRequest setFetchLimit:10];
NSPredicate *query = [NSPredicate predicateWithFormat:#"notes contains[cd] %#",_txtCameraNote.text];
[searchFetchRequest setPredicate:query];
[searchFetchRequest setResultType:NSDictionaryResultType];
NSArray *fetchedObjects = [appContext executeFetchRequest:searchFetchRequest error:nil];
fetchedObjects would be something like this.
({
count = 1;
notes = "glenny and me";
},
{
count = 6;
notes = macair;
})
Related
I'm trying to make a fetch in Core Data where I group by month of my property date.
In SQL
SELECT SUM(total) as value,
date as date,
strftime('%m-%Y', date) as convertDate
FROM table
GROUP BY convertDate
I don't see any function where I could use strftime.
Is there a way to do this with a NSFetchRequest instead of a NSFetchedResultController?
you can do this by using NSExpressionDescription
for the grouped by With NSFetchRequest you can see this tutorial or this stackOverFlow question
For the sum here the NSExpressionDescription to add to your NSFetchRequest
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
expressionDescription.name = #"sumOfAmounts";
expressionDescription.expression = [NSExpression expressionForKeyPath:#"#sum.total"];
expressionDescription.expressionResultType = NSDecimalAttributeType;
For the date i think that you can format it by using NSDateFormatter when you get fetch records.
Let me know if this was helpful :)
UPDATE
[fetch setPropertiesToFetch:[NSArray arrayWithObjects:statusDesc, expressionDescription, nil]];
[fetch setPropertiesToGroupBy:[NSArray arrayWithObject:statusDesc]];
[fetch setResultType:NSDictionaryResultType];
NSError* error = nil;
NSArray *results = [myManagedObjectContext executeFetchRequest:fetch
error:&error];
My core data model:
Contact
=======
name
phone number (unique)
I need to display a table view of contacts with the following condition:
if there is more than one contact with the same name - show name+number
otherwise - show only name
Fo for example, if my core data contacts are:
Michael, 11112221
Jon, 33438282
Jon, 72727272
Lisa, 99939393
My table view should present:
Jon (33438282)
Jon (72727272)
Lisa
Michael
Currently I'm using the following NSFetchRequest to show the contact list:
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Contact"];
request.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES selector:#selector(caseInsensitiveCompare:)]];
request.fetchBatchSize = 20;
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:#"nameFirstLetter"
cacheName:nil];
I'm looking for the most efficient way to know which name appears more than once so I can show the number next to the name.
If you're using NSSQLiteStoreType as your persistent store you can fetch duplicate items with an NSExpression:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contact" inManagedObjectContext:self.managedObjectContext];
NSAttributeDescription *nameDesc = [entity.attributesByName objectForKey:#"name"];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:#"name"];
NSExpression *countExpression = [NSExpression expressionForFunction:#"count:" arguments:#[keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: #"count"];
[expressionDescription setExpression: countExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
NSError *error = nil;
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"Contact"];
[fetch setPropertiesToFetch:#[nameDesc, expressionDescription]];
[fetch setPropertiesToGroupBy:#[nameDesc]];
[fetch setResultType:NSDictionaryResultType];
NSArray *results = [self.managedObjectContext executeFetchRequest:fetch error:&error];
NSArray *duplicates = [results filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"count > 1"]];
NSArray *duplicateNames = [duplicates valueForKeyPath:#"name"];
duplicateNames contains the name of contacts which name appears multiple times in the db. So, whenever a contact displayed in the table view, just query the duplicateNames array to check whether the actual contact is a duplicate or not.
The easiest way if you only want to modify the display I would modify tableView:cellForRowAt... to check the name of the person above and below the current one
in basic mock code:
- tableView:cellForRowAtIndexPath:(id)path {
....
Person *prior = ...
Person *next = ...
if(prior.name == current.name || next.name == current.name) {
//show name+number
}
}
I want to get the sum of all values in a column for a distinct id(date in my case). My code is
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Table"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(endTime!=%#)",nil];
[fetchRequest setPredicate:predicate];
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.returnsDistinctResults=YES;
NSExpressionDescription *aDescription = [[NSExpressionDescription alloc] init];
aDescription.name = #"A";
aDescription.expression = [NSExpression expressionForKeyPath:#"#sum.a"];
aDescription.expressionResultType = NSDecimalAttributeType;
NSExpressionDescription *bDescription = [[NSExpressionDescription alloc] init];
bDescription.name = #"B";
bDescription.expression = [NSExpression expressionForKeyPath:#"#sum.b"];
bDescription.expressionResultType = NSDecimalAttributeType;
NSArray *properties = [NSArray arrayWithObjects:#"date",aDescription, bDescription, nil];
[fetchRequest setPropertiesToFetch:properties];
NSArray *result = [temporaryContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"Array result is : %#",result);
When i run the above query i get sum of column a, column b and distinct dates. The problem is it will not add up the sum for a distinct date, instead it will add up all the values in the column a and b.
In simple words i want to get results as in this question Link but in core data. Any help would be greatly appreciated.
Try setting the fetch request to group:
[fetchRequest setPropertiesToGroupBy:#[ #"date" ]];
I searched high and low but I couldn't find exactly what I was looking for. My question is similar to this, but slightly different:
Core Data - Count of Related Records
Let's say I have a Car entity which has a one to many relation with a Person entity. This means that the car could have multiple people driving it, but each person drives only one car.
I want to be able to execute only one predicate wherein I could achieve the following:
All cars which are 'red'.
Return only the 'Year' and 'Color' attributes of the matching car.
Return a count of how many people are driving this car (i.e the size of the NSSet of People inside each resulting Car).
Is it possible to do all this with one query?
I know how to do this with multiple queries. I would just use setPropertiesToFetch and use a filtered predicate to achieve 1 and 2 above. I would then perform another count query (countForFetchRequest) on the Persons entity for every car to find how many Person(s) drive each car.
The key is the 3rd requirement above. I want to do everything in one predicate and I don't want to bring all of the Person entity objects into memory (performance) on the initial query. Furthermore it hurts to call another countForFetchRequest query for each car.
What's the best way to do this?
Thanks!
I cannot test this at the moment, but that should be possible by adding the following expression description to the "properties to fetch":
NSExpression *countExpression = [NSExpression expressionForFunction: #"count:" arguments: [NSArray arrayWithObject:[NSExpression expressionForKeyPath: #"drivers"]]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: #"driversCount"];
[expressionDescription setExpression: countExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
Return only 'red' cars:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"color LIKE 'red'"];
Return a count of how many people are driving this car:
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:#"people"];
NSExpression *countExpression = [NSExpression expressionForFunction:#"count:"
arguments:#[keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName:#"count"];
[expressionDescription setExpression:countExpression];
[expressionDescription setExpressionResultType:NSInteger32AttributeType];
Return only the 'year' and 'color' attributes (and the count):
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Car"
inManagedObjectContext:context];
NSDictionary *attributes = [entity attributesByName];
NSArray *properties = #[expressionDescription, attributes[#"year"], attributes[#"color"]];
Build and execute the fetch request:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setResultType:NSDictionaryResultType];
[request setPropertiesToFetch:properties]; // return only count, year & color
[request setPredicate:predicate]; // return only red cars
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
Process the results:
if (results) {
for (NSDictionary *result in results) {
NSLog(#"Year: %#", result[#"year"]);
NSLog(#"Color: %#", result[#"color"]);
NSLog(#"Drivers: %#", result[#"count"]);
}
}
else {
NSLog(#"Error: %#", error);
}
For the life of me I can not seem to get this to work.
Assume our entity is an managed object with a status field and an order field.
How would I go about getting all orderedEntries having more than one order that are the same?
Please no answers telling me to just do a subquery with #count in the main predicate, since I know of that solution, the point of this post is to understand how to use the having predicate in core data, which would probably be faster than a subquery anyways. (unless you explain why I can not use a having clause)
The following code would return an array of dictionaries with the number of orders per order number. What I want is to be able to add a having clause to restrict my request to only return the dictionaries representing objects of those orders that have a count greater than 1.
Here is the code so far and my attempts at a having predicate:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"OrderedEntry"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setResultType:NSDictionaryResultType];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"(status == %#)",[NSNumber numberWithInt:EntryStatusAlive]];
[fetchRequest setPredicate:predicate];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: #"order"]; // Does not really matter
NSExpression *maxExpression = [NSExpression expressionForFunction: #"count:"
arguments: [NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: #"orderCount"];
[expressionDescription setExpression: maxExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
[fetchRequest setPropertiesToFetch: [NSArray arrayWithObjects:expressionDescription,#"order",nil]];
[fetchRequest setPropertiesToGroupBy:[NSArray arrayWithObjects:#"order",nil]];
//[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:#"#count > 1"]];
//[fetchRequest setHavingPredicate:[NSComparisonPredicate predicateWithLeftExpression:maxExpression rightExpression:[NSExpression expressionForConstantValue:[NSNumber numberWithInteger:1]] modifier:NSDirectPredicateModifier type:NSGreaterThanPredicateOperatorType options:NSCaseInsensitivePredicateOption]];
NSError *error;
NSArray * array = [context executeFetchRequest:fetchRequest error:&error];
I ended up going with this for anyone interested
-(BOOL)ordersAreSaneOnDay:(NSNumber*)dayNumber forUser:(User*)user inContext:(NSManagedObjectContext*)context {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"BasicEntry"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setResultType:NSDictionaryResultType];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"(status == %#) && ((type != %#) && (type != %#) && (dayNumber == %#)) && ((user == NIL) || (user == %#))",[NSNumber numberWithInt:EntryStatusAlive],[NSNumber numberWithInt:EntryTypeTask],[NSNumber numberWithInt:EntryTypeCompletedTask],dayNumber,user];
[fetchRequest setPredicate:predicate];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: #"order"]; // Does not really matter
NSExpression *maxExpression = [NSExpression expressionForFunction: #"count:"
arguments: [NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: #"orderCount"];
[expressionDescription setExpression: maxExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
[fetchRequest setPropertiesToFetch: [NSArray arrayWithObjects:expressionDescription,#"order",nil]];
[expressionDescription release];
[fetchRequest setPropertiesToGroupBy:[NSArray arrayWithObjects:#"order",nil]];
//[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:#"self.order.#count > 1"]];
//[fetchRequest setHavingPredicate:[NSComparisonPredicate predicateWithLeftExpression:maxExpression rightExpression:[NSExpression expressionForConstantValue:[NSNumber numberWithInteger:1]] modifier:NSDirectPredicateModifier type:NSGreaterThanPredicateOperatorType options:NSCaseInsensitivePredicateOption]];
NSError *error;
NSArray * array = [context executeFetchRequest:fetchRequest error:&error];
array = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"orderCount > 1"]];
//NSLog(#"it worked %#",array);
[fetchRequest release];
if ([array count]) return FALSE;
return TRUE;
}
I've got this working using the following:
[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:#"$orderCount > 1"]];
Use the name of your expressionDecription as variable $orderCount.
Alternatively you can use
NSExpression *countExpression = [NSExpression expressionForVariable:#"orderCount"];
[fetchRequest setHavingPredicate:[NSPredicate predicateWithFormat:#"%# > 1", countExpression]];
Firstly, whenever I try something analogous to what you're doing, I get an error that you can't pass a to-many relationship to setPropertiesToFetch:. The NSFetchRequest documentation backs this up: "The property descriptions may represent attributes, to-one relationships, or expressions." So that's problem #1.
Problem #2 is that it appears that you can't group by a to-many relationship either (this isn't made clear in the documentation, but you get the same error and it also makes sense).
Remove "order" from the properties to fetch. Group by an attribute. Modify your main predicate to only include those attributes you group by (or just remove it). Specify "order" in your having predicate.
[fetchRequest setPropertiesToGroupBy: #[ #"???" ]];
[fetchRequest setHavingPredicate: [NSPredicate predicateWithFormat: #"self.order.#count > 1"]];
Now you'll see the request will work, but the results probably weren't what you were expecting:
- NSArray
- NSDictionary { status = #"alive", orderCount = "4" }
- NSDictionary { status = #"alive", orderCount = "9" }
- NSDictionary { status = #"alive", orderCount = "2" }
- etc...
NSDictionaryResultType doesn't actually give you anything to identify those objects by - it just gives you the values.
So your next step is to get back IDs for those OrderedEntry objects. The trick is to include an expression which will give you back the NSManagedObjectID as a key in each dictionary.
I don't know if this will actually give you improved performance at all (over just AND-ing it in to the main predicate). In my experience, one of the best things you can do to improve fetching performance is to create singleton NSPredicates and use substitution variables to set up each fetch. Predicate parsing can be expensive.
Dictionary result types can be a pain. Usually just constructing a good predicate is better. I tend only to use them for expressions (i.e. performing statistic-type calculations on the graph which return a value). If you look at all the restrictions around properties to fetch and group by and the predicate restrictions, this seems to be what Apple intend it for. I'm not even sure it's possible to do what you want to do by grouping and using a having predicate - for example, what are you going to group by? If by status (which you need to group by to include in your predicate), won't that collapse all the OrderEntries and you won't get the separate objectIDs and orderCounts? It's best to stick to the main predicate for this, not grouping and having predicates.