breeze query where clause is not executed - breeze

I'm quite new to using breeze and at the moment stuck with something which seems very simple.
I have a API call which returns 4 locations. Then using breeze, I'm trying to filter it down using a where clause as follows:
function getLocations(clientId) {
var self = this;
return EntityQuery.from('GetLocations')
.withParameters({ clientId: clientId })
.where("activeStatus", "==", "0")
.expand('LocationType')
.using(self.manager)
.execute()
.then(querySucceeded, this._queryFailed);
function querySucceeded(data) {
if (data.results.length > 1) {
locations = data.results;
}
return locations;
}
}
Ideally, this should give me 0 rows, because in all 4 rows the 'activeStatus' is 1. However, it still shows me all 4 results. I tried with another filter for locationType, and it's the same result. The breeze side where clause does not get executed.
Update to answer the questions:
Following is how the API call in my controller looks like:
public object GetLocations(int clientId) {
}
As you see it only accepts the clientId as a parameter hence I use the with parameter clause. I was thinking that breeze will take care of the activeStatus where clause and I don't have to do the filter on that in the back-end. Is that wrong?
Can someone help with this?

The Breeze documentation indicates that the withParameters is usually used with non-.NET backends or servers which do not recognize oData URIs. Is it possible that the where clause is being ignored because of .withParameters? Can't you rewrite the where clause using the clientID filter?
function getLocations(clientId) {
var self = this;
var p1 = new breeze.Predicate("activeStatus","==","0");
var p2 = new breeze.Predicate("clientId","==",clientId);
var p = p1.and(p2)
return EntityQuery.from('GetLocations')
.where(p)
.expand('LocationType')
.using(self.manager)
.execute()
.then(querySucceeded, this._queryFailed);
function querySucceeded(data) {
if (data.results.length > 1) {
locations = data.results;
}
return locations;
}
}
I'd try this first. Or put the where clause in the withParameters statement, depending on your backend. If that doesn't work, then there might be other options.
Good Luck.
EDIT: An example that I use:
This is the API endpoint that I query against:
// GET: breeze/RST_ClientHistory/SeasonClients
[HttpGet]
[BreezeQueryable(MaxExpansionDepth = 10)]
public IQueryable<SeasonClient> SeasonClients()
{
return _contextProvider.Context.SeasonClients;
}
And here is an example of a query I use:
// qFilters is object. Values are arrays or strings, keys are id fields. SeasonClients might also be Clients
// Setup predicates
var p, p1;
// link up the predicates for passed data
for (var f in qFilters) {
var compareOp = Object.prototype.toString.call(qFilters[f]) === '[object Array]' ? 'in' : '==';
if (!qFilters[f] || (compareOp == 'in' && qFilters[f].length == 0)) continue;
fLC = f.toLowerCase();
if (fLC == "countryid") {
p1 = breeze.Predicate("District.Region.CountryId", compareOp, qFilters[f]);
} else if (fLC == "seasonid") {
p1 = breeze.Predicate("SeasonId", compareOp, qFilters[f]);
} else if (fLC == "districtid") {
p1 = breeze.Predicate("DistrictId", compareOp, qFilters[f]);
} else if (fLC == "siteid") {
p1 = breeze.Predicate("Group.Site.SiteId", compareOp, qFilters[f]);
} else if (fLC == "groupid") {
p1 = breeze.Predicate("GroupId", compareOp, qFilters[f]);
} else if (fLC == "clientid" || fLC == 'seasonclientid') {
p1 = breeze.Predicate("ClientId", compareOp, qFilters[f]);
}
// Setup predicates
if (p1) {
p = p ? p.and(p1) : p1;
}
}
// Requires [BreezeQueryable(MaxExpansionDepth = 10)] in controller
var qry = breeze.EntityQuery
.from("SeasonClients")
.expand("Client,Group.Site,Season,VSeasonClientCredit,District.Region.Country,Repayments.RepaymentType")
.orderBy("DistrictId,SeasonId,GroupId,ClientId");
// Add predicates to query, if any exist
if (p) qry = qry.where(p);
return qry;
That's longer than it needs to be, but I wanted to make sure a full working example is in here. You will notice that there is no reason to use .withParameters. As long as the Context is set up properly on the server, chaining predicates (where clauses) should work fine. In this case, we are creating where clauses with up to 10 ANDs filtering with strict equality or IN a collection, depending on what is passed in the qFilters Object.
I think you should probably get rid of the parameter in your backend controller, make the method parameterless, and include the clientId match as an additional predicate in your query.
This approach also makes your API endpoint much more flexible -- you can use it for a wide variety of queries, even if the ClientId has nothing to do with them.
Does this help? Let me know if you have any more questions?

