如何添加“一对多”的“一对多”部分以获取结果

Posted

技术标签:

【中文标题】如何添加“一对多”的“一对多”部分以获取结果【英文标题】:How to add "one-to" part of "one-to-many" to fetch results 【发布时间】:2017-12-28 22:51:44 【问题描述】:

我希望能够将玩家数据添加到许多关系的“一对一”部分。 fetch 为我做了一些聚合,但我想知道 player 它属于什么。

我有一个如下所示的 CoreData 模型:

我有一个如下所示的获取请求:

func statsPerPlayer(player: Players, managedContext: NSManagedObjectContext)  -> [String: Int] 

    var resultsDic = [String: Int]()

    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Shifts")
    let predicate               = NSPredicate(format: "playersRelationship = %@", player)
    fetchRequest.predicate      = predicate

    let nsExpressionForKeyPath  = NSExpression(forKeyPath: "timeOnIce")

    let nsExpressionForFunctionMin = NSExpression(forFunction: "min:", arguments: [nsExpressionForKeyPath])
    let nsExpressionDescriptionMin = NSExpressionDescription()
    nsExpressionDescriptionMin.expression = nsExpressionForFunctionMin
    nsExpressionDescriptionMin.name = "minShift"
    nsExpressionDescriptionMin.expressionResultType = .integer16AttributeType

    let nsExpressionForFunctionMax = NSExpression(forFunction: "max:", arguments: [nsExpressionForKeyPath])
    let nsExpressionDescriptionMax = NSExpressionDescription()
    nsExpressionDescriptionMax.expression = nsExpressionForFunctionMax
    nsExpressionDescriptionMax.name = "maxShift"
    nsExpressionDescriptionMax.expressionResultType = .integer16AttributeType

    let nsExpressionForFunctionSum = NSExpression(forFunction: "sum:", arguments: [nsExpressionForKeyPath])
    let nsExpressionDescriptionSum = NSExpressionDescription()
    nsExpressionDescriptionSum.expression = nsExpressionForFunctionSum
    nsExpressionDescriptionSum.name = "sumShift"
    nsExpressionDescriptionSum.expressionResultType = .integer16AttributeType

    let nsExpressionForFunctionAvg = NSExpression(forFunction: "average:", arguments: [nsExpressionForKeyPath])
    let nsExpressionDescriptionAvg = NSExpressionDescription()
    nsExpressionDescriptionAvg.expression = nsExpressionForFunctionAvg
    nsExpressionDescriptionAvg.name = "avgShift"
    nsExpressionDescriptionAvg.expressionResultType = .integer16AttributeType

    let nsExpressionForFunctionCount = NSExpression(forFunction: "count:", arguments: [nsExpressionForKeyPath])
    let nsExpressionDescriptionCount = NSExpressionDescription()
    nsExpressionDescriptionCount.expression = nsExpressionForFunctionCount
    nsExpressionDescriptionCount.name = "countShift"
    nsExpressionDescriptionCount.expressionResultType = .integer16AttributeType

    fetchRequest.propertiesToFetch   = [nsExpressionDescriptionMin, nsExpressionDescriptionMax, nsExpressionDescriptionSum, nsExpressionDescriptionAvg, nsExpressionDescriptionCount]

    fetchRequest.resultType = .dictionaryResultType

    do 

        let fetchArray = try managedContext.fetch(fetchRequest)
        print(fetchArray)
        resultsDic = fetchArray.first as! [String : Int]

     catch let error as NSError 

        print("\(self) -> \(#function): Could not fetch. \(error), \(error.userInfo)")
    

    return resultsDic

  //statsPerPlayer

结果看起来很棒,如下所示:

[
    avgShift = 39;
    countShift = 4;
    maxShift = 89;
    minShift = 6;
    sumShift = 157;
]

但是,我想包含此数据所针对的player。如何在结果中添加one-to-many 关系的"to-one" 部分?

谢谢!!

【问题讨论】:

如果只将“playersRelationship”添加到 propertiesToFetch 数组会发生什么? 如果我添加player.playersShiftRelationship! 我得到unrecognized selector sent to instance 如果我添加shifts.playersRelationship! 我得到同样的错误。 您正在获取 Shifts,因此您需要在 Shifts 实体上指定关系的名称,即“playersRelationship”。 我想我按照你的建议去做:let entity = NSEntityDescription.entity(forEntityName: "Shifts", in: managedContext) let shifts = Shifts(entity: entity!, insertInto: managedContext) 【参考方案1】:

在您上面的代码中,只需将相关的关系名称添加到要获取的属性中:

fetchRequest.propertiesToFetch   = ["playersRelationship", nsExpressionDescriptionMin, nsExpressionDescriptionMax, nsExpressionDescriptionSum, nsExpressionDescriptionAvg, nsExpressionDescriptionCount]

然后返回的字典将包含键“playersRelationship”,其值设置为相应Players 对象的NSManagedObjectID。然后,您可以使用上下文的 object(with:) 方法来访问 Players 对象本身。

更新

所以经过一些测试,结果是:

a) 如果您在 propertiesToFetch 中包含关系,CoreData 会对 count 聚合函数感到困惑。这会导致 Invalid keypath (request for aggregate operation on a toOne-only keypath 错误。

b) 如果您在 propertiesToFetch 中包含关系,CoreData 会混淆所有其他聚合函数。 (它计算每个对象的聚合,而不仅仅是那些与谓词匹配的对象。)

解决这两个问题的方法是将关系添加为 GROUP BY 属性。然后,CoreData 会正确计算聚合,并将 count 正确识别为有效操作。因此,添加以下行:

fetchRequest.propertiesToGroupBy   = ["playersRelationship"]

【讨论】:

我收到以下错误:[error] error: exception handling request: &lt;NSSQLFetchRequestContext: 0x60000018ec70&gt; , Invalid keypath (request for aggregate operation on a toOne-only keypath): timeOnIce with userInfo of (null) @PaulS。我不明白为什么会这样;你能用你当前的代码和错误编辑你的原始帖子吗? 当前代码与贴出的完全相同,错误也一样。 @pbasdf 干得好,谢谢。这可能是我唯一没有尝试过的组合。您是否知道一种通过我的所有Players 运行上述内容的快速方法,而无需另一个Players fetch 然后 for in 循环? 只需删除谓词 - 返回的数组将为每个玩家提供一个字典。

以上是关于如何添加“一对多”的“一对多”部分以获取结果的主要内容,如果未能解决你的问题,请参考以下文章

如何从一对多单向关系中获取“多”部分中的对象?

excel如何实现一对多查询

核心数据 - 以一对多关系访问实例与获取请求?

NSPredicate,使用一对多关系的子集获取结果

如何获取所有对象A的一对多关系B的所有属性的NSSet

MyBatis从入门到精通:MyBatis高级结果映射之一对多映射