NSFetchRequest: запрос различных значений и подсчет значений других свойств.

Я пытаюсь настроить NSFetchRequest для субъекта Location с такими свойствами, как countryа также city:

country  | city
————————————————
Germany  | Berlin
USA      | San Francisco
USA      | New York
Germany  | Munich
Germany  | Munich
USA      | San Francisco
Germany  | Stuttgart

NSFetchRequest должен вернуть страну (или объект Location с соответствующей страной) и количество городов.

[
    { country: 'Germany', cityCount: 3 },
    { country: 'USA', cityCount: 2 }
]

Я знаю, что могу просто получить все записи и "посчитать это сам", но меня интересует, как настроить соответствующий запрос на выборку (или, если это возможно, сделать это), и хотел бы посмотреть, как вы это сделаете!:)

2 ответа

Решение

Правильный ответ на этот вопрос для рефакторинга модели данных во избежание избыточности.

Строки страны повторяются без необходимости в таблице. Кроме того, вы делаете простой запрос безвозмездно сложным. Модель должна отражать ваши данные, и выписывание "США" для каждого американского города просто не умно и не эффективно.

Ваша модель данных должна выглядеть так

Country <----->> City

Теперь вы можете просто выбрать все страны и получить города с cities.count,

Мне пришлось прибегнуть к двум отдельным выборкам, чтобы добиться (я думаю) того, что вы хотите. Первая выборка получает идентификаторы объекта для одного объекта для каждой отдельной комбинации country а также city, Вторая выборка фильтруется, используя IN предикат, только к этим объектам. Оно использует NSExpression а также propertiesToGroupBy чтобы получить счет для каждого country:

    // Step 1, get the object IDs for one object for each distinct country and city 
    var objIDExp = NSExpression(expressionType: NSExpressionType.EvaluatedObjectExpressionType)
    var objIDED = NSExpressionDescription()
    objIDED.expression = objIDExp
    objIDED.expressionResultType = .ObjectIDAttributeType
    objIDED.name = "objID"
    var fetch = NSFetchRequest(entityName: "Location")
    fetch.propertiesToFetch = [objIDED]
    fetch.propertiesToGroupBy = ["country", "city"]
    fetch.resultType = .DictionaryResultType
    let results = self.managedObjectContext!.executeFetchRequest(fetch, error: nil)

    // extract the objectIDs into an array...
    let objIDArray = (results! as NSArray).valueForKey("objID") as! [NSManagedObjectID];

    // Step 2, count using GROUP BY
    var countExp = NSExpression(format: "count:(SELF)")
    var countED = NSExpressionDescription()
    countED.expression = countExp
    countED.expressionResultType = .ObjectIDAttributeType
    countED.name = "count"
    var newFetch = NSFetchRequest(entityName: "Location")
    newFetch.predicate = NSPredicate(format: "SELF IN %@", objIDArray)
    newFetch.propertiesToFetch = ["country", countED]
    newFetch.propertiesToGroupBy = ["country"]
    newFetch.resultType = .DictionaryResultType
    let newResults = self.managedObjectContext!.executeFetchRequest(newFetch, error: nil)
    println("\(newResults!)")

Это будет неэффективно: если у вас большое количество разных стран и городов, предикат IN замедлит процесс. Вы можете найти, что более эффективно выбрать все и посчитать их.

Другие вопросы по тегам