基于谓词添加属性的CoreData获取



我有以下数据模型(简化):

WordEntity          ListItemEntity          ListEntity
----------          --------------          ----------
text                                        name
----------          --------------          ----------
listItems <------>> word
                    list <<---------------> items

以及以下基本查询:

let fetchRequest = NSFetchRequest(entityName: "WordEntity")
let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
let list = <ListEntity instance>
... do something clever here ...
controller.performFetch(nil)

我需要获取的结果为每个对象包含一个属性inList,如果WordEntity对象附加到ListItemEntity,而ListItemEntity又附加到list,那么这将是正确的。类似于下面的谓词,只是用于创建一个新属性而不是过滤提取请求:

NSPredicate(format: "ANY listItems.list == %@", list)

我看了NSEexpressions,但它们似乎只用于聚合,不能做谓词。SUBQUERY可以执行谓词,但仅用于筛选结果集。计算的瞬态属性可以进行我想要的任何查找,但它们无法对外部值进行操作。。。

我希望这是清楚的。。。提前谢谢。

更新:

正如pbasdf所建议的那样,我可以使用提取的属性来完成这项工作。我在具有谓词的WordEntity上获取了属性listItemsInList

(word == $FETCH_SOURCE) AND (list == $FETCHED_PROPERTY.userInfo.list)

然后在代码中:

let request = NSFetchRequest()
let entity = NSEntityDescription.entityForName("WordEntity", inManagedObjectContext: context)!
request.entity = entity
for property in entity.properties {
    if property.name == "listItemsInList" {
        let list = <ListEntity instance>
        (property as NSFetchedPropertyDescription).userInfo!["list"] = list
    }
}

最后:

if word.listItemsInList.count > 0 {
    ... this is what I was looking for ...
}

这是有效的。不幸的是,它的效率非常低。提取的属性总是返回提取对象的数组,而不是计算值。他们总是把整件东西拿出来。最糟糕的是,它们不能被预取,所以检查表单元格中的属性意味着每一行都会命中数据库。所以我仍然希望有一种更聪明的方法。

经过一番尝试和错误,我找到了一个解决方案,只要您对实体的属性在一组字典中"按值"返回(即它们将是底层属性的副本)感到满意,就可以通过单个CoreData获取来实现您想要的结果。一个可取之处是字典可以包括相关对象的CoreData对象ID,因此检索该对象相对简单。

诀窍是使用一个NSExpression,它包括一个SUBQUERY(在中替换了相关的列表名称)和@count。这是我用过的(对不起,又是Obj-C!):

// First, get an NSAttribute description for each of the attributes we want in our query
NSEntityDescription* entity = [NSEntityDescription entityForName:@"WordEntity" inManagedObjectContext:self.context];
NSAttributeDescription *wordDesc = [entity.attributesByName objectForKey:@"word"];
// Also get an NSExpression description for the object itself
NSExpression *objIDExpression = [NSExpression expressionForEvaluatedObject];
NSExpressionDescription *objIDDescription = [[NSExpressionDescription alloc] init];
[objIDDescription setName: @"cdObjectID"];
[objIDDescription setExpression: objIDExpression];
[objIDDescription setExpressionResultType: NSObjectIDAttributeType];
// Define an expression to count a subquery
NSExpression *countExpression = [NSExpression expressionWithFormat:@"SUBQUERY(listItems,$x,$x.list.listName like %@).@count",@"Animals",nil]; 
// Note, in the above, replace @"Animals" with list.listName for the list of interest
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: @"count"];
[expressionDescription setExpression: countExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
NSFetchRequest* fetch = [NSFetchRequest fetchRequestWithEntityName:@"WordEntity"];
[fetch setPropertiesToFetch:@[wordDesc, objIDDescription, expressionDescription]];
[fetch setResultType:NSDictionaryResultType];
NSArray *myResults = [self.context executeFetchRequest:fetch error:&error];
NSLog(@"myResults: %@",myResults);
// Recover the actual WordEntity object for the first item in myResults:
WordEntity *myWord = (WordEntity *)[self.context objectWithID:[[myResults firstObject] valueForKey:@"cdObjectID"]];

myResults的输出如下所示。我的测试数据包括我在名为"动物"、"食物"等的列表中包含的各种单词。下面的输出是针对动物的;注意计数=1表示Dog!

    {
    cdObjectID = "0xd000000000040004 <x-coredata://4B6BC796-CAEF-4B0C-9B27-BC6CCDA572C7/WordEntity/p1>";
    count = 0;
    word = Orange;
},
    {
    cdObjectID = "0xd000000000080004 <x-coredata://4B6BC796-CAEF-4B0C-9B27-BC6CCDA572C7/WordEntity/p2>";
    count = 0;
    word = Geranium;
},
    {
    cdObjectID = "0xd0000000000c0004 <x-coredata://4B6BC796-CAEF-4B0C-9B27-BC6CCDA572C7/WordEntity/p3>";
    count = 0;
    word = Banana;
},
    {
    cdObjectID = "0xd000000000100004 <x-coredata://4B6BC796-CAEF-4B0C-9B27-BC6CCDA572C7/WordEntity/p4>";
    count = 1;
    word = Dog;
},
    {
    cdObjectID = "0xd000000000140004 <x-coredata://4B6BC796-CAEF-4B0C-9B27-BC6CCDA572C7/WordEntity/p5>";
    count = 0;
    word = Apple;
},
    {
    cdObjectID = "0xd000000000180004 <x-coredata://4B6BC796-CAEF-4B0C-9B27-BC6CCDA572C7/WordEntity/p6>";
    count = 1;
    word = Elephant;
}, ....

我不知道这将如何与其他解决方案在性能方面进行比较!

最新更新