NSPredicate和Core Data多表查询

时间:2015-10-08 23:40:18

标签: ios sql objective-c core-data nspredicate

我有一个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部分时出现问题。我无法弄清楚如何将其表示为谓词。

我应该注意,我在项目和文件夹之间存在关系,以及文件夹和组之间的关系。

编辑:

这是我的数据模型的设置:

Data Setup

这是我现在用于提出请求的代码:

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];

2 个答案:

答案 0 :(得分:1)

因此,基本上,您想要获取特定文件夹中的所有项目(即,您希望所有具有给定folderName和groupName的项目...唯一标识特定文件夹。)

既然你说你已经有了关系,我会假设它们是以反向关系设置的。

此外,我将假设一个项目具有一个名为"文件夹"的一对一关系。该项目所在的文件夹。该文件夹将具有多对多关系" 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,我很确定这些例子是可行的,但是既然你说他们甚至在我指出你的帖子中的错误之后也没有,我想我会快速完成一个项目并进行测试。 ..我老了,有时会忘记......

所以,我用以下代码破解了测试。

首先,创建模型......

- (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"]);
    }
}

答案 1 :(得分:0)

我认为您的数据模型设置存在缺陷,导致这种混淆。实际上,你的问题很简单。

首先,如果项目被分配到文件夹,则不需要存储文件夹名称 - 这将是多余的,因此是不必要的信息。这同样适用于文件夹和组名称。

其次,父文件夹应该是与同一实体的自反关系,而不是属性。

所有实体应该只有name属性和这些关系(请注意直观的非技术关系名称):

Group (folders)     <------>> (group) Folder (items) <------->> (folder) Item
Folder (subFolders) <------>> (parentFolder) Folder

谓词现在很简单:

[NSPredicate predicateWithFormat:
  @"folder.name = %@ && folder.group.name = %@", folderName, groupName]