Reputation: 3
--- Summary of the Question ---
I have an Entity in Core Data containing the Properties:
name: String
owned: Boolean
price: UInt16
How do I do a Fetch Request in one go (without post processing), to obtain a result collection (Dictionary, as Core Data returns with Aggregate functions), which meets the following description (example of one item in the returned array dictionary):
{
name = <entity.name>;
count = <how many identical entity.name I have>
ownednb = <how many of the identical entity.name have the entity.owned property set>
expensivenb = <how many of the identical entity.name have price > 20 >
}
From above I solved almost everything, except the "expensivenb" part, see below:
--- What I did, how I did, and what is already working ---
I want to implement the equivalent of the following SQL SELECT with Core Data Fetch Request (in Swift preferably). Most of the parts of it I have solved, only the part below the code I am still missing, and have no idea how to combine NSPredicates / NSExpressions to achieve that, or if it is even possible.
I have an Entity in Core Data containing the Properties (as shown in the "play" SQL):
name: String
owned: Boolean
price: UInt16
Intention is to aggregate the Fetch Request to Core Data, based on certain Rules on the Properties.
I did a sample of an SQL script to see if this I can do this with normal queries in SQL, and to visualize the Result (running and working, the example can be executed in https://www.programiz.com/sql/online-compiler/ or any other online SQL simulator, to see the intended Result):
-- create a table -- this part is handled by the Core Data Entity
CREATE TABLE cards (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
owned bool NOT NULL,
price INTEGER NOT NULL
);
-- insert some values -- this part is handled by the UI (User Input)
INSERT INTO cards VALUES (1, 'Liliana', 1, 24);
INSERT INTO cards VALUES (2, 'Jace', 0, 23);
INSERT INTO cards VALUES (3, 'Liliana', 0, 18);
INSERT INTO cards VALUES (4, 'Jace', 1, 16);
INSERT INTO cards VALUES (5, 'Liliana', 0, 25);
INSERT INTO cards VALUES (6, 'Liliana', 1, 29);
-- fetch some values - this is the Fetch Request to the Core Data
SELECT name, COUNT(name) as totalnb, SUM(owned > 0) as ownednb, SUM(CASE WHEN price > 20 THEN 1 ELSE 0 END) as expensivenb FROM cards GROUP BY name
The problematic part is: SUM(CASE WHEN price > 20 THEN 1 ELSE 0 END) to translate to the Fetch Request as an Aggregate Function with a Condition inside. I do not want to add the Condition as NSPredicate (WHERE) of the Fetch Request - as that would limit the Results; I just want to aggregate by the "transformed" Property.
The code in Swift for the Fetch Request (SELECT of the SQL Query) I also have working (except the missing part):
The Fetch Request:
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "CardData")
Aggregation for COUNT(name) as totalnb:
let keyPathCardName = NSExpression(forKeyPath: "name")
let cardCountExpression = NSExpression(forFunction: "count:", arguments: [keyPathCardName])
let cardCountDescriptor = NSExpressionDescription()
cardCountDescriptor.expression = cardCountExpression
cardCountDescriptor.name = "totalnb"
cardCountDescriptor.expressionResultType = .integer64AttributeType
Aggregation for SUM(owned > 0) as ownednb:
let keyPathOwned = NSExpression(forKeyPath: "owned")
let cardOwnedCountExpression = NSExpression(forFunction: "sum:", arguments: [keyPathOwned])
let cardOwnedCountDescriptor = NSExpressionDescription()
cardOwnedCountDescriptor.expression = cardOwnedCountExpression
cardOwnedCountDescriptor.name = "ownednb"
cardOwnedCountDescriptor.expressionResultType = .integer64AttributeType
Aggregation for SUM(CASE WHEN price > 20 THEN 1 ELSE 0 END) as expensivenb:
// >> This is the part I am missing, but I expect it should be something like the chunk above for the SUM(owned > 0) as ownednb, but with a Condition somehow added inside. <<
And the remaining part of the Fetch Request:
request.propertiesToFetch = ["name", cardCountDescriptor, cardOwnedCountDescriptor]
request.propertiesToGroupBy = ["name"]
request.resultType = .dictionaryResultType
do {
let result = try context.fetch(request)
print("\(result)")
} catch {
print("Error: Failed fetching all Card Data from Context \(error)")
}
Any help would be appreciated.
Thanks, Andrei
Upvotes: 0
Views: 116