NSPredicate & Date ancienne plus proche.

LarmeLarme Membre
avril 2013 modifié dans Objective-C, Swift, C, C++ #1

Salut tout le monde.


 


Je joue avec CoreData, et notamment NSPredicate.


 


Imaginons que j'ai une Date1 (définie par moi), et j'ai toute une liste (dans CoreData) avec pleins de dates.


Les dates enregistrées dans CoreData peuvent être uniques (en réalité, elles ne le sont pas forcément, mais simplifions le problème).


Ici, l'idée serait de trouver la date (de CoreData) qui soit :


- à  la fois plus ancienne que Date1

Et la plus proche de Date1.


En bref, si je rangeais Date1 dans CoreData rangée par ordre chronologique " croissant " de dates, j'aurais :


    XxX


    DateRecherchée


    Date1


    XxX


 




Ici, le but, c'est de faire le plus simple possible en évitant de récupérer 36k dates du fetch.


 


En effet, je pourrais très bien récupérer dans un NSArray toutes les dates qui sont avant Date1, puis chercher quelle est la plus tardive (potentiellement le dernier élément du NSArray s'il est rangé par ordre chronologique).


Mais, je me retrouverais bêtement avec un NSArray rempli de 36k dates pour n'en avoir qu'une seule.


 


Alors, mon idée/piste que j'ai pour l'instant dans mes recherches, serait potentiellement de faire un predicate avec un Block...


 


Est-ce que je cherche bêtement à  optimiser inutilement ? Est-ce que c'est " rapidement faisable " avec un NSPredicate, ou c'est à  faire après le fetch quoiqu'il arrive ?


Mots clés:

Réponses

  • AliGatorAliGator Membre, Modérateur

    Pourquoi tu ne fais pas un predicat pour trouver l'entité qui a la valeur positive minimum pour (Date1 - dateCoreData) ?


     


    J'ai pas en tête le prédicat que ça donnerait, mais je pense que c'est jouable de faire un prédicat qui calcule la différence de 2 dates et cherche la valeur MIN... donc c'est juste une piste


  • 36K dates à  raison de 360 environ par ans ça fait pas loin d'un siècle de dates qui se suivent. A moins qu'elles stockent aussi l'heure. Si leur nombre est dû à  leur répétition tu as aussi les opérateurs sur les collections qui pourraient (peut être ?) servir : @distinctUnionOfObjects pour travailler sur les valeurs uniques et @max pour récupérer le plus grand de la liste avec un predicate entre deux pour extraire le tableau de celles antérieures à  date1 ..


    CoreData, de mémoire, ne récupère pas tous les objets pour ce genre d'opérations (mais j'ai lu la doc il y a longtemps et ne me sers toujours pas de lui).


  • J'ai un doute, on peut utiliser les opérateurs de sélection dans predicate destiné à  un fetch ? 


     


    Sinon pour ta requête il te faudrait un truc comme ça : 


     



    NSManagedObjectContext *moc = [self managedObjectContext];
    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@Entity inManagedObjectContext:moc];

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];

    // On cherche toutes les dates plus anciennes que date1
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@date < %@", date1];
    [request setPredicate:predicate];

    // On tri de sorte à  avoir de la plus récente à  la plus ancienne
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@date ascending:NO];
    [request setSortDescriptors:@[sortDescriptor]];

    // On limite pour avoir juste le premier résultat
    [request setFetchLimit:1];

    NSError *error;
    NSArray *array = [moc executeFetchRequest:request error:&error];
    if (array == nil)
    {
    // On gère l'erreur
    }

  • Grillé par Jegnux, c'est exactement ce que j'étais en train d'écrire...


  • Bonsoir,


     


    Pour effectuer des predicate multiples j'utilise NSCoumpoundPredicate.


     


    Je crée les NSPredicate dont j'ai besoin dans un NSArray puis je crée le predicate résultant avec NSCoumpoundPredicate avant de lancer la recherche.


     


    C'est très efficace et on lance un seul request.

  • AliGatorAliGator Membre, Modérateur

    Astuce intéressante fleurantin, ça marche aussi avec CoreData ?


    (Car je sais qu'avec CoreData, il y a certains prédicats qui ne sont pas supportés)


  • Tiens je connaissais même pas les NSCoumpoundPredicate, mais je me demande si ça fait quelque chose de plus que de faire un AND ou un OR dans un predicateWithFormat: ?


    Car bon jusqu'à  maintenant je n'ai utilisé que le +predicateWithFormat: ou bien le +predicateWithBlock: (puissant celui-là  mais qui ne fonctionne pas avec Core Data il me semble)


  • Je fait de la même façon que JegnuX avec les NSPredicate et ca fonctionne trés bien ^^


     


    Bonne astuce pour les NSCoumpoundPredicate, ca à  l'air trés intéressant !


  • LarmeLarme Membre
    avril 2013 modifié #10

    Plop, merci pour toutes ces pistes, je vais voir ce que j'arrive à  faire à  partir de ça, mais à  partir de la semaine prochaine (d'autres trucs à  m'occuper entre temps). Donc ne vous étonnez pas si je ne poste pas de réponse et ce que j'ai fait d'ici là .


    En tout cas, j'ai vu que cela a intéressé pas mal de monde, avec plusieurs pistes potentielles.


     


    Pour expliciter un peu plus et à  laudema qui était perplexe par rapport à  ces fameux 36k:


    Mes dates sont horaires.


    Ce qui fait pour une année 8.760 données (365*24). J'ai potentiellement 2 types de données, à  raison de 4 variations par jours, ce qui donne pour une année 10.220 (365*28). Mon application se veut pérenne, du coup, on peut monter facilement à  3 ans, d'où ces 36k.


    Je travaille encore sur mon modèle (passer uniquement à  365*24, avec des champs vierges, ce que je vais sûrement faire), mais grosso-modo, c'est ça.


  • colas_colas_ Membre
    avril 2013 modifié #11

    Teste d'abord si ça rame ou pas avec la solution de JegnuX.


     


    Si ça rame :


    1) tu pourras ajouter un index sur ton attribut date (de toute façon, je te conseille de le faire)


    2) si ça rame encore, il faudrait savoir qui est Date1 a priori (une date quelconque ou pas) et surtout, garder une sorte d'historique de


    tes recherches qui te permettront d'optimiser ta requête. Typiquement, tu peux avoir une borne inférieure sur la date recherchée :


     


    /*maths


    Soit f la fonction qui à  Date1 associe la plus grande date <= Date1 dans ton registre.


    Alors :   Date1 <= Date2 implique f(Date1) <= f(Date2)


     


    Donc, si tu as déjà  calculé f pour une date inférieure à  Date1, tu as aussi une borne inférieure sur ta date.


     


     


    Enfin, par rapport à  NSCoumpoundPredicate, j'ai lu dans la doc que ça permet de construire facilement des prédicats composés au runtime.


  • Dans la mesure où il s'agit de dates horaires est-ce qu'il ne vaudrait pas mieux d'abord extraire un sous ensemble (genre les dates horaire de la même journée que la date de requête), pour ensuite faire le tri dessus ?


  • @Paul : par dates horaires il voulait dire qu'une date c'est "jj/mm/YYYY hh:mm:ss", donc il va avoir plein de dates


  • @MonsieurPaul :


    Je ne pense pas. Je vais te présenter un cas plus concret :


    Je demande toutes les infos pour la semaine en cours. Du coup, de 00:00 le lundi à  maintenant.


    Dans le cas, où mon appareil distant aura eu un problème et ne m'aurait pas donné version pour 00:00 (disfonctionnement, extinction, etc.), on peut supposer qu'il a eu celle du dimanche, donc je pourrais demander celle du dimanche à  23:00.


    Maintenant, on peut imaginer le cas où il était en rade tout le week-end. Du coup, la requête devrait se le vendredi précédent, etc.


    Maintenant, une autre solution serait de faire ainsi : Si je n'ai rien à  cette date-là , déjà  prévoir et la remplir via lissage (+booléen pour valeur créer ?, afin de pallier un changement de lissage par exemple).


  • LarmeLarme Membre
    avril 2013 modifié #15

    Alors :


    Je remercie à  nouveau tout ceux qui se sont posé sur mon problème.


    J'ai découvert vraiment des trucs intéressants sur lesquels j'étais passé lors de ma lecture de la page de la classe, et notamment les NSPredicate et les FetchLimit. Comme quoi, CoreData, ça va finir par rentrer ! 


    J'utilise pour l'instant la méthode de JegnuX, qui s'avère bonne (je ne poste pas de code " ou tout du moins actuellement " car celui de JegnuX, modulo un warning " pas fait un copier/coller, du coup, j'peux pas dire qu'il compile :P " peut-être me semble bon).


    Je n'ai juste pour l'instant, pas mis le fetchLimit (mais j'ai placé un warning pour me dire d'updater la méthode de fetch en incluant en paramètre la fetchLimit), car pour l'instant, je m'occupe d'autres problèmes plus importants.


     


    PS : Message à  but totalement idiot, mais je peux faire des " " " sur le forum !


  • AliGatorAliGator Membre, Modérateur


     


    PS : Message à  but totalement idiot, mais je peux faire des " " " sur le forum !




     


    Hé c'est trop bien ça ! Ca fait un bail " j'avais même prévenu Alex " que ça m'énervait de pas pouvoir en faire " car j'aime utiliser les tirets quadratins à  bon escient " du coup c'est top ! Avant c'était lié à  un raccourci inutile " ça masquait la barre de boutons de l'éditeur " qui a dû être désactivé depuis la dernière mise à  jour du forum :)

  • Ou peut être est ce lié aux modifications qu'a apporté Apple à  la saisie récemment " depuis qu'un long appui, sur e par exemple, propose un choix de lettres accentués et non une répétition " parce que option + tiret c'est pas un raccourci ordinaire non plus, même pour l'interface de ce forum ;)


Connectez-vous ou Inscrivez-vous pour répondre.