NSPredicate 和 Core Data 多 table 查询
NSPredicate and Core Data multi-table queries
我有一个 SQL 命令,用普通的 SQL 构建起来非常简单,但我在使用 Core Data 时遇到了很多麻烦。
我有 3 个实体;项目、文件夹和组。项目在文件夹中,文件夹在组中。通过文件夹名称和组名称使文件夹唯一。项目只存储文件夹名称,而文件夹存储文件夹名称和组名称。
有问题的SQL命令大致是这样的:SELECT ItemID FROM Item WHERE Item.folderName = folderName AND Folder.folderName = Item.folderName AND Folder.groupName = groupName;
(是的,我知道我没有获得 CoreData 的特定属性,我只是为了示例而这样做)
当我执行 Folder.folderName = Item.folderName AND Folder.groupName = groupName
部分时出现问题。我不知道如何将其表示为谓词。
我应该注意到,我在 Item 和 Folder 之间有关系,在 Folder 和 Group 之间也有关系。
编辑:
这是我的数据模型的设置:
这是我现在用来发出请求的代码:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"FavouriteItem" inManagedObjectContext:context];
[request setEntity:entity];
request.predicate = [NSPredicate predicateWithFormat:@"folderName = %@ AND folderRelationship.groupName = %@", self.folderName, self.groupName];
NSError *fetchError;
NSArray *results = [context executeFetchRequest:request error:&fetchError];
所以,基本上,您想要获取特定文件夹中的所有项目(即,您想要所有具有给定文件夹名称和组名称的项目...它们唯一标识特定文件夹)。
既然你说你已经有了关系,我会假设他们是建立在反向关系上的。
此外,我将假设一个项目具有名为 "folder" 的一对一关系,即该项目所在的文件夹。该文件夹将具有一对多关系 "items"该文件夹中的项目。
因此,以下是实现您想要的目标的众多方法之一。
- (NSSet*)getItemsForFolderName:(NSString*)folderName
groupName:(NSString*)groupName
inMOC:(NSManagedObjectContext*)moc
error:(NSError**)error {
NSSet *result = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Folder"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"folderName = %@ AND groupName = %@", folderName, groupName];
fetchRequest.relationshipKeyPathsForPrefetching = @[@"items"];
NSArray *folders = [moc executeFetchRequest:fetchRequest error:error];
if (folders) {
result = [[folders firstObject] items];
}
return result;
}
您也可以简单地获取项目。至于哪个最好,这取决于你如何为你的属性建立索引,以及你正在做什么类型的其他抓取。
- (NSArray*)getItemsForFolderName:(NSString*)folderName
groupName:(NSString*)groupName
inMOC:(NSManagedObjectContext*)moc
error:(NSError**)error {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Item"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"folderName = %@ AND folder.groupName = %@", folderName, groupName];
return [moc executeFetchRequest:fetchRequest error:error];
}
编辑
JamEngulfer221,我很确定这些例子会起作用,但既然你说即使我指出了你 post 中的错误,它们也不起作用,我想我会快速搞定一个项目并且测试...我老了,有时忘记东西...
所以,我用下面的代码破解了一个测试。
首先,创建模型...
- (NSManagedObjectModel *)makeModel {
NSEntityDescription *group = [[NSEntityDescription alloc] init];
group.name = @"Group";
NSAttributeDescription *groupName = [[NSAttributeDescription alloc] init];
groupName.name = @"groupName";
groupName.attributeType = NSStringAttributeType;
NSEntityDescription *folder = [[NSEntityDescription alloc] init];
folder.name = @"Folder";
NSAttributeDescription *folderName = [[NSAttributeDescription alloc] init];
folderName.name = @"folderName";
folderName.attributeType = NSStringAttributeType;
NSEntityDescription *item = [[NSEntityDescription alloc] init];
item.name = @"Item";
NSAttributeDescription *itemName = [[NSAttributeDescription alloc] init];
itemName.name = @"itemName";
itemName.attributeType = NSStringAttributeType;
NSRelationshipDescription *folderToGroupRelationship = [[NSRelationshipDescription alloc] init];
NSRelationshipDescription *groupToFoldersRelationship = [[NSRelationshipDescription alloc] init];
groupToFoldersRelationship.name = @"folders";
groupToFoldersRelationship.destinationEntity = folder;
groupToFoldersRelationship.minCount = 0;
groupToFoldersRelationship.maxCount = 0;
groupToFoldersRelationship.deleteRule = NSCascadeDeleteRule;
groupToFoldersRelationship.inverseRelationship = folderToGroupRelationship;
folderToGroupRelationship.name = @"group";
folderToGroupRelationship.destinationEntity = group;
folderToGroupRelationship.minCount = 0;
folderToGroupRelationship.maxCount = 1;
folderToGroupRelationship.deleteRule = NSNullifyDeleteRule;
folderToGroupRelationship.inverseRelationship = groupToFoldersRelationship;
NSRelationshipDescription *folderToItemsRelationship = [[NSRelationshipDescription alloc] init];
NSRelationshipDescription *itemToFolderRelationship = [[NSRelationshipDescription alloc] init];
folderToItemsRelationship.name = @"items";
folderToItemsRelationship.destinationEntity = item;
folderToItemsRelationship.minCount = 0;
folderToItemsRelationship.maxCount = 0;
folderToItemsRelationship.deleteRule = NSCascadeDeleteRule;
folderToItemsRelationship.inverseRelationship = itemToFolderRelationship;
itemToFolderRelationship.name = @"folder";
itemToFolderRelationship.destinationEntity = folder;
itemToFolderRelationship.minCount = 0;
itemToFolderRelationship.maxCount = 1;
itemToFolderRelationship.deleteRule = NSNullifyDeleteRule;
itemToFolderRelationship.inverseRelationship = folderToItemsRelationship;
group.properties = @[groupName, groupToFoldersRelationship];
folder.properties = @[folderName, groupName, folderToGroupRelationship, folderToItemsRelationship];
item.properties = @[itemName, folderName, itemToFolderRelationship];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] init];
model.entities = @[group, folder, item];
return model;
}
以及搜索方法...
- (NSArray*)getItemsForFolderName:(NSString*)folderName
groupName:(NSString*)groupName
inMOC:(NSManagedObjectContext*)moc
error:(NSError**)error {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Item"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"folderName = %@ AND folder.groupName = %@", folderName, groupName];
return [moc executeFetchRequest:fetchRequest error:error];
}
还有测试...
- (void)testBlarg {
NSManagedObjectModel *model = [self makeModel];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
[psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:NULL];
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
moc.persistentStoreCoordinator = psc;
for (int g = 0; g < 10; ++g) {
NSManagedObject *group = [NSEntityDescription insertNewObjectForEntityForName:@"Group" inManagedObjectContext:moc];
NSString *groupName = [NSString stringWithFormat:@"Group %02d", g];
[group setValue:groupName forKey:@"groupName"];
for (int f = 0; f < 10; ++f) {
NSManagedObject *folder = [NSEntityDescription insertNewObjectForEntityForName:@"Folder" inManagedObjectContext:moc];
NSString *folderName = [NSString stringWithFormat:@"Folder %02d", f];
[folder setValue:folderName forKey:@"folderName"];
[folder setValue:groupName forKey:@"groupName"];
[folder setValue:group forKey:@"group"];
for (int i = 0; i < 10; ++i) {
NSManagedObject *item = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:moc];
NSString *itemName = [NSString stringWithFormat:@"Item %02d (%@;%@)", i, folderName, groupName];
[item setValue:itemName forKey:@"itemName"];
[item setValue:folderName forKey:@"folderName"];
[item setValue:folder forKey:@"folder"];
}
}
}
[moc save:NULL];
[moc reset];
NSString *folderName = @"Folder 04";
NSString *groupName = @"Group 02";
NSArray *items = [self getItemsForFolderName:folderName groupName:groupName inMOC:moc error:NULL];
items = [items sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"itemName" ascending:YES]]];
XCTAssertEqual(10, items.count);
for (int i = 0; i < 10; ++i) {
NSManagedObject *object = items[i];
NSString *expectedItemName = [NSString stringWithFormat:@"Item %02d (%@;%@)", i, folderName, groupName];
XCTAssertEqualObjects(expectedItemName, [object valueForKey:@"itemName"]);
NSManagedObject *folder = [object valueForKey:@"folder"];
XCTAssertEqualObjects(folderName, [folder valueForKey:@"folderName"]);
XCTAssertEqualObjects(groupName, [folder valueForKey:@"groupName"]);
NSManagedObject *group = [folder valueForKey:@"group"];
XCTAssertEqualObjects(groupName, [group valueForKey:@"groupName"]);
}
}
我认为您的数据模型设置有缺陷,导致了这种混乱。其实你的问题很简单。
首先,如果一个项目被分配到一个文件夹您不需要存储文件夹名称 - 这将是多余的,因此是不必要的信息。这同样适用于文件夹和组名称。
其次,父文件夹应该是同一实体的自反关系,而不是属性。
所有实体都应该只有一个 name
属性和这些关系(注意直观的非技术关系名称):
Group (folders) <------>> (group) Folder (items) <------->> (folder) Item
Folder (subFolders) <------>> (parentFolder) Folder
谓词现在很简单:
[NSPredicate predicateWithFormat:
@"folder.name = %@ && folder.group.name = %@", folderName, groupName]
我有一个 SQL 命令,用普通的 SQL 构建起来非常简单,但我在使用 Core Data 时遇到了很多麻烦。
我有 3 个实体;项目、文件夹和组。项目在文件夹中,文件夹在组中。通过文件夹名称和组名称使文件夹唯一。项目只存储文件夹名称,而文件夹存储文件夹名称和组名称。
有问题的SQL命令大致是这样的:SELECT ItemID FROM Item WHERE Item.folderName = folderName AND Folder.folderName = Item.folderName AND Folder.groupName = groupName;
(是的,我知道我没有获得 CoreData 的特定属性,我只是为了示例而这样做)
当我执行 Folder.folderName = Item.folderName AND Folder.groupName = groupName
部分时出现问题。我不知道如何将其表示为谓词。
我应该注意到,我在 Item 和 Folder 之间有关系,在 Folder 和 Group 之间也有关系。
编辑:
这是我的数据模型的设置:
这是我现在用来发出请求的代码:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"FavouriteItem" inManagedObjectContext:context];
[request setEntity:entity];
request.predicate = [NSPredicate predicateWithFormat:@"folderName = %@ AND folderRelationship.groupName = %@", self.folderName, self.groupName];
NSError *fetchError;
NSArray *results = [context executeFetchRequest:request error:&fetchError];
所以,基本上,您想要获取特定文件夹中的所有项目(即,您想要所有具有给定文件夹名称和组名称的项目...它们唯一标识特定文件夹)。
既然你说你已经有了关系,我会假设他们是建立在反向关系上的。
此外,我将假设一个项目具有名为 "folder" 的一对一关系,即该项目所在的文件夹。该文件夹将具有一对多关系 "items"该文件夹中的项目。
因此,以下是实现您想要的目标的众多方法之一。
- (NSSet*)getItemsForFolderName:(NSString*)folderName
groupName:(NSString*)groupName
inMOC:(NSManagedObjectContext*)moc
error:(NSError**)error {
NSSet *result = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Folder"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"folderName = %@ AND groupName = %@", folderName, groupName];
fetchRequest.relationshipKeyPathsForPrefetching = @[@"items"];
NSArray *folders = [moc executeFetchRequest:fetchRequest error:error];
if (folders) {
result = [[folders firstObject] items];
}
return result;
}
您也可以简单地获取项目。至于哪个最好,这取决于你如何为你的属性建立索引,以及你正在做什么类型的其他抓取。
- (NSArray*)getItemsForFolderName:(NSString*)folderName
groupName:(NSString*)groupName
inMOC:(NSManagedObjectContext*)moc
error:(NSError**)error {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Item"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"folderName = %@ AND folder.groupName = %@", folderName, groupName];
return [moc executeFetchRequest:fetchRequest error:error];
}
编辑
JamEngulfer221,我很确定这些例子会起作用,但既然你说即使我指出了你 post 中的错误,它们也不起作用,我想我会快速搞定一个项目并且测试...我老了,有时忘记东西...
所以,我用下面的代码破解了一个测试。
首先,创建模型...
- (NSManagedObjectModel *)makeModel {
NSEntityDescription *group = [[NSEntityDescription alloc] init];
group.name = @"Group";
NSAttributeDescription *groupName = [[NSAttributeDescription alloc] init];
groupName.name = @"groupName";
groupName.attributeType = NSStringAttributeType;
NSEntityDescription *folder = [[NSEntityDescription alloc] init];
folder.name = @"Folder";
NSAttributeDescription *folderName = [[NSAttributeDescription alloc] init];
folderName.name = @"folderName";
folderName.attributeType = NSStringAttributeType;
NSEntityDescription *item = [[NSEntityDescription alloc] init];
item.name = @"Item";
NSAttributeDescription *itemName = [[NSAttributeDescription alloc] init];
itemName.name = @"itemName";
itemName.attributeType = NSStringAttributeType;
NSRelationshipDescription *folderToGroupRelationship = [[NSRelationshipDescription alloc] init];
NSRelationshipDescription *groupToFoldersRelationship = [[NSRelationshipDescription alloc] init];
groupToFoldersRelationship.name = @"folders";
groupToFoldersRelationship.destinationEntity = folder;
groupToFoldersRelationship.minCount = 0;
groupToFoldersRelationship.maxCount = 0;
groupToFoldersRelationship.deleteRule = NSCascadeDeleteRule;
groupToFoldersRelationship.inverseRelationship = folderToGroupRelationship;
folderToGroupRelationship.name = @"group";
folderToGroupRelationship.destinationEntity = group;
folderToGroupRelationship.minCount = 0;
folderToGroupRelationship.maxCount = 1;
folderToGroupRelationship.deleteRule = NSNullifyDeleteRule;
folderToGroupRelationship.inverseRelationship = groupToFoldersRelationship;
NSRelationshipDescription *folderToItemsRelationship = [[NSRelationshipDescription alloc] init];
NSRelationshipDescription *itemToFolderRelationship = [[NSRelationshipDescription alloc] init];
folderToItemsRelationship.name = @"items";
folderToItemsRelationship.destinationEntity = item;
folderToItemsRelationship.minCount = 0;
folderToItemsRelationship.maxCount = 0;
folderToItemsRelationship.deleteRule = NSCascadeDeleteRule;
folderToItemsRelationship.inverseRelationship = itemToFolderRelationship;
itemToFolderRelationship.name = @"folder";
itemToFolderRelationship.destinationEntity = folder;
itemToFolderRelationship.minCount = 0;
itemToFolderRelationship.maxCount = 1;
itemToFolderRelationship.deleteRule = NSNullifyDeleteRule;
itemToFolderRelationship.inverseRelationship = folderToItemsRelationship;
group.properties = @[groupName, groupToFoldersRelationship];
folder.properties = @[folderName, groupName, folderToGroupRelationship, folderToItemsRelationship];
item.properties = @[itemName, folderName, itemToFolderRelationship];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] init];
model.entities = @[group, folder, item];
return model;
}
以及搜索方法...
- (NSArray*)getItemsForFolderName:(NSString*)folderName
groupName:(NSString*)groupName
inMOC:(NSManagedObjectContext*)moc
error:(NSError**)error {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Item"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"folderName = %@ AND folder.groupName = %@", folderName, groupName];
return [moc executeFetchRequest:fetchRequest error:error];
}
还有测试...
- (void)testBlarg {
NSManagedObjectModel *model = [self makeModel];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
[psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:NULL];
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
moc.persistentStoreCoordinator = psc;
for (int g = 0; g < 10; ++g) {
NSManagedObject *group = [NSEntityDescription insertNewObjectForEntityForName:@"Group" inManagedObjectContext:moc];
NSString *groupName = [NSString stringWithFormat:@"Group %02d", g];
[group setValue:groupName forKey:@"groupName"];
for (int f = 0; f < 10; ++f) {
NSManagedObject *folder = [NSEntityDescription insertNewObjectForEntityForName:@"Folder" inManagedObjectContext:moc];
NSString *folderName = [NSString stringWithFormat:@"Folder %02d", f];
[folder setValue:folderName forKey:@"folderName"];
[folder setValue:groupName forKey:@"groupName"];
[folder setValue:group forKey:@"group"];
for (int i = 0; i < 10; ++i) {
NSManagedObject *item = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:moc];
NSString *itemName = [NSString stringWithFormat:@"Item %02d (%@;%@)", i, folderName, groupName];
[item setValue:itemName forKey:@"itemName"];
[item setValue:folderName forKey:@"folderName"];
[item setValue:folder forKey:@"folder"];
}
}
}
[moc save:NULL];
[moc reset];
NSString *folderName = @"Folder 04";
NSString *groupName = @"Group 02";
NSArray *items = [self getItemsForFolderName:folderName groupName:groupName inMOC:moc error:NULL];
items = [items sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"itemName" ascending:YES]]];
XCTAssertEqual(10, items.count);
for (int i = 0; i < 10; ++i) {
NSManagedObject *object = items[i];
NSString *expectedItemName = [NSString stringWithFormat:@"Item %02d (%@;%@)", i, folderName, groupName];
XCTAssertEqualObjects(expectedItemName, [object valueForKey:@"itemName"]);
NSManagedObject *folder = [object valueForKey:@"folder"];
XCTAssertEqualObjects(folderName, [folder valueForKey:@"folderName"]);
XCTAssertEqualObjects(groupName, [folder valueForKey:@"groupName"]);
NSManagedObject *group = [folder valueForKey:@"group"];
XCTAssertEqualObjects(groupName, [group valueForKey:@"groupName"]);
}
}
我认为您的数据模型设置有缺陷,导致了这种混乱。其实你的问题很简单。
首先,如果一个项目被分配到一个文件夹您不需要存储文件夹名称 - 这将是多余的,因此是不必要的信息。这同样适用于文件夹和组名称。
其次,父文件夹应该是同一实体的自反关系,而不是属性。
所有实体都应该只有一个 name
属性和这些关系(注意直观的非技术关系名称):
Group (folders) <------>> (group) Folder (items) <------->> (folder) Item
Folder (subFolders) <------>> (parentFolder) Folder
谓词现在很简单:
[NSPredicate predicateWithFormat:
@"folder.name = %@ && folder.group.name = %@", folderName, groupName]