let fetchRequest = NSFetchRequest(entityName: "Product")
fetchRequest.predicate = NSPredicate(format: "shopItem.wishList == %@", currentWishList)
fetchRequest.resultType = .DictionaryResultType
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "mainBestPrice", ascending: true)]
let minPriceExpression = NSExpression(format: "min:(mainBestPrice)")
let minPriceED = NSExpressionDescription()
minPriceED.expression = minPriceExpression
minPriceED.name = "productPrice"
minPriceED.expressionResultType = .DoubleAttributeType
fetchRequest.propertiesToFetch = ["shopItem.keyword", "productTitle", minPriceED]
fetchRequest.propertiesToGroupBy = ["shopItem.keyword"]
I want to group by by one field with NSFetchRequest, but always get error:
CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. SELECT clauses in queries with GROUP BY components can only contain properties named in the GROUP BY or aggregate functions
that i should use all fields from propertiesToFetch in group by. But i don't want do this...
One option is to use an NSFetchedResultsController (see the Apple Docs). This is normally used to populate a table view, but can also be used to group the results of a CoreData fetch - the sectionNameKeyPath determines the grouping. The fetch must be sorted in a way that ensures that all items in a group are sorted together. In your example, you are grouping by shopItem.keyword, so you need to specify shopItem.keyword as the first sort descriptor. The second sort descriptor should be mainBestPrice (ascending), so that the first item in each group is the item with the lowest mainBestPrice for that group. Hence the configuration for the FRC would look something like this:
let fetchRequest = NSFetchRequest(entityName: "Product")
fetchRequest.predicate = NSPredicate(format: "shopItem.wishList == %@", currentWishList)
let keyWordSort = NSSortDescriptor(key: "shopItem.keyword", ascending: true)
let priceSort = NSSortDescriptor(key: "mainBestPrice", ascending: true)
fetchRequest.sortDescriptors = [keyWordSort, priceSort];
let myFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath:"shopItem.keyword", cacheName:nil)
do {
try myFetchedResultsController.performFetch()
} catch let error as NSError {
// Replace this implementation with code to handle the error appropriately.
print("Unresolved error \(error), \(error!.userInfo)")
abort()
}
Now, the products with the minimum best price will be the first item in each section, eg. for the first section (i.e. the first shopItem.keyword):
let firstProduct = myFetchedResultsController.sections![0].objects![0] as! Product
or to get an array of them (for all shopItem.keywords),
let products = myFetchedResultsController.sections!.map() { $0.objects![0] as! Product}
The NSFetchedResultsController will do its best to optimise for memory and speed, though you should take care if you have large volumes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With