Twelveg
Twelveg

Reputation: 3

iOS Core Data using CASE WHEN END equivalent for aggregate functions

--- 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

Answers (0)

Related Questions