I have a collection of objects called Parents (some without children yet), and a related collection of Children.
I have all parents and children cached locally. For certain views I want to show just those Parents who have children.
I'm having trouble figuring how to do this.
I've tried
breeze.EntityQuery
.from("Parents")
.where("Children", "!=", null)
This returns all Parents.
I've also tried
breeze.EntityQuery
.from("Children")
.select("Parents")
This returns duplicate parents for families with more than one child. In addition it returns simple objects, not breeze entities.
I've also tried
breeze.EntityQuery
.from("Parents")
.where("Children", "!=", [])
&
breeze.EntityQuery
.from("Parents")
.where("Children.length", ">", 0)
Is there a way to do this?
Thanks!
Updated post: 11/25/13
As of Breeze 1.4.6, 'any' and 'all' operators are now supported.
Older post
Breeze doesn't yet support 'any' and 'all' query operators, (which is what would allow this operation) but they are on our roadmap. Please vote for this on the Breeze User Voice.
If you only need to determine this locally then the easy workaround would probably be to just use this: ( untested code, so there might be typos).
// assuming "Parent" is the name of the entity type corresponding to the "Parents" endpoint
var parentEntitiesWithChildren = myEntityManager.getEntities("Parent").filter(function(parent) {
return parent.getProperty("Children").length > 0;
});
Related
I am getting a dynamic value (_FilterDate) on the parent type that I want to use as a filter for the nested type /Trips but can't get it to work because I still get entries in the nested data that do not meet the filter. Actually, there is no difference whether I use this filter or not.
$filter=Trips/all(d:d/EndDate ge _FilterDate)
I also tried this:
$expand=Trips($filter=EndDate ge $it/_FilterDate)
but got the error: "Could not find a property named '_FilterDate' on type 'Default.Trips'."
So I'm wondering how to get the syntax right and thus kindly ask for help.
Example portion:
"value": [
{
"_FilterCompany": "YES",
"_FilterLocation": "YES",
"_FilterDate": "2020-01-08",
"Trips": [
{
"StartDate": "2019-06-24",
"EndDate": "2019-06-28",
},
{
"StartDate": "2020-02-07",
"EndDate": "2020-02-07",
}
]
}
There are two issues going on here:
this response is specifically regarding the OData v4 specification and the .Net ODataLib implementation.
You have correctly identified that when filtering results based on a nested collection you you must separately apply the filter within that collection if you want the fitler to apply to the items within that collection as well.
This is because the root level $filter criteria only affects the selection of the root items, think of it as if the $expand operator is applied after the $filter has identified the top level of row the return, $expand is simply executed as a Linq Include statement.
In your second attempt, $it references the instance of Trips, which is a known bug/by design, according to the spec it is expected to work the way you have implemented it:
5.1.1.6.4 $it
Example 82: customers along with their orders that shipped to the same city as the customer's address. The nested filter expression is evaluated in the context of Orders; $it allows referring to values in the outer context of Customers.
http://host/service/Customers?
$expand=Orders($filter=$it/Address/City eq ShipTo/City)
So knowing the $it is broken, the spec doc does specify a $root identifier that you might also be able to use, but in ODataLib 7.3 $root is still not supported OOTB either. There is an issue logged here: $it references the wrong resource #616
Workaround
If your Trips data type has a navigation property back to the Filter/root record, then you can use that navigation property as part of the $filter:
Assuming the navigation property is called Filter
$filter=Trips/all(d:d/EndDate ge _FilterDate)&$expand=Trips($filter=EndDate ge Filter/_FilterDate)
If your Trips type does not have this navigation link back to the parent record then you are stuck at this stage with these two workarounds:
Create a Function on the controller to return this filtered data specifically, as this would be simple to evaluate as a Linq query in the server-side.
Accept that the server will return extra rows in the Trips collections, and apply the filter over the results in the client-side.
I have the following code
PagedResultList res = myService.getPage(paginateParams, ...)
println res.size() // returns 2
println res.getTotalCount() // returns 1
getPage looks like:
def criteria = MyDomain.createCriteria()
criteria.list(max: paginateParams.max, offset: paginateParams.offset) { // max is 10, offset is 0, sortBy is updatedAt and sortOrder is desc
eq('org', org)
order(paginateParams.sortBy, paginateParams.sortOrder)
}
why do the two method return different values? The documentation doesn't explain the difference, but does mention that getTotalCount is for number of records
currently on grails 2.4.5
edits:
println on res prints out:
res: [
com.<hidden>.MyDomain: 41679f98-a7c5-4193-bba8-601725007c1a,
com.<hidden>.MyDomain: 41679f98-a7c5-4193-bba8-601725007c1a]
Yes, res has a SINGLE object twice - that's the bug I'm trying to fix. How do I know that? I have an primary key on MyDomain's ID, and when I inspect the database, it's also showing one record for this particular org (see my criteria)
edit 2: I found this comment (http://docs.grails.org/2.4.5/ref/Domain%20Classes/createCriteria.html)
listDistinct If subqueries or associations are used, one may end up
with the same row multiple times in the result set. In Hibernate one
would do a "CriteriaSpecification.DISTINCT_ROOT_ENTITY". In Grails one
can do it by just using this method.
Which, if I understand correctly, is their way of saying "list" method doesn't work in this scenario, use listDistinct instead but then they go on to warn:
The listDistinct() method does not work well with the pagination
options maxResult and firstResult. If you need distinct results with
pagination, we currently recommend that you use HQL. You can find out
more information from this blog post.
However, the blog post is a dead link.
Related: GORM createCriteria and list do not return the same results : what can I do?
Not related to actual problem after question edited but this quote seems useful
Generally PagedResultList .size() perform size() on resultList property (in-memory object represent database record), while .getTotalCount() do count query against database. If this two value didn't match your list may contain duplicate.
After viewing related issues (GORM createCriteria and list do not return the same results : what can I do?) I determined that there were several approaches:
Use grails projection groupBy('id') - doesn't work b/c i need the entire object
USe HSQL - Domain.executeQuery - actually this didn't work for my scenario very well because this returns a list, whereas criteria.list returns a PagedResultList from which I previously got totalCount. This solution had me learning HSQL and also made me break up my existing logic into two components - one that returned PagedResultList and one that didn't
Simply keep a set of IDs as I process my PagedResultList and make sure that I didn't have any duplicates.
I ended up going with option 3 because it was quick, didn't require me to learn a new language (HSQL) and I felt that I could easily write the code to do it and I'm not limited by the CPU to do such a unique ID check.
I am using breeze to call a web api method which is:
Repository.ShipmentAppeals.Where(sa => sa.ShipmentID == shipmentID).Select(sa => sa.Appeal).Include("Case");
My breeze query looks like:
var query = EntityQuery.from('GetEditShipmentAppeals')
.withParameters({ shipmentID: shipmentID, caseID: caseID })
.orderByDesc("Case.ID")
GetEditShipmentAppeals is a web api method that contains the first query. In spite of using .Include("Case") in the query I am not able to use "Case.ID" in the order by clause of breeze query.
var query = EntityQuery.from('Appeals')
.expand("Case,Patient")
.orderByDesc("Case.ID").inlineCount();
Even if I use navigation property on a breeze query that does not involve a EF query in web api, it does not work. In above query Case is a navigation property in Appeal table.
If I understand your example correctly, then I think that this is an Entity Framework issue. My understanding is that Entity Framework does not support "Includes" on a projection. See http://connect.microsoft.com/VisualStudio/feedback/details/347543/entity-framework-eager-loading-not-working-in-some-projection-scenarios.
To confirm this, I would try executing your EF query in isolation and see if the "Include" is actually doing anything. My guess is that it isnt.
However, you can still accomplish what you want with a slightly different server side projection. ( I'm not sure what object 'Case' is a property of and the syntax may be a bit off but...) Something like:
Repository.ShipmentAppeals.Where(sa => sa.ShipmentID == shipmentID).Select(sa => new
{ Appeal: sa.Appeal, Case: sa.Appeal.Case, CaseId: sa.Appeal.Case.Id });
Note that Breeze will return a collection of 'anonymous' javascript objects from this query, but each of the 'entities' within each of these objects (i.e. Appeals and Cases) will be full Breeze entities and will be part of the EntityManager cache.
So we want to be able to search for a person by their phone number. From this SO post I gather that adding a where clause through a navigation property that is a list doesn't work in breeze yet.
That is ok with me because breeze provides an awesome way to do server side properties. However when I write my query on the server I need the extra where clause to be added as an "OR" to the query so that is doesn't interfere with where clauses that I already have on the client. I am doing this on the server but it isnt working. (Note this is using the DevForce Predicate Builder):
var pred = PredicateBuilder.False<Person>();
pred.Or(x => x.PhoneNumbers.Any(y => y.Value.StartsWith(searchString)));
var qry = _contextProvider.Context.People.Where(pred);
Am I building the predicate wrong or is what I am doing being added as and "And" clause and thus interfering with my other where clauses from the client?
Updated post: 11/25/13
As of Breeze 1.4.6, 'any' and 'all' operators are now supported.
So your client side Breeze query would look something like:
var query = EntityQuery.from("People").where("PhoneNumbers", "any", "Value", "startsWith", searchString);
This assumes that you have a "People" endpoint that returns a person object that has a "PhoneNumbers" property that in turn has its own "Value" property.
Also see: http://www.breezejs.com/documentation/query-examples
Older post
Breeze does support or'ing predicates together as in:
var pred = Predicate.create("ShipCity", "stArtsWiTH", "F")
.or("ShipCity", "startswith", "C");
var q = EntityQuery.from("Orders").where(pred);
What breeze does not yet support are the 'any' and 'all' operators. Although these will be supported in the near future.
Works: My UI sends up entity sql sort expressions from a jqGrid to my DAL and then applies some Where clauses that get returned to my service layer. My service layer will then create a PaginatedList object that applies the Skip()..Take() to the IQueryable.
Example:
var qry = ((IObjectContextAdapter)DbContext).ObjectContext
.CreateQuery<TEntity>(entityName)
.OrderBy(pEntitySQLSort.GetEntitySQL());
//GetEntitySQL() i.e. "it.WorksheetID ASC"
return qry.Where(p=> pStatus == "blah").Skip(5).Take(10);
Doesn't Work: Applying a GroupBy() then Select() that returns a list of the same type of entities (Worksheet).
Example:
var qry = ((IObjectContextAdapter)DbContext).ObjectContext
.CreateQuery<TEntity>(entityName)
.OrderBy(pEntitySQLSort.GetEntitySQL());
var qryGrouped = qry.GroupBy(pWorksheet => pWorksheet.ParticipantID)
.Select(pGroup => new {Group = pGroup, LatestWorksheetID = pGroup.Max(pWorksheet => pWorksheet.WorksheetID)})
.Select(p => p.Group.FirstOrDefault(pWorksheet => pWorksheet.WorksheetID == p.LatestWorksheetID));
return qryGrouped.Skip(5).Take(10); //throws exception.
Throws NotSupportedException: The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'.
It seems to me that the first snippet does return an IOrderedQueryable that applies the esql sorting expression but the second snippet does not? Or maybe does GroupBy() remove the ordering of a query/collection ? If this is is the case, and since esql must be applied BEFORE LINQ to Entities, how could I accomplish the sql sorting + LINQ GroupBy ?
Related:
When is ObjectQuery really an IOrderedQueryable?
Why can't we mix ESQL and LINQ TO Entities
GroupBy() returns an IGrouping<int, Worksheet>. The actual object is an ObjectQuery returned as IQueryable. You linked to my question (When is ObjectQuery really an IOrderedQueryable?), so you know that the fact that this ObjectQuery also implements IOrderedQueryable does not necessarily mean that it actually behaves as such.
In a more elementary, related, question, Jon Skeet made the distinction between actual type and compile-time type. The compile-time type (IQueryable) is what matters.
So, GroupBy effectively cancels the previous OrderBy. In a quick test on a similar case I could see that even the ordering is gone in the grouping. Conclusion: you'll have to re-apply an OrderBy for the Skip to be executed successfully.