Related

breezeJS custom predicates

I want to write a slightly complex query, so I wonder if there is a way to provide a custom function as the predicate of a where clause.
For example, it would be nice if we could do something like:
var myArray = [1, 2, 3];
var filter = function (person) {
return elementExists(person.id, myArray);
};
EntityQuery.from('persons').toType('Person')
.where(filter);
Looking at the source code I realized that such capability is not present in the latest version of BreezeJS (i might be wrong).
I just wonder if breeze supports anything similar to that.
On the client side you should be able to find out if the element exists by checking the length of the return.
var getItems = function (runId, tankId, topicId) {
var localquery = EntityQuery.from("Items")
.using(manager);
var p1 = new breeze.Predicate("runId", "eq", runId);
var p2 = breeze.Predicate("tankId", "eq", window.app.vm.tanks.activetank());
var p3 = breeze.Predicate("topicId", "eq", topicId);
var p4 = breeze.Predicate("topicId", "eq", app.Topics.Growth_Topic);
var pred;
var runId = p1._value;
var tankId = p2._value;
// If the third parameter exists, add it to your complex predicate
// adding a specific Topic to the predicate
// Otherwise only add the General topic
if (p4)
pred = breeze.Predicate.or([p3, p4]);
else
pred = breeze.Predicate.or(p3);
var newpred = breeze.Predicate.and([p1, p2, pred]);
// newpred is now querying for a specific runId and tankId and (p3 or p4) if p4 exists
// otherwise it is querying for runId and tankId or (p3)
// So look in the local metadataStore first
var queryb = localquery.where(newpred);
var results = manager.executeQueryLocally(queryb);
// If we do have it locally use it
if (results.length) {
window.app.vm.Items.Items(results);
}
// otherwise get it from the database
else {
var query = EntityQuery
.from("Items")
.where(newpred);
// return the promise from breeze
return manager.executeQuery(query)
.then(function (data) {
// check to see if the call to the webapi controller returned any data
if (data.length > 0) {
// stick it into the viewmodel it to your viewmodel
window.app.vm.Items.Items(data.results);
return "element exists";
} else {
return "element does not exist";
}
})
.fail(queryFailed);
}
};
This example is quite a bit more complicated that you requested, so cut out the parts you do not want to simplify your query. I am showing you here how to get both an "and" and an "or" into your query. I am also showing how to check the local metadataStore, to see if the item is there before going to the server.
If the item does not exist and you want to create it, be careful about your thread timing and wrap the object creation in a promise before you do something like navigating to another page to show the new item. The navigation may be faster than the creation function and the new item may not be bound into your viewmodel yet by knockout, so it might show up blank on the new page. That will drive you crazy for quite a while.

Asp.net MVC 4 Database Query returning sql query.

I am trying to query my database to get a specific data from my database. however when I convert the query to string it doesn't return the select value, instead it returns the whole SQL Query in a string. I am stumped on why this is happening
public ActionResult StudiedModules()
{
Model1 studiedModules = new Model1();
List<StudiedModulesModel> listModules = new List<StudiedModulesModel>();
using (EntityOne context = new EnitityOne())
{
foreach(var module in context.StudiedModules){
studiedModules.School = context.ModuleDatas.Where(p=>p.ModuleCode == module.ModuleCode).Select(u=>u.School).ToString();
studiedModules.Subject = context.ModuleDatas.Where(p=>p.ModuleCode == module.ModuleCode).Select(u=>u.Subject).ToString();
}
}
var data = listModules;
return View(data);
}
Calling ToString() on an Entity Framework Linq query like that will in fact return the SQL Query. So as it's written, your code is doing exactly what you wrote it to do.
If you want to select the first result from the IQueryable<T>, then you need to call First() before your ToString(). So, try changing
studiedModules.School = context.ModuleDatas.Where(p=>p.ModuleCode == module.ModuleCode).Select(u=>u.School).ToString();
studiedModules.Subject = context.ModuleDatas.Where(p=>p.ModuleCode == module.ModuleCode).Select(u=>u.Subject).ToString()
to
studiedModules.School = context.ModuleDatas.Where(p=>p.ModuleCode == module.ModuleCode).Select(u=>u.School).First().ToString();
studiedModules.Subject = context.ModuleDatas.Where(p=>p.ModuleCode == module.ModuleCode).Select(u=>u.Subject).First().ToString()
There are a whole lot more methods available depending on what you're trying to accomplish. If you want to get a list, use ToList(), or as Uroš Goljat pointed out, you can get a comma-separated list of values using the Aggregate( (a, b)=> a + ", " + b) method.
How about using Aggregate( (a, b)=> a + ", " + b) instead of ToString().
Regards,
Uros

