breezeJS custom predicates - breeze

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.

Related

GEE joined collection is not working in a function

I am working with two collections of satellite data. I want to select specific bands from "collection 1", join them to "collection 2", and then run a function. Unfortunately, the function does not work with the joined data, although it works for "collection 1".
Here is an example just using B10 of Sentinel-2
//identifying area and date
var geometry = ee.Geometry.Point([4,45]);
Map.centerObject(geometry,10);
var start = '2019-03-10';
var end = '2019-05-10';
//my function
function testing(img){
img = img.updateMask(img.select(['B10']).gt(200).focal_min(2).focal_max(2).not());
return img;
}
//my two collections
var collection1 = ee.ImageCollection('COPERNICUS/S2').filterDate(start,end)
.filterBounds(geometry);
var B10s=collection1.select('B10');
//print('B10s',B10s);
var collection2 = ee.ImageCollection('COPERNICUS/S2_SR')
.filterDate(start,end)
.filterBounds(geometry);
// joining the collections
var filtering = ee.Filter.equals({
leftField: 'system:time_start',
rightField: 'system:time_start'
});
var simpleJoin = ee.Join.inner();
var innerJoin = simpleJoin.apply(collection2, B10s, filtering);
var joined = innerJoin.map(function(feature) {
return ee.Image.cat(feature.get('primary'), feature.get('secondary'));
});
print('Joined', joined);
//just to visualize one image
//var coll1 = ee.Image(collection1.first());
//Map.addLayer(coll1, {bands:['B2'], min:0, max:5000},'B2Coll1 test');
//running the function for collection 1 works
var test = collection1.map(testing);
var tess = ee.Image(test.first());
Map.addLayer(tess, {bands:['B2'], min:0, max:5000},'B2 test');
//here when running with the joined collection, there is a problem
var TestingJoined = joined.map(testing);
The error is: img.select(...).gt is not a function
How do I make this work?
When you debug, does the join work as intended? Is there any issue with the datetimes being too-precise to allow a full join?
The second route I would go down is ensuring that the joined object is identical to collections. I doubt this would be the case without you casting it or something (though I'm not familiar with this library). Your "testing" function that you're mapping to these collections may work with just the unjoined ones. If you provide the actual 'Problem' or error output that would be immensely helpful.
Ok. I solved it. Thank you for your input.
I needed to use a cast in the function. Now it works.
function testing(img){
img = ee.Image(img).updateMask(ee.Image(img).select(['B10']).gt(200).focal_min(2).focal_max(2).not());
return img;
}

Is it possible for a script to reference a line on a new record?

This relates to some older questions on SuiteScript 1.0. I've got a 2.0 script that has to be 2.0, so I can't use the old nlapiGetLineItemValue - it has to be Record.getSublistValue(options). But I need it to get the line value on a new, unsaved record BeforeSubmit.
It keeps returning "getSublistValue" is not defined in object, and checking NetSuite Field Explorer confirms that the unsaved record has no defined lines.
The same applies to AfterSubmit.
So is there any work around, or is it even possible, to reference the line item value when the record is being created?
ADDING SOME CODE, WHERE THE QUESTION APPLIES:
var recNew = context.newRecord
var ItemID = recNew.getSublistValue({
sublistId: 'items',
fieldId: 'itemid',
});
var listIDs = ["6646", "17745", "17945", "21349"];
var a_filters = [];
a_filters.push(new nlobjSearchFilter(ItemID, null, 'anyof', listIDs));
{
// an action
}
getSublistValue requires you to pass the line number.
var recNew = context.newRecord
for (var x =0; x< recNew.getLineCount({sublistId:'item'}); x++) {
var ItemID = recNew.getSublistValue({ sublistId: 'item', fieldId: 'item', line: x });
// DO STUFF
}
try using
if(scriptContext.type == 'edit'){
var recNew = scriptContext.newRecord
recNew.getSublistValue(options)
}

breeze query where clause is not executed

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?

dapper querymultiple spliton error

I'm using: ASP.NET MVC, MySql, Dapper.NET micro-orm
I made a stored procedure with 3 SELECTs, two of which returns lists and the third one returns an integer.
Here is my code:
using (var conn = new MySqlConnection(GetConnectionString()))
{
var readDb = conn.QueryMultiple(storedProcedure, parameters, commandType: CommandType.StoredProcedure);
var result = new someView
{
TopicsList = readDb.Read<ITopic>().ToList(),
TopTopicsList = readDb.Read<IMessage>().ToList(),
TopicsCount = readDb.Read<int>().Single()
};
return result;
}
In ITopic I have TopicId, in IMessage I have MessageId.
And here's the error:
When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id Parameter name: splitOn
I tried adding splitOn on both QueryMultiple and Read, and nigher accepted it.
Though I dont understand why I need splitOn? can't dapper see that I have three separate SELECTs? When using conn.Read(storedProcedure,parameters) on each of the selects separately (instead of MultipleQuery on all of the together) dapper has no problem mapping it to a given object.
What am I doing wrong?
1) Problem solved when I used the real models names instead of their interfaces names:
TopicView instead of ITopic, TopTopicsView instead of IMessage;
2) Once that was fixed and there was no longer "no splitOn" error, started another problem with the < int > casting in line:
TopicsCount = readDb.Read<int>().Single()
probably mysql doesnt return numbers back as ints?
I tried using decimal, object, dynamic, etc.. with no luck. Eventually fixed it by creating another Model with int property inside that has the same name as the database int parameter and now it works.
3) Here's the final working code:
using (var conn = new MySqlConnection(GetConnectionString()))
{
var parameters = context.MapEntity(query);
var multi = conn.QueryMultiple(storedProcedure, parameters, commandType: System.Data.CommandType.StoredProcedure);
var TopicsList = multi.Read<TopicView>().ToList();
var TopTopicsList = multi.Read<TopTopicsView>().ToList();
var result = multi.Read<HomeView>().Single();
result.TopicsList = TopicsList;
result.TopTopicsList = TopTopicsList;
return result;
}

Client Side Query to filter by extended properties

I have a need where i want to get the count of deleted items and get change set/collection for items that are not deleted.
How can i achieve that ?
i am able to do it using the LINQJS, but was wondering if there is more proper way of doing it.
var countryType = manager.metadataStore.getEntityType("Country");
var countries = manager.getEntities(countryType);
var deletedCount = Enumerable.From(countries)
.Where(function (x) {
return x.entityAspect.entityState == "Deleted"
}).Count();
self.totalRows(self.totalServerRows() - deletedCount);
var queryResult = Enumerable.From(countries)
.OrderBy(function(x){ x.Country_Code })
.Where(function (x) {
return x.entityAspect.entityState != "Deleted"
})
.Skip(self.pageIndex() * self.pageSize())
.Take(self.pageSize())
.ToArray();
self.list(queryResult);
The EntityManager.getEntities call is overloaded (see: http://www.breezejs.com/sites/all/apidocs/classes/EntityManager.html#method_getEntities ) so that you can simply ask for those entities within the entityManager that are of specific types or in specified states. So for 'deleted, try this:
var deletedEntities = em.getEntities(null, [EntityState.Deleted]);
or if you wanted just the Added or Modified entities
var deletedEntities = em.getEntities(null, [EntityState.Added, EntityState.Modified]);
I hope this helps.

Resources