Hi Ricardo, You need to create a branch (push from your local) in your GitHub fork of Wonder for each set of changes in GitHub (aka a Feature Branch), then make a pull request from that branch to Wonder. Otherwise all of your changes get inter-mixed.
Chuck From: Ricardo Parada <rpar...@mac.com> Date: Saturday, March 18, 2017 at 11:22 PM To: "webobjects-dev@lists.apple.com" <webobjects-dev@lists.apple.com> Cc: Markus Ruggiero <mailingli...@kataputt.com>, Paul Hoadley <pa...@logicsquad.net>, "koa...@icloud.com" <koa...@icloud.com>, "g...@knuckleheads.net" <g...@knuckleheads.net>, Chuck Hill <ch...@gevityinc.com>, "aa...@chatnbike.com" <aa...@chatnbike.com> Subject: Re: ERXQuery Preview Hi all, As promised, I have committed ERXQuery to my Wonder clone on GitHub, i.e. rparada/wonder. I just need help creating the pull request. :-) It turns out that I created first a pull request for ERXExtensions.java unrelated to ERXQuery. Now when I tried to create the pull request for ERXQuery it just seems to show the previous pull request. So I’m not sure what is going on. I don’t have much experience with GitHub. Anyways, the ERXQuery is an additive change to Wonder and consists of only three files: ERXQuery.java, ERXQueryAttributes and ERXQueryEOAttribute. It’s the last two commits in rparada/wonder. I took the time to polish the API a bit more. I think the API came out nicer than what I had the first time around. Specially the single variadic select() method that allows you to specify EOAttributes, Strings, ERXKeys, or any Iterable containing these. Same for groupBy(). And the a similar technique with the orderBy() which can take EOSortOrderings or any Iterable (ERXSortOrderings, NSArray, List, Collection). The documentation is more extensive now. I have included here a copy of the ERXQuery class from the javadoc tab in my Eclipse. I have tested with ORACLE which is the easiest for me to test. It would be great to hear others using this with other databases (h2, derby, mssql, mysql, frontbase, etc.) ERXQuery.java Overview This class has a fluent API that mimics a select statement: NSArray<NSDictionary<String,Object>> records = ERXQuery.create() .select (keys) .from (entity) .where (qualifier) .groupBy (groupings) .having (havingQualifier) .orderBy (sortings) .fetch(); It allows you to use EOF/Wonder higher-level constructs (qualifiers, attributes, orderings, key paths, ERXKeys, etc.) to create a query that looks like this: SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... Specifying the Attributes to Fetch The select() method is very flexible and powerful. It accepts a variable number of objects of different types that specify the attributes to fetch. These objects can be EOAttributes, ERXKeys, Strings. You may also specify NSArray or List objects containing any combination of these (EOAttributes, ERXKeys, Strings). The ERXKeys and String objects correspond to keys and key paths to the attributes to fetch, i.e. "customer.name". The keys and key paths can also be relationships to objects, i.e. "customer" which translate into a fetch of foreign keys used to build object faults and return them in the results. You may call the select() method multiple times to keep adding to the list of attributes to fetch. Using Ad Hoc Attributes It is very common to aggregate attributes in these queries. For this purpose, you may want to create what ERXQuery refers to as ad hoc attributes. These attributes have a definition but are not physically attached to the entity. You can use the ERXQueryAttributes class to easily create multiple ad hoc attributes. The definition of the attribute can reference relationships and attributes as shown below. If you just want to create a single ad hoc attribute you may use the ERXQueryEOAttribute class. // Using a single query against the order entity to count the number of // orders and line items that match an order qualifier. ERXQueryAttributes attributes = ERXQueryAttributes.create(orderEntity) .add("itemCount", "COUNT(DISTINCT lineItems.lineItemID)", "intNumber") .add("orderCount", "COUNT(DISTINCT orderID)", "intNumber"); ERXQuery query = ERXQuery.create() .select (attributes) .from (orderEntity) .where (qualifier); // Fetch into a dictionary NSDictionary<String,Object> row = query.fetch().lastObject(); int orderCount = ((Number) row.objectForKey("orderCount")).intValue(); int itemCount = ((Number) row.objectForKey("itemCount")).intValue(); Fetching Results into a Custom Class It is useful to fetch results into objects of a custom class. This allows you to have type checking on the getter methods and add methods for computed values on the data fetched. For the example above you could have fetched the results into a custom class as follows: // Fetch into object instances of the a custom Result class Result result = query.fetch(editingContext, Result.class).lastObject(); int orderCount = result.orderCount(); int itemCount = result.itemCount(); The Result custom class would have to be defined as shown below. The constructor may keep the mutable dictionary passed in to the constructor or make an immutable copy from it as shown below. public static class Result { NSDictionary<String,Object> data; public Result(EOEditingContext ec, NSMutableDictionary<String,Object> row) { data = row.immutableClone(); } public int itemCount() { return ((Number) data.objectForKey("itemCount")).intValue(); } public int orderCount() { return ((Number) data.objectForKey("orderCount")).intValue(); } } } In general, fetching into a custom class can be done in several ways: // If your custom class has a constructor that takes an editing context and // a mutable dictionary then it is very simple: NSArray<Foo> objs = query.fetch(editingContext, Foo.class); // Using java 8 or later you may use a lambda expression: NSArray<Foo> objs = query.fetch(editingContext, (ec, row) -> new Foo(ec, row)); // You may also create an implementation of the RecordConstructor // functional interface and pass it into the fetch method: ERXQuery.RecordConstructor<Foo> recordConstructor = new ERXQuery.RecordConstructor<Foo> { @Override public Foo constructRecord(EOEditingContext ec, NSMutableDictionary row) { return new Foo(ec, row); } }; NSArray objs = query.fetch(editingContext, recordConstructor) } Augmenting Row Values You can have entries from a dictionary added in to the rows fetched from the database. The mutable dictionary passed in to the record constructor will contain the data fetched along with the keys/values from this recordInitializationValues dictionary. NSDictionary<String,Object> recordInitializationValues = new NSDictionary<>((Object)2017, "preferredYear"); NSArray<Foo> objs = query.fetch(editingContext, recordInitializationValues, Foo.class); Foo aFoo = objs.lastObject(); int preferredYear = aFoo.preferredYear(); // i.e. 2017 Defining Ad Hoc Attributes in the Entity An alternate way to define your ad hoc attributes is to define them in your entity and flagging them as non-class properties. Unlike ERXQueryEOAttribute objects, these attributes will be instances of EOAttribute and reside in your entity. They may be a bit distracting when looking at the entity if you have a lot but this method allows you to reuse all the existing attributes and relationships already defined in the entity and does not require code for creating the attributes. One incovenience is that eogeneration templates do not generate ERXKeys for non-class properties. However, this problem could be overcome by enhancing the eogeneration templates to generate ERXKeys for derived non-class property attributes. // Fetch last year's customer order totals exceeding $1000 in descending order NSArray<OrderSummary> lastYearTopSales = ERXQuery.create() .select (Order.CUSTOMER) // customer to-one .select (Order.SUM_TOTAL_AMOUNT) // non-class property defined as SUM(totalAmount) .from (Order.ENTITY_NAME) .where (lastYearQualifier) .groupBy (Order.CUSTOMER) .having (Order.SUM_TOTAL_AMOUNT.greaterThan(1000.00)) .orderBy (Order.SUM_TOTAL_AMOUNT.desc()) .fetch(editingContext, OrderSummary.class); // Peek at top sale record OrderSummary topSale = ERXArrayUtilities.firstObject(lastYearTopSales); if (topSale != null) { System.out.println("Customer " + topSale.customer().fullName() + " ordered " + moneyFormatter.format(topSale.sumTotalAmount())); } } It would be nice to enhance the eogeneration templates to also create a custom class for fetching the results, i.e. WonderEntitySummary.java and _WonderEntitySummary.java with the getters for attributes/relationships in the entity including derived non-class properties. These templates would be used when the entity has a user info key with ERXQuery.enabled=yes. Limitations Ad hoc attributes created with ERXQueryAttributes or ERXQueryEOAttribute are not physically attached to an entity. When EOF generates SQL for a qualifier it calls sqlStringForSQLExpression(q,e) where q is an EOQualifier and e is an EOSQLExpression. Qualifiers then try to reach the attribute by following the qualifier's referenced keys starting with the entity of the EOSQLExpression, i.e. e.entity(). The current workaround used by ERXQuery is to temporarily add to the entity any ad hoc attributes referenced by the qualifiers. This typically happens with the havingQualifier which normally references the ad hoc attributes corresponding to aggregated attributes. For example, "sumTotalAmount" defined as "SUM(totalAmount)" could be used in a having qualifier: // When grouping orders by customer and fetching sumTotalAmount we may want to have // this having qualifier so that we only fetch the groups totaling more than 1000. EOQualifier havingQualifier = ERXQ.greaterThan("sumTotalAmount", new BigDecimal(1000.0)); However, if you were to define your "sumTotalAmount" attribute in your entity as a derived non-class property with definition "SUM(totalAmount)" then ERXQuery doesn't have to add the attribute to the entity. Defaults for Behavior Properties 1. er.extensions.eof.ERXQuery.useBindVariables=false 2. er.extensions.eof.ERXQuery.useEntityRestrictingQualifiers=true 3. er.extensions.eof.ERXQuery.removesForeignKeysFromRowValues=true Author: Ricardo J. Parada On Mar 14, 2017, at 8:31 AM, Ricardo Parada <rpar...@mac.com<mailto:rpar...@mac.com>> wrote: Thanks all for the feedback. I'll be adding it by the weekend I hope. On Mar 14, 2017, at 4:29 AM, Markus Ruggiero <mailingli...@kataputt.com<mailto:mailingli...@kataputt.com>> wrote: Great thing, looks very useful. On 11.03.2017, at 23:41, Ricardo Parada <rpar...@mac.com<mailto:rpar...@mac.com>> wrote: Hi all, Over the years, I’ve worked on an class for doing ad hoc queries. This class has gotten better and better. At first, the API of this class and some implementation details were not ready for public consumption in my opinion. However, recently I took the time to polish the code as much as possible and if I can get my employer's approval I would like to contribute this code to project Wonder which has given us so much in return during the years. Here is a preview of its functionality. Please let me know if you guys think this would be a useful contribution. ERXQuery.java This class has a fluent API that mimics a select statement: NSArray<NSDictionary<String,Object>> records = ERXQuery.create() .select (keys) .from (entity) .where (qualifier) .groupBy (groupings) .having (havingQualifier) .orderBy (sortings) .fetch(); Overview ERXQuery allows you to use EOF constructs (EOQualifiers, EOAttributes, EOSortOrdering, EOAttribute names, relationships, etc.) to create queries of the form: SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... Using Ad Hoc Attributes You may use the ERXAdHocAttributes class to easily create ad hoc attributes to use with your query. For example, the code below creates two ad hoc EOAttributes. An ad hoc attribute is an EOAttribute that is not physically attached to an entity and that has a definition. They are useful for aggregating other attributes. The definition of the attribute can reference relationships and attributes as shown below. // Using a single query against the order entity, count the number of // orders and line items that match the qualifier. ERXAdHocAttributes attributes = ERXAdHocAttributes.create(orderEntity) .add("itemCount", "COUNT(DISTINCT lineItems.lineItemID)", "intNumber") .add("orderCount", "COUNT(DISTINCT orderID)", "intNumber"); NSDictionary<String,Object> row = ERXQuery.create() .select (attributes) .from (orderEntity) .where (qualifier) .fetch() .lastObject(); int orderCount = ((Number) row.objectForKey("orderCount")).intValue(); int itemCount = ((Number) row.objectForKey("itemCount")).intValue(); Fetching into a Custom Record Class You can also fetch using a custom class of your choice, i.e. Foo, as shown below: // Using java >= 8 is easy with a lambda expression NSArray<Foo> objs = query.fetch(editingContext, (ec, row) -> new Foo(ec, row)); // Using java < 8 you must provide a RecordConstructor ERXQuery.RecordConstructor<Foo> recordConstructor = new ERXQuery.RecordConstructor<Foo> { @Override public Foo constructRecord(EOEditingContext ec, NSMutableDictionary row) { return new Foo(ec, row); } }; NSArray objs = query.fetch(editingContext, recordConstructor); Author: Ricardo J. Parada _______________________________________________ Do not post admin requests to the list. They will be ignored. Webobjects-dev mailing list (Webobjects-dev@lists.apple.com<mailto:Webobjects-dev@lists.apple.com>) Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/webobjects-dev/mailinglists%40kataputt.com This email sent to mailingli...@kataputt.com<mailto:mailingli...@kataputt.com> Markus Ruggiero mailingli...@kataputt.com<mailto:mailingli...@kataputt.com> Check out the new book about Project Wonder and WebObjects on http://learningthewonders.com/
_______________________________________________ Do not post admin requests to the list. They will be ignored. Webobjects-dev mailing list (Webobjects-dev@lists.apple.com) Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com