Core Data NSPredicate IN entre NSSet et NSArray
mushu
Membre
Bonjour,
me voilà bloqué depuis un bon moment sur la construction d'une NSRequest !
Voilà un bout de code :
NSArray *allotUserLocalisation = [ETICKET_ALLOTEMENT allotmentsByUserUuid:[UserContext getUserContext].userUuid
andType:@USER_LOCALISATION];
NSMutableArray *localisationPredicate = [NSMutableArray arrayWithCapacity:allotUserLocalisation.count];
NSMutableArray *clauseINUserLocalisationArray = nil;
NSString *niveau = nil;
for(int i=1; i<=10; i++) {
clauseINUserLocalisationArray = [NSMutableArray arrayWithCapacity:allotUserLocalisation.count];
// génération de la liste des localisations par niveau
for(ETICKET_ALLOTEMENT *allot in allotUserLocalisation)
{
niveau = [allot performSelector:NSSelectorFromString([NSString stringWithFormat:@niv%i, i])];
if(niveau.length > 0 && ![@* isEqualToString:niveau])
[clauseINUserLocalisationArray addObject:niveau];
}
if(clauseINUserLocalisationArray.count > 0) {
NSLog(@clauseINUserLocalisationArray: %@", clauseINUserLocalisationArray);
NSString *leftPredicateString = [NSString stringWithFormat:@allotement_projet_ticket_list_rel.ticket_vmax_rel.ticket_rel.localisation_rel.niv%d, i];
[localisationPredicate addObject:[NSPredicate predicateWithFormat:@%K IN %@", leftPredicateString, clauseINUserLocalisationArray]];
}
}
[subPredicates addObject:[NSCompoundPredicate andPredicateWithSubpredicates:localisationPredicate]];
Cela me génère la bonne requête :
(allotement_projet_ticket_list_rel.ticket_vmax_rel.ticket_rel.localisation_rel.niv1 IN {"Kraft Noel", "Kronenbourg"} AND allotement_projet_ticket_list_rel.ticket_vmax_rel.ticket_rel.localisation_rel.niv2 IN {"Commande", "Commande"})
si ce n'est que ça me plante l'application :
Uncaught exception: unimplemented SQL generation for predicate : (allotement_projet_ticket_list_rel.ticket_vmax_rel.ticket_rel.localisation_rel.niv1 IN {"Kraft Noel", "Kronenbourg"})
Quelqu'un aurait une idée ?
PS: tous les attributs qui serterminent par _rel sont des relations...
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
ben utilise plus tôt MagicalRecord tu verras ça change la vie !
Je ne suis pas trop fan du "utilise un truc qui fait tout pour toi".
Je ne comprends pas trop ton code, est-ce que tu peux expliquer ce que tu cherche à faire ? Quels sont les types de données en entrée et ce que tu veux à la fin ?
ben si le truc fait bien les choses pourquoi faire le GRAND puriste et ne pas l'utiliser ?!
Par ce que tu es contraint à travailler comme l'outil est pensé. L'intérêt de CoreData c'est qu'il est puissant, malléable et pas si complexe à mettre en oe“uvre.
Passer par une sorte de "midleware" pour s'éviter deux ou trois prise de tête sur des cas complexes qui demandent mine de rien de réfléchir à l'optimisation de nos requêtes et de nos bases n'est pas vraiment une bonne idée IMHO.
La différence de complexité entre avec et sans est trop faible pour se contraindre à un outil third part qui ne va pas forcément évoluer à la même vitesse que le reste.
Je comprend ton point de vue mais je ne suis pas sûr que tu aies vraiment testé MagicalRecord de fond en comble. Car moi aussi au début j'étais sceptique, mais à l'utilisation j'ai été agréablement surpris. Ca a permis d'éviter pas mal de code répétitif et de diviser mon nombre de LOC par ~3 dans mes applis fortement CoreData-esques.
Après, chacun ses choix, tien ne t'oblige à l'utiliser, mais MR permet à la fois de simplifier ton code en réduisant grandement le nombre de lignes de code à écrire pour toutes les opérations courantes et classiques qu'on fait régulièrement avec CoreData, ET de pouvoir sortir de ce cadre et faire ce que l'on veut. Utiliser MagicalRecord n'interdit pas pour autant d'utiliser CoreData avec des cas tricky.
Genre au lieu d'à chaque fois créer une NSEntityDescription pour l'associer à ta NSFetchRequest, puis créer un NSSortDescriptor, puis faire "setEntity:" + "setPredicate:" + "setSortDescriptor:", tu peux faire tout ça en une seule ligne avec MR. Certes tu vas me dire c'est pas grand chose de gagné, mais n'empêche que sinon c'est répétitif et rébarbatif, et n'est-ce pas l'objectif d'un bon framework et d'un bon code POO que d'encapsuler dans des méthodes le code répétitif plutôt que de le copier/coller à chaque fois ?
Le but de MR n'est pas de remplacer CoreData ou son API, c'est de proposer des méthodes "helper" pour tout ce code répétitif qu'on écrit.
Ce que j'adore dans MagicalRecord, c'est ce que je recherche dans n'importe quel soft ou framework bien pensé : c'est simple à prendre en main, ça facilite la vie et en 2 temps 3 mouvements tu sais déjà comment l'utiliser... mais si tu veux pousser plus loin et aller dans des cas plus complexes et plus poussés, tu peux aussi.
Rien ne t'empêche (si si, je t'assure) d'utiliser les méthodes classiques de CoreData en même temps que MagicalRecord. Tu n'es pas du tout contraint de travailler "comme l'outil est pensé", tu peux sortir des clous et utiliser CoreData de base quand tu veux.
Perso, moi qui avait "peur" de CoreData au début (je me disais qu'il me faudrait plusieurs jours à fond dans la doc et les tutos dessus pour bien comprendre comment ça marche et capter les multiples LOC que je devais écrire tout ça pour faire un bête fetch sans me mélanger les pinceaux entre MO, MOC, EntityDescription, etc...) MR m'a bien aidé à rentrer dans le bain. Et maintenant que je suis bien rentré dedans et que MOC, MO & co me paraissent finalement naturels et que j'ai bien assimilés les concepts derrière, si j'ai besoin d'utiliser du CoreData pur je sais le faire (et je sais quand j'ai besoin de le faire). Mais dans les autres cas, c'est à dire dans 90% du temps, je réduis ma quantité de code à taper par 3.
Mouais, je reste toujours méfiant sur ces truc.
Des outils comme MOGenerator par exemple j'aime bien, le code généré est modifiable.
Certe je n'ai pas assez regardé MagicalRecord, mais ça me semble trop simplificateur...
Du coup, tu n'utilises pas de framework comme AFNetworking non plus je suppose ?
(Tu dois pas utiliser beaucoup de framework tierce-partie, en fait, j'imagine, vu ta vision des choses)
AFNetworking fait parti des framework que je fuis le plus possible. Surtout que c'est pas grand chose à refaire.
En fait j'utilise sans soucis des framework tant que c'est quelque chose qui m'apporte un réel plus. MailCore2 ou NSLogger par exemple. ça m'offre des possibilité qui ne sont pas facilement accessible, là ça vaut le coup de s'adapter à l'outil voir discuter avec le mainteneur pour le faire évoluer.
Par contre pour des truc comme les connexions HTTP ou CoreData, c'est tellement simple à refaire tous ces framework à la mode que je trouve idiot de s'en servir dans le sens où, refaire le petit bout de framework dont on a réellement besoin me semble plus rapide que de devoir prendre tout le framework et s'adapter à son fonctionnement pas forcément logique.
Prend RestKit par exemple. Ce truc recommandé par plein de gens est une merde infâme qui n'utilise même pas correctement les block dans leur design d'API... Je m'en suis servis une fois, j'ai passé plus de temps à tweaker son fonctionnement qu'autre chose.
Je suis d'accord pour RestKit, c'est mal foutu.
Mais AFN et MR, je suis fan. Parce que c'est bien pensé, et que ça facilite les choses, et pas qu'un peu.
Et je ne suis pas forcément d'accord quand tu dis "c'est tellement simple à refaire". Car non, il y a plein de cas tricky auquel tu ne penses pas quand tu commences à faire ce genre de framework, tu sais très bien le temps que ça prend à tester unitairement tous les cas possibles dans lesquels tu pourrais tomber. Pourquoi à chaque nouveau projet réinventer exactement toujours les mêmes méthodes, qui gèrent toujours les mêmes cas, font toujours les mêmes choses, au lieu de capitaliser ?!
Quel intérêt de réinventer la roue et de "refaire le petit bout de framework dont tu as besoin" quand tu as à ta disposition un framework bien pensé, testé unitairement et testé par plein de gens qui l'utilisent, et qui a donc déjà essuyé tous les plâtres ? Quel intérêt de refaire le boulot toi-même, au risque de ton côté de ne pas penser à plein de cas tricky ?
Tu n'achètes pas ta table dans un magasin, tu préfères couper du bois dans la forêt près de chez toi, puis la fabriquer toi-même de toutes pièces ? Tu peux, certes tu auras le bois que tu veux, la forme précise que tu veux pour ta table, la couleur et la finition, et tout, et tu vas me dire que c'est pas grand chose à faire, mais bon...
Bon après, moi aussi je suis du genre à aimer réinventer la roue parce que ce qui me plait c'est le challenge d'un côté et la découverte de "comment ça marche sous le capot" et que j'aime bien me faire mes propres trucs, mais bon, y'a des limites. Si y'a des outils bien pensés et qui ont déjà dégrossi le travail pour toi, et qui s'avèrent être efficaces, c'est bête de s'en passer quand même, et de réinventer exactement la même chose. Et puis c'est le fondement même de l'esprit communautaire et OpenSource (Au final, je me demande bien pourquoi je mets des trucs sur mon GitHub et pourquoi je publie sur CocoaPods, tu dois tout réécrire from scratch de ton côté je parie)
À propos de MR : j'étais enthousiaste sur cet outil, mais je l'ai abandonné car il ne fonctionne pas dans le cas des "document-based applications"... (pour osx)
@colas2 : Ca m'intéresse ta remarque, car moi je ne l'utilise que pour iOS.
C'est peut-être là la différence, il est peut-être fortement pensé/utilisé sur iOS où les gens en sont ravis, mais pour ceux qui font de l'OSX comme toi ou yoann il n'est pas forcément adapté ?
Qu'est ce qui ne marche pas dans le cas des document-based applications ?
Je suppose que chaque document a son propre MOC, mais cela ne pose pas de soucis avec MR donc qu'est ce qui bloque ?
Entity: PROJECT relationship: _ allottement_projet_ticket_list_rel (dest: ALLOTEMENT_PROJET_TICKET)
Entity: ALLOTEMENT_PROJET_TICKET relationship: _ ticket_vmax_rel (dest: TICKET_VMAX)
Entity: TICKET_VMAX relationship: _ ticket_rel (dest: TICKET)
Entity: TICKET relationship: _ localisation_rel (dest: LOCALISATION)
Entity: LOCALISATION attributes: _ niv1, niv2, ..., niv10 (NSString)
De mémoire, le problème est qu'on n'arrive pas à lui dire que le MOC à prendre en compte est celui du document.
On contourne ce problème en utilisant les méthodes helper de MR de type
Attention, ça ne marche qu'à condition d'avoir
MR, ce qu'on peut faire en appelant une de ses méthodes.
Mais, même avec ça, ça ne marche pas. Sur une simple requête, j'ai l'erreur
Tu peux aller voir le issue sur github : https://github.com/magicalpanda/MagicalRecord/issues/151
@Mushu
Peut-être une copie d'écran de la présentation graphique pourrait nous aider.
Avec les noms et les conventions d'écriture que tu as choisis, je trouve ton modèle très difficile à décrypter !!
Pistes :
" est-ce que ça marche si tu fais “ == "Kronenbourg" †au lieu de "IN" ?
" problème dans ton modèle ?
Ok en effet AppKit n'a pas l'air d'utiliser les API aussi à jour de CoreData que celles qu'on a sous OSX, en particulier les possibilités pour les MOC d'avoir des parent MOC et surtout des private queue, pratique pourtant mis en avant par Apple dans ses dernières conférences WWDC sur le sujet (un comble ^^) et expliquées ici dans la doc.
C'est bon à savoir pour ma prochaine présentation sur MR aux CocoaHeads, tiens.
Ca serait bien qu'Apple mette à jour AppKit et les APIs de Cocoa/OSX comme NSPersistentDocument pour faire profiter de tout ça à OSX un jour (Mavericks ?)
Je capitalise en interne. J'ai mes propres framework que je passe d'un projet perso à l'autre.
Quand j'ai un projet pour un client que je veux faire vite fait et que je n'aurais pas à maintenir dans le temps, ça m'arrive d'utiliser AFN.
Par contre quand c'est des outils perso comme ma suite d'outil d'admin OS X Server, je travail sur des framework interne uniquement. Cela me permet de connaitre réellement le code qui est derrière. Si j'ai besoin d'améliorer un fonctionnement je peux le faire plus rapidement que sur un projets aussi gros qu'AFN et surtout aussi usine à gaz. Le truc fait le thé et le café... Quand il faut changer un comportement cela prend plus de temps que d'avoir écris réellement la partie qui te sert.
Encore une fois je parle pour des framework aussi simpliste que AFN, se passer d'une dépendance extérieur n'est pas un mal.
Comme je disais plus haut, je me sert de MailCore2 qui capitalise une très grosse expérience dans le domaine des e-mail avec MailCore, libetpan et Sparrow. Dernièrement je me suis retrouvé coincé avec, une fonction mandataire pour moi qui manquait, j'ai essayé de comprendre comment marche le framework sans réussir. Par chance, le dev du framework, que je connais, a répondu à ma demande de feature dans les 48h. Si la demande ne l'intéressait pas, j'aurais fini bloqué avec un développement fortement engagé sur un API d'un framework que je ne connais pas. Tout à refaire...
Nous aussi on capitalise en interne, t'inquiète. Mais ça prend du temps, et ça n'est pas incompatible avec l'utilisation de frameworks externes, surtout ceux qui sont très actifs (on les trie sur le volet, on n'utilise pas n'importe quel fmk externe qu'on trouve sur le net non plus, je passe la validation dessus avant).
Après, je comprend ton point de vue, quelque part ça serait aussi le mien si j'avais le même boulot et le même contexte que toi. Mais c'est un point de vue de quelqu'un qui a les compétences et des années d'expérience en développement OSX, qui connais bien les entrailles de Cocoa & co, et est à l'aise avec tous ces outils, dont c'est le métier à plein temps de faire des applications et qui bosse seul (ou en petite équipe).
PS : D'ailleurs, pourquoi ne partages-tu pas tes frameworks avec la communauté OpenSource ?
Si j'avais autant de temps que toi pour faire tout cela, et que je bossais seul sur des projets ou des clients que je choisis, je me ferai un plaisir de le faire également d'ailleurs, rien que parce que par pur plaisir je préfère réinventer les choses à ma manière que d'utiliser un truc tout fait, mais aussi pour avoir mes propres outils. Mais la réalité des choses fait que je ne suis pas seul dans ma boite, qu'on n'a pas le temps de réinventer les choses quand elles existent, ou alors moi et 1 ou 2 autres on aurait peut-être le temps mais faudrait que les 15 autres aient le même rythme et la même expérience que nous pour que ça tienne dans les temps...
Cependant, pour la plupart des développeurs, qu'ils soient purs newbies ou un peu moins néophytes mais pas pour autant experts, réinventer la roue from scratch, genre recoder toute leur couche HTTP, c'est un boulot monstre. Pour des gars comme toi ou moi qui connaissons bien les RFC et les standards et qui avons l'habitude c'est pas trop un problème, ça ne nous dérange pas (voire nous amuse) et on a tout de suite la vision de l'architecture du framework qu'on veut faire dans la tête, par expérience, et on a déjà pensé à tous les use cases.
Mais pour les autres, les collègues avec qui je bosse, les gens de tous niveaux qui trainent ici sur CocoaCafe, et autres, c'est pas la même chose, ils ont déjà assez à faire à se concentrer sur les bonnes pratiques de codage, à savoir quel classes utiliser, réinventer des trucs qui existent déjà en version toute faite serait de la perte de temps pour eux, alors qu'utiliser un truc qui leur facilite la vie est un gain de temps et permet de se plonger dans le domaine assez rapidement. Quitte le jour où ils commencent à avoir de l'expérience et du temps à ce qu'ils recodent leur propre framework à leur usage perso une fois qu'ils auront bien compris toutes les subtilités que l'on peut rencontrer.
Pour reprendre l'exemple de AFNetworking, la plupart des gens ne connaissent rien au protocole HTTP ou si peu, et recréer leur propre framework pour gérer tout ça leur prendrait bien plus de temps qu'à toi ou moi. Et leurs besoins sont en général largement couverts par AFNetworking ou MagicalRecord, surtout quand ils commencent sur le sujet. Du coup, je trouve que leur conseiller MagicalRecord ou AFNetworking pour débuter, qui leur facilite la prise en main de toutes ces composantes, ce n'est pas une mauvaise chose, car eux ne sont pas prêt (en terme de niveau d'expérience entre autres) et/ou n'ont pas le temps de réinventer la roue from scratch.
Evidemment si tu as le loisirs, le temps et les compétences pour réinventer la roue et tout réécrire toi-même (du coup avec ton code à toi que tu maà®trises et limites à ton cas d'usage), pourquoi pas. (Même si, à force de capitaliser ce composant, il y a des chances qu'il va lui aussi finir par faire le thé et le café). Mais pour qqun qui n'a pas ton niveau c'est quand même cher payé que de lui conseiller de tout réinventer alors qu'il ne maà®trise déjà pas les bases de CoreData et qu'il a à sa portée un framework bien pensé et testé qui lui rendrait la vie moins difficile à ce sujet. C'est encore l'histoire de la table que tu construit toi-même sur mesure avec ton propre bois coupé dans la foret d'à côté, ou la table que tu achètes toute faite dans ton magasin de meubles préféré. Certes il faut être un peu bricoleur mais tout le monde n'a pas des mains de menuisier.
Je t'envoie un capture demain
Merci 8--)
Je partage uniquement la partie R&D que je considère comme intéressante en elle même mais pas financièrement viable. Typiquement sur mon GitHub tu va trouver mon hack de la base de mot de passe de Apple Remote Desktop, un prototype fonctionnel d'exposition REST pour CoreData via CocoaHTTPServer, un backend REST pour CoreData via NSIncrementalStore.
Par contre, les framework complet qui ont demandé des semaines de boulot sont plus des framework qui vont se retrouver à la vente comme le projet stardav.com qui propose un framework de synchro CoreData sur n'importe quel type de serveur DAV (même fonctionnement qu'iCloud sync, plus stable et sur serveurs privés). Ce truc est pas encore publié car on est en train de faire les dernières opération d'optimisation et de gestion des lock avant de passer en phase de beta.
Et d'une autre manière, les framework beaucoup trop complet, spécialisé et simple que je me suis fait, ne sont pas publié pour garder un avantage concurrentiel. Typiquement mon framework de contrôle d'OS X Server sur le WebService d'Apple. J'ai expliqué comment le WS fonctionne çà et là , par contre mon code est privé car il me permet de bosser réellement vite. En gros en un jour, un jour et demi max je peux implémenter l'admin d'un nouveau service, et dans la semaine j'ai terminé tous les tests d'usage et les fonctions un peu custom qui vont hors des limites officielles d'Apple...
Reste les framework bidon, genre un qui me permet de faire du Syslog plus simplement qu'avec l'API C, que je n'ai pas publié par ce que c'est un simple wrapper bête et méchant. Je ne vois pas l'intérêt de le partager.
Après faut pas oublier que pour moi le dev c'est un passe temps. Je le fait juste pour le plaisir (ce qui explique que bien des projets que j'ai dans le domaine sont au point mort par manque de temps, comme mes outils d'admin unix, un plugin pam ou encore un projet de CPL en hub).
Mon "vrai" boulot c'est l'admin sys, le réseau et un peu la sécu. Sur ces domaines là j'ai mon propre type d'OpenSource avec mon blog (blog.inig-services.com) où je documente énormément de chose qui ne sont pas à porté de tous.
À la limite sur ce point de vu je suis d'accord, mais bon AFN c'est pas non plus se refaire tout le protocole HTTP hein, c'est juste suivre les recommandations d'Apple sur les options de NSURLConnection, la run loop et packager le tout en opération pour faire le taff qu'Apple n'a pas fait... C'est vraiment que de la lecture de doc quand tu regarde le code.
Voici mon modèle de données ...
Mon modèle est assez clair ?
Disons que ça ressemble plus à un modèle dédié à un gestionnaire de base de données, ce que n'est pas Core Data.
Quand tu créés un modèle avec Core Data, il faut prendre en compte non seulement le fonctionnel de ton appli mais aussi l'interface utilisateur (en créant par exemple une entité qui contiendra uniquement les informations utiles pour un table view controller).
est-ce que ça marche si tu fais “ == "Kronenbourg" †au lieu de "IN" ?
Le modèle a effectivement été créé en regard d'une base de donnée pré-existante.
Par contre je ne vois pas l'intérêt de créer des entités pour les controller.
Tu pourrais développer un peu ce sujet ?
Alors ça marche ça ou pas ?
VIEW_LOCALISATION predicate: allotement_projet_ticket_list_rel.ticket_vmax_rel.ticket_rel.localisation_rel.niv1 == "Kronenbourg"
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'keypath allotement_projet_ticket_list_rel.ticket_vmax_rel.ticket_rel.localisation_rel.niv1 not found in entity <NSSQLEntity ETICKET_PROJET id=17>'
Avec la complétion automatique, je n'arrive pas à atteindre le ticket_vmax_rel;
sûrement parce que allotement_projet_ticket_list_rel me renvoie un NSSet ...
Cette propriété est configurée en optional, To-Many relationship, inverse(project_rel)
@mushu :
quelques pistes :
1) sur ton diagramme, les relations ne sont pas en to-many, il me semble
2) il est conseillé de toujours associer une relation inverse à toute relation : l'as-tu fait ? d'après ton diagramme, il semble que non
3) attention, si tu utilises des "ordered to-many relationships", l'implémentation d'Apple est buguée (mais ça se corrige)
Je pense que tes problèmes viennent de 2)
C'est une question d'optimisation. Je n'ai rien contre les SUBQUERY (j'en utilise moi-même, parfois même des subquery dans des subquery) mais le nombre d'étapes pour réaliser ta requête (pour un table view) me semble un peu énorme.
En plus, par défaut, quand tu fetch une entité Core Data te ramène en mémoire tous les attributs de cette entité (sauf les relationships qui sont en fault). Ici on imagine un peu ce que ça peut donner vue la taille de tes entités.
Dans mon appli, j'ai une liste d'opérations bancaires. Mon entité d'opération bancaire contient beaucoup d'informations. Mais pour afficher dans un table view controller la liste des opérations, j'ai créé une entité spécifique qui contient uniquement ce dont j'ai besoin pour l'affichage. Résultat : une requête toute simple et très véloce.
Bon je n'arrivais pas à atteindre ticket_vmax_rel à cause d'une erreur d'orthographe (mal épelé allotement_projet_ticket_list_rel)
j'ai donc :
ce qui me renvoie... rien !
J'ai beau faire le chemin à la main dans la base de donnée sqlite pour vérifier que les données existent bien et sont bien organisées entre elles...
mon problème vient plus de mon manque de connaissance en Core Data je epnse car je n'arrive même pas à récupérer les allotements_projet_ticket ...
Cette contrainte ne me retourne aucun projet alors que ces données sont bien en base ...