Filtered tokenised search result with Linq and EF

I'm looking for a way to create a filtering system based upon tokenised query strings to return a list of farms.
The filtering mechanism would hopefully be flexible enough for me to supply the tokens in any order to return results.
The rules for search would be like this:
state:WA crop:Banana
would give me a filtered list of all farms in WA with the crop banana.
crop:Banana state:WA
should return the same result.
city:Albany crop:Banana
would give me a filtered list of all farms in Albany with the crop banana.
Each of the values supplied could be wrapped in quotation marks to allow space separated values to be grouped. e.g
city:"Mount barker" crop:Banana
would give me a filtered list of all farms in Mount Barker with the crop banana.
Furthermore any non tokenised queries would just look within a farms Details property to return the list of farms again with quotation marks combining multiple word queries.
---------------------------------------EDIT--------------------------------------------
My current search system using predicates is coded as follows. it's long (sorry) and is my first attempt though I'm hoping this could be refactored by some kind soul.
Many thanks in advance:
public ActionResult Search(string query, int? page)
{
IQueryable<Farm> farms = this.ReadOnlySession.All<Farm>();
if (!String.IsNullOrWhiteSpace(query))
{
// http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder
var predicate = PredicateBuilder.True<Farm>();
// We want to replace the spaces in quoted values here so we can split by space later.
// http://stackoverflow.com/questions/2148587/regex-quoted-string-with-escaped-quotes-in-c
Regex quoted = new Regex(#"""[^""\\]*(?:\\.[^""\\]*)*""");
foreach (var match in quoted.Matches(query))
{
query = query.Replace(match.ToString(), match.ToString().Replace(' ', '-'));
}
// Tidy up the query to remove "".
string[] splitQuery = HttpUtility.UrlDecode(query).Replace("\"", "").Split(' ');
Dictionary<string, string> tokenDictionary = new Dictionary<string, string>();
// Loop through our string[] and create a dictionary. Guids used to allow multiple keys
// of the same value.
Parallel.ForEach(splitQuery, subQuery =>
{
string[] tempArray = subQuery.Split(':');
if (tempArray.Length == 2)
{
tokenDictionary.Add(String.Format("{0}:{1}", tempArray[0], Guid.NewGuid()), tempArray[1]);
}
else
{
tokenDictionary.Add(String.Format("description:{0}", Guid.NewGuid()), subQuery);
}
});
// Loop through the dictionary and create our predicate.
foreach (KeyValuePair<string, string> item in tokenDictionary)
{
string value = item.Value.Replace('-', ' ');
string key = item.Key.Split(':')[0].ToUpperInvariant();
switch (key)
{
case "CROP":
value = Utilities.CreateSlug(value, OzFarmGuideConfig.RemoveDiacritics);
predicate = predicate.And(x => x.Crops.Any(y => value.Equals(y.Slug, StringComparison.OrdinalIgnoreCase)));
break;
case "STATE":
predicate = predicate.And(x => value.Equals(x.City.State.Name, StringComparison.OrdinalIgnoreCase));
break;
case "CITY":
value = Utilities.CreateSlug(value, OzFarmGuideConfig.RemoveDiacritics);
predicate = predicate.And(x => value.Equals(x.City.Slug, StringComparison.OrdinalIgnoreCase));
break;
default:
predicate = predicate.And(x => !String.IsNullOrWhiteSpace(x.Details) && x.Details.Contains(value));
break;
}
}
farms = farms.Where(predicate).OrderByDescending(x => x.Rating)
.ThenByDescending(x => x.RatingVotes);
PagedList<Farm> pagedFarms = new PagedList<Farm>(farms, page.HasValue ? page.Value - 1 : 0, 5);
return View(pagedFarms);
}
else
{
PagedList<Farm> pagedFarms = null;
return View(pagedFarms);
}
}
Just a guess, would the problem correct itself with the introduction of DefaultIfEmpty()?
default:
// This is not working at the mo. Getting a null exception when we try
// to initialise PagedList.
predicate = predicate.And(x => x.Details.DefaultIfEmpty().Contains(value));
break;

How to cache aggregate column values on Doctrine_Record instance?

Lets say i have a record class that often gets queried with dyanmic colums that are MySQL aggregate values:
$results = Doctrine_Core::getTable('MyRecord')->creatQuery('m')
->select('m.*, AVG(m.rating) as avg_rating, SUM(m.id) as nb_related')
->innerJoin('m.AnotherRecords a')
->where('m.id = ?')
->fetchOne();
Now lets say i want a method on that record to check if the aggregate columns exist from when the record was queried, and if not then i want to go ahead an issue a separate query to get these values:
// this doesnt actually work because of filterSet and filterGet
// but its general idea
public function getAverageRating($wtihNbRelated = false)
{
if(!isset($this->avg_rating) || ($withNbRelated && !isset($this->nb_related))
{
$rating = $this->getTable()->getAverageRating($this, $withNbRelated);
$this->avg_rating = $rating['avg_rating'];
if($withNbRealted)
{
$this->nb_related = $rating['nb_related'];
}
}
return $withNbRelated
? array('avg_rating' => $this->avg_rating, 'nb_related' => $this->nb_related)
: array('avg_rating' => $this->avg_rating);
}
Is there an easy way (ie. not writing a custom hydrator) to do this?
Simple answer really. I forgot that Doctrine prefixes all its direct protected members with _. So, even though i initially tried manipulating the data member i was forgot the prefix giving me the same result as if i tried $this->avg_rating or its accessor method. The solution was:
public function getAverageRating($wtihNbRelated = false)
{
if(!isset($this->_data['avg_rating']) || ($withNbRelated && !isset($this->_data['nb_related']))
{
$rating = $this->getTable()->getAverageRating($this, $withNbRelated);
$this->_data['avg_rating'] = $rating['avg_rating'];
if($withNbRealted)
{
$this->_data['nb_related'] = $rating['nb_related'];
}
}
return $withNbRelated
? array('avg_rating' => $this->_data['avg_rating'], 'nb_related' => $this->_data['nb_related'])
: array('avg_rating' => $this->_data['avg_rating']);
}

ASP.Net MVC : Get query values with no key

I have URL: http://site.com/page.aspx?update
how do I check if that update value is present?
HttpValueCollection treats that as an entity with null key. I have tried:
var noKeyValues = Request.QueryString.GetValues(null);
if (noKeyValues != null && noKeyValues.Any(v=>v==update)) ...
but it gives me a frowny line, because GetValues' argument is decorated with [NotNull].
so I end up doing:
var queryValuesWithNoKey =
Request.QueryString.AllKeys.Select((key, index) => new { key, value = Request.QueryString.GetValues(index) }).Where(
item => item.key == null).Select(item => item.value).SingleOrDefault();
if (queryValuesWithNoKey != null && queryValuesWithNoKey.Any(v => v.ToLower() == "update")) live = true;
not the most elegant workaround. Is there a better way to get key-less value from query string?
You can use
Request.QueryString[null]
to retrieve a comma separated list of keys with no values. For instance, if your url is:
http://mysite/?first&second
then the above will return
first,second
In your case, you could just do something like:
if(Request.QueryString[null] == "update")
{
// it's an update
}
if that's the only key you would use
Request.QueryString.ToString() to get the "update" value
I know I'm late to the party, but this a function that I use for this kind of task.
internal static bool HasQueryStringKey(HttpRequestBase request, string key)
{
// If there isn't a value, ASP will not recognize variable / key names.
string[] qsParts = request.QueryString.ToString().Split('&');
int qsLen = qsParts.Length;
for (int i = 0; i < qsLen; i++)
{
string[] bits = qsParts[i].Split('=');
if (bits[0].Equals(key, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
You may need to update it so that it is case sensitive, or uses different arguments depending on your purposes, but this has always worked well for me.

Resources