Re: NScollections vs Java collections
Re: NScollections vs Java collections
- Subject: Re: NScollections vs Java collections
- From: Hugi Thordarson via Webobjects-dev <email@hidden>
- Date: Mon, 03 Feb 2025 14:26:16 +0000
Hi Samuel,
Yeah, Expressions (the things actually generated by the Properties) can be used
for both querying the DB and for in-memory evaluation, i.e. what you call
"object queries”. I haven’t looked much into how much you can do “in-memory”
with the more complex Expressions though, have to do some experimentation on
that.
I’d like to say “yes” regarding using some of that Property magic on generic
objects/methods rather than Persistent object attributes, I certainly have in
the past. But Cayenne is focused on using this for Persistent objects and their
modeled attributes so there might be caveats I don’t know of currently. I have
replaced most of the code where I used Cayenne to perform actions on generic
objects/methods with Streams, but I know what you mean — ERXKey/Property is
certainly nicer in some ways, especially when it comes to working with nested
objects (long keypaths).
Feel free to hit me up here or in #cayenne on Slack if you decide to check out
Cayenne and need some rubber ducking. I incidentally made a video last night
for another WO guy on creating a simple WO/Cayenne project from scratch. It
auto-generates an in-memory h2 database at application startup from the model,
meaning you can play around with adding things to the model and try out
features along the way.
Video:
https://www.youtube.com/watch?v=Ahu3Qnki1-w
Project:
https://github.com/hugithordarson/cay
Cheers,
- hugi
PS: The example query in the previous message was kind of nonsensical (sum on
receipt total? wat?) and I even pasted in the wrong SQL (generated by a
previous version of the example query). Pardon that, I’ll probably write a
short article and then include some actual, correct examples :)
> On 3 Feb 2025, at 13:56, Samuel Pelletier <email@hidden> wrote:
>
> Hi Hugi,
>
> So those Properties can build objects queries AND more custom queries like
> ERXQuery, seems very cool.
>
> Can you create Properties for EO methods ? I have many of them for things
> like "return a previously set value or compute a default one"...
>
> I want to play with Cayenne but have not found the real good yet... Porting a
> large application seems a bit too big to experiment a new frameworks but I
> have a personal project that seems a very good fit.
>
> Regards,
>
> Samuel
>
>
>> Le 3 févr. 2025 à 08:12, Hugi Thordarson via Webobjects-dev
>> <email@hidden> a écrit :
>>
>> Hi Samuel,
>>
>> Yes, we have a very nice querying API/syntax similar to ERXKey in Cayenne
>> (although they’re called “Properties” in the Cayenne world).
>>
>> For example, selecting all my receipts, ordering them by date and
>> prefetching their entries:
>>
>> ====
>> ObjectSelect
>> .query( Receipt.class )
>> .where( Receipt.USER.dot( User.NAME ).eq( “Hugi Þórðarson” ) )
>> .orderBy( Receipt.SHOP ).dot( Shop.NAME ).desc() )
>> .prefetch( Receipt.ENTRIES.joint();
>> .select( objectContext );
>> ====
>>
>> They’ve also evolved quite a bit in the last years along with Cayenne’s
>> SQL/querying abilities and now include fun stuff like SQL subqueries,
>> aggregates, functions and features like EXISTS and HAVING. So for a more
>> complex example, selecting a [Receipt]’s date/total, the related [Shop]’s
>> name, the number of it’s related [Entry] records, with a creation_date in
>> year 2023, where they have more than five entries, a higher total than 1.000
>> and have a related OcrResult object (nonsensical query, but yeah… it’s a
>> demo):
>>
>> ====
>> ObjectSelect
>> .query( Receipt.class )
>> .columns(
>> Receipt.DATE_ONLY,
>> Receipt.TOTAL_AS_WRITTEN_ON_RECEIPT,
>> Receipt.SHOP.dot( Shop.NAME ),
>> Receipt.ENTRIES.count()
>> )
>> .where(
>> Receipt.USER.dot( User.NAME ).in( "Hugi Þórðarson", "Ósk Gunnlaugsdóttir" )
>> .andExp( Receipt.CREATION_DATE.year().eq( 2023 ) )
>> .andExp( Receipt.OCR_RESULTS.exists() )
>> )
>> .having(
>> Receipt.ENTRIES.count().gt( 5l )
>> .andExp( Receipt.TOTAL_AS_WRITTEN_ON_RECEIPT.sum().gt( BigDecimal.valueOf(
>> 1000 ) ) )
>> )
>> .orderBy(
>> Receipt.ENTRIES.count().desc()
>> )
>> .select( oc );
>> ====
>>
>> … generating the following SQL:
>>
>> ====
>> SELECT "t0"."date_only", "t1"."name", COUNT( "t2"."id" ),
>> "t0"."total_as_written_on_receipt" FROM "fd_receipt" "t0" JOIN "fd_shop"
>> "t1" ON "t0"."shop_id" = "t1"."id" JOIN "fd_entry" "t2" ON "t0"."id" =
>> "t2"."receipt_id" JOIN "fd_user" "t3" ON "t0"."user_id" = "t3"."id" WHERE (
>> "t3"."name" = ? ) AND ( EXTRACT(YEAR FROM "t0"."creation_date") = ? ) AND
>> EXISTS (SELECT "t4"."id" FROM "fd_ocr_result" "t4" WHERE "t4"."receipt_id" =
>> "t0"."id") GROUP BY "t0"."date_only", "t1"."name",
>> "t0"."total_as_written_on_receipt" HAVING ( ( COUNT( "t2"."id" ) > ? ) AND (
>> SUM( "t0"."total_as_written_on_receipt" ) > ? ) ) ORDER BY COUNT( "t2"."id"
>> ) DESC [bind: 1->name:'Hugi Þórðarson', 2:2023, 3:5, 4:1000]
>> ====
>>
>> This showcases just a part of the features, and works so well it feels
>> almost magical at times. I could also have specified Receipt.SELF as a
>> “column” instead of fetching specific values of the Receipt entity, meaning
>> I get the entire Receipt object (with all it's associated ORM features)
>> along with it’s aggregate values. I use this quite a lot (didn’t do that in
>> the example since it makes the resulting SQL longer, since there’s a lot of
>> columns involved).
>>
>> And yes, you can use Properties to perform in-memory operations like
>> filtering and sorting.
>>
>> Receipt.CREATION_DATE.desc().orderedList( receipt );
>> Receipt.USER.dot( User.NAME ).eq( “Hugi” ).filterObjects ( receipts );
>>
>> Cheers,
>> - hugi
>>
>>
>>
>>> On 3 Feb 2025, at 12:18, Samuel Pelletier via Webobjects-dev
>>> <email@hidden> wrote:
>>>
>>> HI,
>>>
>>> Those NS collections where essentials in the first java WO mainly because
>>> at that time Java did not had real collections classes (they appeared in
>>> Java 1.8), and the name was probably kept to help porting. I did not switch
>>> to java WO at that time and maintained some objective-C apps for a long
>>> time!
>>>
>>> I mostly use the NS versions because I'm still on EOF and uses ERXKey for
>>> sort orderings, qualifier building and aggregate computation to have type
>>> checking:
>>>
>>> - EOQualifier qualifier =
>>> Evenement.DATE.greaterThanOrEqualTo(dateDebut()).and(Evenement.DATE.lessThanOrEqualTo(dateFin()));
>>> - ERXKey.sum(ContratRetenue.NB_HEURES).valueInObject(retenues);
>>> - NSArray<Etudiant> etudiants =
>>> Groupe.ETUDIANTS_ACTIFS.atFlatten().arrayValueInObject(evenement.groupes());
>>> - sortOrderings = Evenement.DATE.asc()
>>> .then(Evenement.ORDRE_AFF_MOIS_SALLE.asc())
>>>
>>> .then(Evenement.GROUPE_PRINCIPAL.dot(Groupe.SEMESTRE_DEBUT.dot(Semestre.DATE_DEBUT)).desc()
>>> .then(Evenement.HEURE_DEBUT.asc()));
>>>
>>> I still think those are more readable than creating lambda, probably mostly
>>> explained because I'm use to the syntax.
>>>
>>> Is there something like ERXKey when using Cayenne ?
>>>
>>> Regards,
>>>
>>> Samuel
>>>
>>>
>>>> Le 2 févr. 2025 à 07:21, Amedeo Mantica via Webobjects-dev
>>>> <email@hidden> a écrit :
>>>>
>>>> Iirc the NS collections were there due to simplifying porting of apps from
>>>> objc to Java. I don’t think there is any big difference in performance
>>>>
>>>> Sent from my iPhone
>>>>
>>>>> On 2 Feb 2025, at 12:18, Jérémy DE ROYER via Webobjects-dev
>>>>> <email@hidden> wrote:
>>>>>
>>>>> Hi all,
>>>>>
>>>>> Even if I still use EOF (due to inheritance limitations of Cayenne), I
>>>>> followed Hugi’s precepts :
>>>>> - « use 100% java native whenever possible »
>>>>>
>>>>> One other advantage when working in a team… is that 100% java is widely
>>>>> documented and exampled... and it's more attractive to newbees.
>>>>>
>>>>> Sorry if I don’t « really » answer the question 😄
>>>>>
>>>>> Jérémy
>>>>>
>>>>>> Le 2 févr. 2025 à 11:13, Hugi Thordarson via Webobjects-dev
>>>>>> <email@hidden> a écrit :
>>>>>>
>>>>>> When I made the switch to Java collections I did do some benchmarking.
>>>>>> Haven’t got the code anymore (this was a decade ago) but at that time,
>>>>>> the Java collection classes were faster, but the operations were really
>>>>>> so fast in both cases that the differences were negligible — at that
>>>>>> time.
>>>>>>
>>>>>> Since then, a decade of improvements has happened in the Java
>>>>>> collections so I think we can guess where you’ll find performance
>>>>>> improvements — and will keep getting performance improvements. On one
>>>>>> hand you have old classes written in an old version of Java, on the
>>>>>> other hand you have actively maintained open source classes used by
>>>>>> millions of programmers and maintained by the performance-obsessed
>>>>>> authors of Java and the JDK itself.
>>>>>>
>>>>>> And now for the opinion piece:
>>>>>> Unless you’re writing extremely performance-sensitive code — even if the
>>>>>> foundation collections were faster I think it makes sense to use Java
>>>>>> collections and write to the standard Java collection APIs where you
>>>>>> don’t *need* foundation collections, because If you’re using foundation
>>>>>> specific APIs, your code is really already obsolete at the time of
>>>>>> writing. I never regretted the switch and have hardly seen an NS*
>>>>>> collection class in my code in years, except where explicitly required
>>>>>> as a parameter for passing into WO APIs. (that story may be a little
>>>>>> different if you’re using EOF which uses the NS collections everywhere,
>>>>>> so this may not apply in that case).
>>>>>>
>>>>>> The Java collection classes do have their warts, the most obvious one to
>>>>>> us coming from the NS* world being the non-API-differentiation between
>>>>>> mutable and immutable collections (weird design oversight) but that
>>>>>> hasn't plagued me, really. It’s just something you’re aware of and don’t
>>>>>> really hit often.
>>>>>>
>>>>>> Another one for us WO users is that you can’t use KVC operators on Java
>>>>>> collections (someArray.@sortAsc, .@sum etc). When I made the switch I
>>>>>> always thought I’d miss these hugely and planned to write operator
>>>>>> support into ERXComponent’s valueForKeyPath(), but never got around to
>>>>>> it since I really didn’t miss the operators, preferring to keep my logic
>>>>>> in Java rather than templates (compile time errors and refactoring
>>>>>> support are awesome things).
>>>>>>
>>>>>> Probably just saying things you know — but I thought it might have some
>>>>>> value hearing from someone that moved to Java collections and doesn’t
>>>>>> regret it.
>>>>>>
>>>>>> Cheers,
>>>>>> - hugi
>>>>>>
>>>>>>
>>>>>>> On 2 Feb 2025, at 00:29, ocs--- via Webobjects-dev
>>>>>>> <email@hidden> wrote:
>>>>>>>
>>>>>>> Hi there,
>>>>>>>
>>>>>>> did ever anybody tried some benchmarks to find whether it is better to
>>>>>>> use WO collections (NSArray, NSDictionary...) as widely as possible (ie
>>>>>>> essentially anywhere, unless one really needs to store nulls or can't
>>>>>>> do without ConcurrentHashMap or so), or whether it's better to use
>>>>>>> standard collections (List, HashMap...) wherever they happen to work
>>>>>>> properly (which is surprisingly often, but not anywhere)?
>>>>>>>
>>>>>>> Are they roughly comparable, or are one or the others considerably
>>>>>>> better?
>>>>>>>
>>>>>>> Thanks!
>>>>>>> OC
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> Do not post admin requests to the list. They will be ignored.
>>>>>>> Webobjects-dev mailing list (email@hidden)
>>>>>>> Help/Unsubscribe/Update your Subscription:
>>>>>>>
>>>>>>> This email sent to email@hidden
>>>>>>
>>>>>> _______________________________________________
>>>>>> Do not post admin requests to the list. They will be ignored.
>>>>>> Webobjects-dev mailing list (email@hidden)
>>>>>> Help/Unsubscribe/Update your Subscription:
>>>>>>
>>>>>> This email sent to email@hidden
>>>>>
>>>>> _______________________________________________
>>>>> Do not post admin requests to the list. They will be ignored.
>>>>> Webobjects-dev mailing list (email@hidden)
>>>>> Help/Unsubscribe/Update your Subscription:
>>>>>
>>>>> This email sent to email@hidden
>>>> _______________________________________________
>>>> Do not post admin requests to the list. They will be ignored.
>>>> Webobjects-dev mailing list (email@hidden)
>>>> Help/Unsubscribe/Update your Subscription:
>>>>
>>>> This email sent to email@hidden
>>>
>>> _______________________________________________
>>> Do not post admin requests to the list. They will be ignored.
>>> Webobjects-dev mailing list (email@hidden)
>>> Help/Unsubscribe/Update your Subscription:
>>>
>>> This email sent to email@hidden
>>
>> _______________________________________________
>> Do not post admin requests to the list. They will be ignored.
>> Webobjects-dev mailing list (email@hidden)
>> Help/Unsubscribe/Update your Subscription:
>>
>> This email sent to email@hidden
>
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden