Filtering unique records of a table widget datasource based on a single column in Google AppMaker - google-app-maker

I have a one-to-many relation, and I want to retrieve the "many" from a "one", however I am interested in only a single column of the "many" and although the records are unique the value of that column might not and I want to get the rows corresponding to the unique values of that column, possibly the first row in order of appearance (it doesn't really matter).
I added a table widget and added some code in the onDataLoad:
var items = widget.datasource.items;
var uniqueItems = [];
var keys = new Set();
for (var i = 0; i < items.length; i++)
{
if (!keys.has(items[i].Address))
{
uniqueItems.push(items[i]);
keys.add(items[i].Address);
}
}
widget.datasource.items = uniqueItems;
Here the column of interest is Address. The point was to go through the records and keep track of unique Address values, and whenever a new Address is encountered, keep it, and finally set the datasource to the unique list of rows that was just built.
It's not working. The keys are unique, but it is failing to set the datasource.
I am missing possibly more than one thing. Possible things that are happening:
This code shouldn't be in onDataLoad
This code actually sets the datasource but it is not reflected in the table
Wrong data type for uniqueItems
You can't just set a datasource
Bug I haven't seen...
How can I have my unique set appear in the table?

Related

Best way for displaying total Pages for a datasource in Appmaker

I have a Google drive table data source which stores list of open positions. Now in the data source I've set "Query per size" field to 10 so that I can get 10 records per page. I've added a Pager as well to show pagination.
My query is I want to display like "Page 1 of X" to my end users and this X will vary based on certain search filters. What will the best way to achieve this in Appmaker?
I've tried counting total records in a data source as per below code but every time updating that with the search criteria and recounting it is not a proper solution.
//Server side
var newQuery = app.models.Company.newQuery();
var records = newQuery.run();
var totalCount =0;
for(var i=0;i<records.length;i++)
{
totalCount=totalCount+1;
}
return totalCount;
In case you don't have any filters in your table your server code can be as simple as
// Server script
function getPagesCount(pageSize) {
var recordsCount = app.models.MyModel.newQuery().run().length;
var pagesCount = Math.ceil(recordsCount / pageSize);
return pagesCount;
}
As an alternative you can consider creating Calculated Model with a single field PagesCount.
In case you have some filters associated with the table then you'll need to run the query for the pages number with exact same filters.
Most likely the entire setup will not work effectively with Drive Tables since there is no way to query records number without querying records themselves. With Cloud SQL data backend one can create Calculated SQL Model with lightweight native SQL query (here :PageSize is query parameter which should be equal to the query.limit of the actual datasource):
SELECT
Ceil(COUNT(1) / :PageSize) AS RecordsNumber
FROM
TableName
WHERE
...
I've achieved this using Calculated Model as suggested by Pavel.
Steps :
Create a calculated data source with one field count.
In that data source add one parameter searchQuery. This will contain users filter going forward. Currently I have only one search query in which user can search many things. So I've added one parameter only.
In this data source add following server script.
Code:
// Server script
function getTotalRecords(query) {
var receivedQuery = query.parameters.searchQuery;
// console.log('Received query:' + query.parameters.searchQuery);
var records = app.models.Company.newQuery();
records.parameters.SearchText = query.parameters.searchQuery;
if(receivedQuery !== null) {
records.where = '(Name contains? :SearchText or InternalId contains? ' +
':SearchText or LocationList contains? :SearchText )';
}
var recordsCount = records.run().length;
var calculatedModelRecords = [];
var draftRecord = app.models.RecordCount.newRecord();
draftRecord.count = ''+recordsCount;
calculatedModelRecords.push(draftRecord);
return calculatedModelRecords;
}
.
On the Appmaker page bind a label with this data source.
On search query/your filter applied event add following code which Reload this data source and assign value to Parameter.
// Client script
function updateRecordCount(newValue) {
var ds = app.datasources.RecordCount;
ds.query.parameters.searchQuery = newValue;
ds.unload();
ds.load();
}

DynamoDb - .NET Object Persistence Model - LoadAsync does not apply ScanCondition

I am fairly new in this realm and any help is appreciated
I have a table in Dynamodb database named Tenant as below:
"TenantId" is the hash primary key and I have no other keys. And I have a field named "IsDeleted" which is boolean
Table Structure
I am trying to run a query to get the record with specified "TenantId" while it is not deleted ("IsDeleted == 0")
I can get a correct result by running the following code: (returns 0 item)
var filter = new QueryFilter("TenantId", QueryOperator.Equal, "2235ed82-41ec-42b2-bd1c-d94fba2cf9cc");
filter.AddCondition("IsDeleted", QueryOperator.Equal, 0);
var dbTenant = await
_genericRepository.FromQueryAsync(new QueryOperationConfig
{
Filter = filter
}).GetRemainingAsync();
But no luck when I try to get it with following code snippet (It returns the item which is also deleted) (returns 1 item)
var queryFilter = new List<ScanCondition>();
var scanCondition = new ScanCondition("IsDeleted", ScanOperator.Equal, new object[]{0});
queryFilter.Add(scanCondition);
var dbTenant2 = await
_genericRepository.LoadAsync("2235ed82-41ec-42b2-bd1c-d94fba2cf9cc", new DynamoDBOperationConfig
{
QueryFilter = queryFilter,
ConditionalOperator = ConditionalOperatorValues.And
});
Any Idea why ScanCondition has no effect?
Later I also tried this: (throw exception)
var dbTenant2 = await
_genericRepository.QueryAsync("2235ed82-41ec-42b2-bd1c-d94fba2cf9cc", new DynamoDBOperationConfig()
{
QueryFilter = new List<ScanCondition>()
{
new ScanCondition("IsDeleted", ScanOperator.Equal, 0)
}
}).GetRemainingAsync();
It throws with: "Message": "Must have one range key or a GSI index defined for the table Tenants"
Why does it complain about Range key or Index? I'm calling
public AsyncSearch<T> QueryAsync<T>(object hashKeyValue, DynamoDBOperationConfig operationConfig = null);
You simply cant query a table only giving a single primary key (only hash key). Because there is one and only one item for that primary key. The result of the Query would be that still that single item, which is actually Load operation not Query. You can only query if you have composite primary key in this case (Hash (TenantID) and Range Key) or GSI (which doesn't impose key uniqueness therefore accepts duplicate keys on index).
The second code attempts to filter the Load. DynamoDBOperationConfig's QueryFilter has a description ...
// Summary:
// Query filter for the Query operation operation. Evaluates the query results and
// returns only the matching values. If you specify more than one condition, then
// by default all of the conditions must evaluate to true. To match only some conditions,
// set ConditionalOperator to Or. Note: Conditions must be against non-key properties.
So works only with Query operations
Edit: So after reading your comments on this...
I dont think there conditional expressions are for read operations. AWS documents indicates they are for put or update operations. However, not being entirely sure on this since I never needed to do a conditional Load. There is no such thing like CheckIfExists functionality as well in general. You have to read the item and see if it exists. Conditional load will still consume read throughput so your only advantage would be only NOT retrieving it in other words saving the bandwith (which is very negligible for single item).
My suggestion is read it and filter it in your application layer. Dont query for it. However what you can also do is if you very need it you can use TenantId as hashkey and isDeleted for range key. If you do so, you always have to query when you wanna get a tenant. With the query you can set rangeKey(isDeleted) to 0 or 1. This isnt how I would do it. As I said, would just read it and filter it at my application.
Another suggestion thing could be setting a GSI on isDeleted field and writing null when it is 0. This way you can only see that attribute in your table when its only 1. GSI on such attribute is called sparse index. Later if you need to get all the tenants that are deleted (isDeleted=1) you can simply scan that entire index without conditions. When you are writing null when its 0 dynamoDB wont put it in the index at the first place.

How to bind Dropdown widget to query filter?

I have a Calculated Model, MonthlyTotalsByResource, displayed in a table that I am trying to query with a filter. First, I am retrieving the initial data from a regular Data Model called Allocations. I only wish to retrieve records from Allocations where the "Approved" field =true.
I also want to allow the user to filter MonthlyTotalsByResource by the "ManagerName" field. I have created a Dropdown widget with the Options as the full list of managers, and the Value is a query on the Calculated Model datasource:
#datasource.query.filters.ManagerName._equals
Here is the beginning of my code for getting the data for the Calculated Model MonthlyTotalsByResource from the regular data model Allocations, and where I filter for only "true" values in the Approved field. I am unclear what I should make the ManagerName filter set to in order for it to be binded to my Dropdown widget, or if I should add another query on the Calculated Model itself, instead of here on the regular Data Model.
function getMonthlyTotalsByResource_() {
var allRecordsQuery = app.models.Allocations.newQuery();
allRecordsQuery.filters.Approved._equals = true;
allRecordsQuery.filters.Resource.Manager.ManagerName._equals = ;
First things first, you need to introduce ManagerName parameter in your calculated datasource:
Once you add the parameter, you'll be able to set its value on client and read on server.
// dropdown widget's 'value' property binding
#datasources.MonthlyTotalsByResource.query.parameters.ManagerName
// server side code to get parameter value
var query = app.models.Allocations.newQuery();
...
query.filters.Resource.Manager.ManagerName._equals = query.parameters.ManagerName;
...

Having some trouble with getting an automatic import of a Sheet into a Data Table

Hoping someone here can help me, I have the below code:
function getSSData(){
var values = SpreadsheetApp.openById('1iKO7j_ETu_x1iJf7y_ih76sDTBS21JULid_5pNIit8w').getSheets()[0].getDataRange().getValues();
var ssData = [];
// app.datasources.P11d.unload(function(){});
console.log('Made it to Line 5');
for (var i = 0; i<values.length; i++){
var newRecord = app.models.P11d.newRecord();
// add all fields to the new record
console.log('Made it to Line 9');
newRecord.MODEL_FIELD = values[i][0];
ssData.push(newRecord);
// console.log(newRecord.MODEL_FIELD);
}
console.log('Finished');
// return the array of the model.newRecord objects that would be consumed by the Model query.
return ssData;
}
I have taken this from another post on here, however I can't seem to understand what is happening around the MODEL_FIELD section. Do I need to specify each column title individually or will this just know what to do?
Thank you in advance and I'm sorry if the question seems simple, I'm still very new at this and trying to pick it up as I go along.
values is a 2d array of all of the data in your sheet.
Effectively, the code iterates over all of the rows retrieved from the sheet. For each row a new record is created and the value in the first column of each row is assigned to the field MODEL_FIELD in the new record.
Each new record is pushed into another array which is returned to the caller to be saved with app.saveRecords();

Filtering on table on aspx page

I need to implement Excel kind of filtering on a table on an aspx page.
What I want is, when I click on any column header it should show me the list of rows in that column with check-box for each one and when I select specific rows then the table should filter accordingly.
Here is a working example for one column at a time. I'm working on getting this working for several columns as well: http://jsfiddle.net/mwB37/20/
Since you're asking for a complete plugin with a UI and everything, including relevant code on SO is kinda difficult. But here are the key excerpts that defines my thinking and the concept:
// fetching the column index
var columnIndex = th.index();
// then fetching the corresponding TDs (use for-loop when thousands of elements)
var tds = th.closest('table')
.find('tbody td:nth-child(' + (columnIndex+1) + ')');
// distinctively selecting the unique text values in those columns
var checks = $.unique(tds.map(function() {
return $(this).text();
}).get());
// the most basic way possible of filtering
// (should be extended into an OR query of all current column filters)
if (index != -1 || arr.length == 0)
td.parent('tr').show();
else
td.parent('tr').hide();
If you want something already finished instead, this is such a plugin: PicNet Table Filter
There is also a filtering plugin available for the jQuery DataTables plugin, although sorting is obviously the main focus of that one.

Resources