I have two columns in my database table (dueDate & lateMinutes).
How do I create a Where predicate that adds lateMinutes to dueDate and compares the result to a given date (aGivenDate).
eg. .where(dueDate + lateMinutes > aGivenDate)
Any idea how to do this with a Breeze query where predicate?
Any help appreciated.
Regards,
Paul.
There is no standard query syntax supporting date arithmetic within breeze, so your best bet for this would be to use a named query with a parameter. i.e. So assuming that the name of your EntityType was say 'Schedule' something like this
On the client
var q = EntityQuery.from("SchedulesAfter")
.where(...) // this can be any valid where clause for the 'Schedule' type ( or can be omitted completely if you don't need additional query restrictions.
.withParameter( { givenDate: aGivenDate });
On the server
[HttpGet]
public IQueryable<Schedule> SchedulesAfter(DateTime givenDate) {
// This needs to return a valid IQueryable
// You will need to find the correct EF syntax to support your query here. You may need to use an EF function here instead.
return ContextProvider.Context.Schedules
.Where(s => DbFunctions.AddMinutes(s.DueDate ,s.LateMinutes) > givenDate);
}
In effect, you are calling a custom query method on the server with a parameter and then telling the server how to implement the query using this parameter.
Related
This is using BreezeJS and a Breeze controller through to an EF provider. I have a couple of related Entities, lets call them Customer, which has a Navigation Property called Orders which links to a set of Order Entities for that customer.
What I'd like to display on the UI is a summary of Order Counts for a set of customers who match a partial name search. I can do this through returning all the Order objects, but they're quite large objects and I don't really want to return 100's of them when I don't have to. The inlineCount() method seems to always give the count of the top-level entity (Customer) rather than of the sub-Entities, no matter where I place it in the statement.
var predicate = breeze.Predicate.create('displayName', 'contains', partialName);
return this.entityQuery.from('Customers')
.where(predicate)
.orderBy('displayName')
.using(this.manager)
.expand('Orders')
.execute();
The documentation suggests that you can chain the expand in some way, but I have yet to find a syntax which is valid.
Ideally, I'd like to apply a where to the Orders by a property on Order called Status of say 0 (incomplete) and then give me just the count of those matching Orders. ie, return me all the Customer entities, but have a matching order count for each (rather than the whole list of Order objects and filter client-side).
Would appreciate any pointers in the right direction if it's possible to achieve. My current thinking is that I'll have to create a custom method on the server-side controller and do the work there, but before I make assumptions about what OData can support, I thought I'd check here for some confirmation.
So far, this is my best approach (maybe someone can correct me if there's a better way).
On the server, add this method:
public IQueryable<object> CustomerSummaries()
{
return Context.Customers.Select(p => new
{
Customer = p,
ActiveOrderCount = p.Orders.Count(o => o.Status == 1)
});
}
Then on the client end:
var predicate = breeze.Predicate.create('customer.displayName', 'contains', partialName);
return this.entityQuery.from('CustomerSummaries')
.where(predicate)
.using(this.manager)
.execute();
In my application, the query is being built by appending the first part(where clause) with the second part(order by) using a separate script like QueryBuilder.groovy and hence the order by part is prone to HQL injection which can't be sanitized by using Named Parameters. Therefore, I want to use findAll to retrieve a set of records by passing it a query and sorting and paging parameters separately. I saw an implementation like this:
domainClass.findAll(query,[namedParams],[max: 10, offset: 5])
When i passed sortColumn and sortDirection as named parameters, sortColumn worked fine but sortDirection didn't work. i need a way to either make sortDirection work as a named parameter or any other way which will combine 'sorting by direction' with the findAll result. Many people have suggested on various forums to just use the parameters directly as part of the query but it is unacceptable for my application as it will expose the query to HQL Injection.
Thanks in advance.
here is an example:
queryString = "FROM BookCatalog b WHERE b.bookNumber = :bookNumber"
this is passed to the QueryBuilder.groovy where something like this happens:
sort = "$params.sortColumn $params.sortDirection"
queryString.order(sort)
public void sort(String query){
this.query = this.query+" order by "+query
}
finally findAll retrieves the list of records:
def list = findAll(queryString,namedParams,queryParams)
so as the logic just appends the sorting parameters to the query string a potential hacker can do something like this:
bookCatalogView?offset=2&max=5&sortColumn=1,2,3 **or 1=1**
or
bookCatalogView?offset=2&max=5&sortColumn=1,2,3;**select * from whatever**
Don't concat strings, it's bad practice.
If you want to create complex queries, consider using createCriteria()
SomeDomainClass.createCriteria().list {
order("propName", "desc")
}
or, if you need more control, in the sessionFactory way:
query = sessionFactory.getCurrentSession().createCriteria(DomainClass.class)
query.addOrder(Order.asc("someField"))
query.addOrder(Order.desc("someotherField"))
I'm trying to do something like:
var predicate = breeze.Predicate.create('columnName', breeze.FilterQueryOp.Contains, 'regexHere');
manager.executeQuery(entityQuery.From('tableName').where(predicate));
It all works fine when I try to search it "normal" way, but I'd like to include regex search that would work as sql LIKE operator. Mostly, I'm interested in how to make clause similar to sqls:
WHERE columnName LIKE '%abc%def%'
Is that possible with Breeze?
Sorry but [FilterQueryOp.Contains], FilterQueryOp.StartsWith and FilterQueryOp.EndsWith are the closest query operators to what you want. The OData spec that is used to construct a query url does not support regex based queries.
That said you can use EntityQuery.withParameters to do whatever you want with any parameters passed from a client. For example
// Client side
var query = EntityQuery.from("CustomersByRegex")
.withParameters({ regex: myRegex });
// Server side
[HttpGet]
public IQueryable<Customer> CustomersByRegex(string regex) {
// use the regex here against your customers collection
// and return the resulting customers;
}
You can also mix and match the two mechanisms. i.e. a regular breeze query with Filters and a 'withParamters' call.
I have the following model:
Products (DbSet<Product>)
ProductHasWidgets (DbSet<ProductHasWidget> many-to-many w/ payload)
Widgets (DbSet<Widget>)
I am getting stuck with querying across and/or from the many-to-many table and have two questions:
How do I write a breeze query to return all Products where:
Widget.IsActive == true
ProductHasWidgets.WidgetId == 1
You can't do that from a Breeze client yet because Breeze does not support the any keyword at this time.
You can write a service method to do the query on the server. The client can pass query parameters to that method using the BreezeJS withParameters query verb.
As of Breeze 1.4.6, Breeze now supports "any" and "all" queries. See: http://www.breezejs.com/documentation/query-examples
Depending on your model, this means that your query would look something like:
var predicate = breeze.Predicate.create("WidgetId", "==" 1)
.and("Widget.IsActive", "==", true);
var query = EntityQuery.from("Products")
.where("ProductHasWidgets", "any", predicate);
Getting an error client side with breeze: "Cannot call method 'map' of undefined" when trying to pull over some data. The difference between this action and one that works is that this action is calling a stored procedure and returning ObjectResult<T> instead of DbSet<T>.
Might this be why I get an error? Using Chrome Developer tools, I do see that the breeze controller is returning json data.
I have created a complex model type in the edmx for mapping the rows returned from the stored procedure.
The action in the breeze controller has a return type of IEnumerable<T>.
I experienced the same error when using an EF complex type. A workaround was to create a view in my database instead of using a complex type, set the stored procedure to return a type of the new view which had a primary key and then it worked. It would seem that breeze requires entities to have a primary key defined.
Hm... not quite sure what is happening, so just guessing here, but try adding an AsQueryable() to the result returned, and changing the result type to a IQueryable.
We don't have any stored proc tests for breeze yet, but this is impetus for me to add some :)
I had the very same issue, but thank God I figured out a solution. Instead of using a stored procedure, you should use a view, as Breeze recognizes views as DbSet<T>, just like tables. Say you have a SQL server table that contains two tables Customers and Orders.
Customers (**CustomerId**, FirstName, LastName)
Orders (OrderId, #CustomerId, OrderDate, OrderTotal)
Now, say you want a query that returns orders by CustomerId. Usually, you would do that in a stored procedure, but as I said, you need to use a view instead. So the query will look like this in the view.
Select o.OrderId, c.CustomerId, o.OrderDate, o.OrderTotal
from dbo.Orders o inner join dbo.Customers c on c.CustomerId = o.CustomerId
Notice there is no filtering (where ...). So:
i. Create a [general] view that includes the filtering key(s) and name it, say, OrdersByCustomers
ii. Add the OrdersByCustomers view to the entity model in your VS project
iii. Add the entity to the Breeze controller, as such:
public IQueryable<OrdersByCustomers> OrdersByCustomerId(int id)
{
return _contextProvider.Context.OrdersByCustomers
.Where(r => r.CustomerId == id);
}
Notice the .Where(r => r.CustomerId == id) filter. We could do it in the data service file, but because we want the user to see only his personal data, we need to filter from the server so it only returns his data.
iv. Now, that the entity is set in the controller, you may invoke it in the data service file, as such:
var getOrdersByCustomerId = function(orderObservable, id)
{
var query = breeze.EntityQuery.from('OrdersByCustomerId')
.WithParameters({ CustomerId: id });
return manager.executeQuery(query)
.then(function(data) {
if (orderObservable) orderObservable(data.results);
}
.fail(function(e) {
logError('Retrieve Data Failed');
}
}
v. You probably know what to do next from here.
Hope it helps.