是否可以在havingPredicate中使用group by进行CoreData获取(用于dupe检测)?

stu*_*ckj 5 core-data ios nsexpression

作为参考,我正在尝试解决的问题是有效地查找和删除可能包含大量条目的表中的重复项.

我正在使用的表名为PersistedDay,其中包含一个dayString对象(它是一个字符串.:-P).有更多列与此问题无关.我想找到任何有重复的PersistedDay.

在SQL中,这是您可以这样做的有效方法之一(仅供参考,我可以在CoreData支持SQLite DB上执行此查询):

SELECT ZDAYSTRING FROM ZPERSISTEDDAY GROUP BY ZDAYSTRING HAVING COUNT(ZDAYSTRING) > 1;
Run Code Online (Sandbox Code Playgroud)

这只返回具有重复项的dayStrings,然后您可以通过使用生成的日期字符串查询来获取这些对象的所有字段(您可以将其用作子查询以在一个请求中完成所有操作).

NSFetchRequest似乎也拥有所有必需的部分,但它似乎没有用.这是我试图做的事情:

NSManagedObjectContext *context = [self managedObjectContext];

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"PersistedDay" inManagedObjectContext:context];
[request setEntity:entity];

NSPropertyDescription* dayStringProperty = entity.propertiesByName[@"dayString"];

request.propertiesToFetch = @[dayStringProperty];
request.propertiesToGroupBy = @[dayStringProperty];
request.havingPredicate = [NSPredicate predicateWithFormat: @"dayString.@count > 1"];
request.resultType = NSDictionaryResultType;

NSArray *results = [context executeFetchRequest:request error:NULL];
Run Code Online (Sandbox Code Playgroud)

这不起作用.:-P如果我尝试在尝试进行提取时遇到错误"不支持的函数表达式计数:(dayString)".我不认为"dayString.@ count"中的dayString甚至在上面的代码中都很重要......但是,为了清楚起见,我把它放进去(SQL计数只对分组的行进行操作).

所以,我的问题是:这是可能的,如果是这样,它的语法是什么?我在CoreData文档中找不到任何指示如何执行此操作的内容.

我发现了一个类似的SO帖子,我现在很遗憾地再找不到那个关于在一个条款中运行计数(我不认为有一个组).但是,海报放弃了,并在找不到解决方案之后采取了不同的方式.我希望这更明确,所以也许有人有答案.:)

作为参考,这就是我现在正在做的事情,它可以工作,但需要返回几乎所有的行,因为在大多数情况下几乎没有重复:

NSManagedObjectContext *context = [self managedObjectContext];

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"PersistedDay"
                                          inManagedObjectContext:context];
[request setEntity:entity];

NSPropertyDescription* dayStringProperty = entity.propertiesByName[@"dayString"];

// Get the count of dayString...
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: @"dayString"]; // Does not really matter
NSExpression *countExpression = [NSExpression expressionForFunction: @"count:" arguments: [NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: @"dayStringCount"];
[expressionDescription setExpression: countExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];

request.propertiesToFetch = @[dayStringProperty, expressionDescription];
request.propertiesToGroupBy = @[dayStringProperty];
request.resultType = NSDictionaryResultType;

NSArray *results = [context executeFetchRequest:request error:NULL];
Run Code Online (Sandbox Code Playgroud)

然后,我必须循环结果并仅返回具有dayStringCount> 1的结果.这是having子句应该执行的操作.:-P

注意:我知道CoreData不是SQL.:)我想知道我是否可以以与SQL相同的效率执行等效类型的操作.

Ben*_*air 6

对的,这是可能的.您不能引用count为关键路径,但可以将其作为变量引用.就像在SQL中一样.在我的例子中,我创建了具有重复名称的城市.

let fetchRequest = NSFetchRequest(entityName: "City")

let nameExpr = NSExpression(forKeyPath: "name")
let countExpr = NSExpressionDescription()
let countVariableExpr = NSExpression(forVariable: "count")

countExpr.name = "count"
countExpr.expression = NSExpression(forFunction: "count:", arguments: [ nameExpr ])
countExpr.expressionResultType = .Integer64AttributeType

fetchRequest.resultType = .DictionaryResultType
fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "name", ascending: true) ]
fetchRequest.propertiesToGroupBy = [ cityEntity.propertiesByName["name"]! ]
fetchRequest.propertiesToFetch = [ cityEntity.propertiesByName["name"]!, countExpr ]

// filter out group result and return only groups that have duplicates
fetchRequest.havingPredicate = NSPredicate(format: "%@ > 1", countVariableExpr)
Run Code Online (Sandbox Code Playgroud)

完整的游乐场文件:https: //gist.github.com/pronebird/cca9777af004e9c91f9cd36c23cc821c


Dav*_*rry 5

我能想出的最好的是:

NSError*                error;

NSManagedObjectContext* context = self.managedObjectContext;
NSEntityDescription*    entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:context];

// Construct a count group field
NSExpressionDescription*    count = [NSExpressionDescription new];
count.name = @"count";
count.expression = [NSExpression expressionWithFormat:@"count:(value)"];
count.expressionResultType = NSInteger64AttributeType;

// Get list of all "value" fields (only)
NSPropertyDescription*  value = [entity propertiesByName][@"value"];

NSFetchRequest*         request = [[NSFetchRequest alloc] initWithEntityName:@"Event"];
request.propertiesToFetch = @[ value, count];
request.propertiesToGroupBy = @[ value ];
request.resultType = NSDictionaryResultType;
NSArray*                values = [context executeFetchRequest:request error:&error];

// Filter count > 1
values = [values filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"count > 1"]];

// slice to get just the values
values = [values valueForKeyPath:@"value"];
Run Code Online (Sandbox Code Playgroud)

但这与你正在使用的东西并没有太大的不同.