Dynamodb pagination of 10 results - amazon-web-services

I have a message table which I would like to get the last 10 messages for that user and if they click more it would retrieve another 10 until there were no more message left. I could not see how to do this, so far I am able to get message based on time, but this is not exactly what I want. Reading through the documentation I can see it a method called LastEvaluatedKey, but where and how do I use this I can't find a working example of this. This is my code for time:
long startDateMilli = (new Date()).getTime() - (15L*24L*60L*60L*1000L);
long endDateMilli = (new Date()).getTime() - (5L*24L*60L*60L*1000L);
java.text.SimpleDateFormat df = new java.text.SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
String startDate = df.format(startDateMilli);
String endDate = df.format(endDateMilli);
QuerySpec spec = new QuerySpec()
.withProjectionExpression("to,fr,sta,cr")
.withKeyConditionExpression("to = :v_to and cr between :v_start_dt and :v_end_dt")
.withValueMap(new ValueMap()
.withString(":v_id", username)
.withString(":v_start_dt", startDate)
.withString(":v_end_dt", endDate));
ItemCollection<QueryOutcome> items = table.query(spec);
System.out.println("\nfindRepliesPostedWithinTimePeriod results:");
Iterator<Item> iterator = items.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().toJSONPretty());
}
How can I modify this to eliminate the time based pagination and use instead a last 10 messages type of pagination?

Related

How to get the correct count for a Lucid Model's Paginate when joining with additional tables

I have 2 Lucid models: Ad and Campaign, which are associated using a Many:Many relationship. They have a pivot table which manages the relationship which has additional information, so my table structure is as follows:
ads
id
...
campaign_ads
campaign_id
ad_id
spend
sent
clicks
leads
ftds
campaigns
id
...
I am trying to fetch the results of a paginate query using the Ad models' query function, but in addition to the Ad models' fields, I would also like to fetch the sum of spend, sent, clicks, leads and ftds from the related Campaign models' pivots.
I have come up with the following code, which returns the correct information in the collection, but returns an incorrect value for the count
const Ad = use('App/Models/Ad');
const query = Ad.query()
.leftJoin('campaign_ads', 'ads.id', 'campaign_ads.ad_id')
.select('ads.*')
.sum('campaign_ads.spend as spend')
.sum('campaign_ads.sent as sent')
.sum('campaign_ads.clicks as clicks')
.sum('campaign_ads.leads as leads')
.sum('campaign_ads.ftds as ftds')
.groupBy('ads.id')
.paginate()
I assume that this is related to how the paginate function rewrites or performs the query, but I have no idea how to fix it.
Here is some example usage based on the answer:
const Ad = use('App/Models/Ad');
const query = Ad.query()
.leftJoin('campaign_ads', 'ads.id', 'campaign_ads.ad_id')
.select('ads.*')
.sum('campaign_ads.spend as spend')
.sum('campaign_ads.sent as sent')
.sum('campaign_ads.clicks as clicks')
.sum('campaign_ads.leads as leads')
.sum('campaign_ads.ftds as ftds')
.groupBy('ads.id')
const paginate = async (query, page = 1, perPage = 20) {
// Types of statements which are going to filter from the count query
const excludeAttrFromCount = ['order', 'columns', 'limit', 'offset', 'group']
// Clone the original query which we are paginating
const countByQuery = query.clone();
// Case Page and Per Page as Numbers
page = Number(page)
perPage = Number(perPage)
// Filter the statments from the array above so we have a query which can run cleanly for counting
countByQuery.query._statements = _.filter(countByQuery.query._statements, (statement) => {
return excludeAttrFromCount.indexOf(statement.grouping) < 0
})
// Since in my case, i'm working with a left join, i'm going to ensure that i'm only counting the unique models
countByQuery.countDistinct([Ad.table, 'id'].join('.'));
const counts = await countByQuery.first()
const total = parseInt(counts.count);
let data;
// If we get a count of 0, there's no point in delaying processing for an additional DB query
if (0 === total) {
data = [];
}
// Use the query's native `fetch` method, which already creates instances of the models and eager loads any relevant data
else {
const {rows} = await query.forPage(page, perPage).fetch();
data = rows;
}
// Create the results object that you would normally get
const result = {
total: total,
perPage: perPage,
page: page,
lastPage: Math.ceil(total / perPage),
data: data
}
// Create the meta data which we will pass to the pagination hook + serializer
const pages = _.omit(result, ['data'])
if (Ad.$hooks) {
await Ad.$hooks.after.exec('paginate', data, pages)
}
// Create and return the serialized versions
const Serializer = Ad.resolveSerializer()
return new Serializer(data, pages);
}
paginate(query, 1, 20)
.then(results => {
// do whatever you want to do with the results here
})
.catch(error => {
// do something with the error here
})
So, as I noted before in my notes, the problem that I was have was caused by how Lucid's query builder handles the paginate function, so I was forced to "roll my own". Here's what I came up with:
paginate (query, page = 1, perPage = 20) {
// Types of statements which are going to filter from the count query
const excludeAttrFromCount = ['order', 'columns', 'limit', 'offset', 'group']
// Clone the original query which we are paginating
const countByQuery = query.clone();
// Case Page and Per Page as Numbers
page = Number(page)
perPage = Number(perPage)
// Filter the statments from the array above so we have a query which can run cleanly for counting
countByQuery.query._statements = _.filter(countByQuery.query._statements, (statement) => {
return excludeAttrFromCount.indexOf(statement.grouping) < 0
})
// Since in my case, i'm working with a left join, i'm going to ensure that i'm only counting the unique models
countByQuery.countDistinct([this.#model.table, 'id'].join('.'));
const counts = await countByQuery.first()
const total = parseInt(counts.count);
let data;
// If we get a count of 0, there's no point in delaying processing for an additional DB query
if (0 === total) {
data = [];
}
// Use the query's native `fetch` method, which already creates instances of the models and eager loads any relevant data
else {
const {rows} = await query.forPage(page, perPage).fetch();
data = rows;
}
// Create the results object that you would normally get
const result = {
total: total,
perPage: perPage,
page: page,
lastPage: Math.ceil(total / perPage),
data: data
}
// Create the meta data which we will pass to the pagination hook + serializer
const pages = _.omit(result, ['data'])
// this.#model references the Model (not the instance). I reference it like this because this function is part of a larger class
if (this.#model.$hooks) {
await this.#model.$hooks.after.exec('paginate', data, pages)
}
// Create and return the serialized versions
const Serializer = this.#model.resolveSerializer()
return new Serializer(data, pages);
}
I only use this version of pagination when I detect group by in my query, and it follow's Lucid's own paginate function pretty closely, and returns identical feedback. While it's not a 100% drop-in solution, it's good enough for my needs

How to get the results of a stored procedure when using cfscript new StoredProc()

First time trying to use a stored procedure via cfscript and I can't figure out how to get the results. With a regular query I do something like this to get my result set:
queryResult = queryResult.execute().getResult();
With the stored procedure my code is:
queryResult = new storedProc( procedure = 'stpQueryMyResultSet', datasource = 'mydsn' );
queryResult = queryResult.execute();
writeDump(queryResult);
That returns 3 structs - prefix, procResultSets and procOutVariables, but I can't seem to figure out how to get my query results.
Thanks to #Ageax for pointing me to that page. Here's how I got it working (I also added in a param for max rows to return):
queryResult = new storedProc( procedure = 'stpQueryMyResultSet', datasource = 'mydsn' );
queryResult.addParam( type = 'in', cfsqltype = 'cf_sql_integer', value = '10');
queryResult.addProcResult( name = 'result' );
qResult = queryResult.execute().getProcResultSets().result;
writeDump(qResult);

Corda - problem using VaultCustomQueryCriteria

I'm trying to use a VaultCustomQueryCriteria (Corda - Java) with the aggregate function SUM, but I get no results.
If I use another VaultCustomQueryCriteria, the query works.
What am I doing wrong?
Below some examples:
Query OK:
QueryCriteria statusCriteria = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED);
Field name = ExampleSchemaV1.Ingestion.class.getDeclaredField("name");
QueryCriteria countCriteria = new QueryCriteria.VaultCustomQueryCriteria(Builder.equal(name, "Mark"));
List<StateAndRef<IngestionState>> results = rpcOps.vaultQueryByCriteria(countCriteria,IngestionState.class).getStates();
Query KO: (no results)
QueryCriteria statusCriteria = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED);
Field nr = ExampleSchemaV1.Ingestion.class.getDeclaredField("nr");
Field name = ExampleSchemaV1.Ingestion.class.getDeclaredField("name");
CriteriaExpression sumQta = Builder.sum(nr, Arrays.asList(name));
QueryCriteria sumQtaCriteria = new QueryCriteria.VaultCustomQueryCriteria(sumQta);
QueryCriteria criteria = statusCriteria.and(sumQtaCriteria);
List<StateAndRef<IngestionState>> results = rpcOps.vaultQueryByCriteria(criteria,IngestionState.class).getStates();
Each vault query returns a Vault.Page object. When performing a sum query, the result of the sum is accessible via Vault.Page.getOtherResults(), rather than via Vault.Page.getStates().
This is because the sum query doesn't return any actual states, but rather the result of a computation over these states.

Acumatica GI - Inventory Transaction History Screen

I want to create a GI for Inventory Transaction History Screen (IN405000).
Since the table InventoryTranHistEnqResult isn't present in the database...few of the columns of this screen are taken from INTran Table.
I am not able to find following columns: BegQty, QtyIn, QtyOut, EndQty...
I also tried the following query on database to find these columns
SELECT c.name AS ColName, t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE '%EndQty%'
The DAC for these fields is:
enter image description here
Looking at the information behind the page in the page Graph will give you the answers to your question. Inventory Transaction History Screen (IN405000) uses graph InventoryTranHistEnq. The grid in this page uses DAC InventoryTranHistEnqResult in the following view:
PXSelectJoin<InventoryTranHistEnqResult,
CrossJoin<INTran>,
Where<True, Equal<True>>,
OrderBy<Asc<InventoryTranHistEnqResult.gridLineNbr>>> ResultRecords
The ResultsRecords are built dynamically in the inquiry using the following:
protected virtual IEnumerable resultRecords()
{
int startRow = PXView.StartRow;
int totalRows = 0;
decimal? beginQty = null;
List<object> list = InternalResultRecords.View.Select(PXView.Currents, PXView.Parameters, new object[PXView.SortColumns.Length], PXView.SortColumns, PXView.Descendings, PXView.Filters, ref startRow, PXView.MaximumRows, ref totalRows);
PXView.StartRow = 0;
foreach (PXResult<InventoryTranHistEnqResult> item in list)
{
InventoryTranHistEnqResult it = (InventoryTranHistEnqResult)item;
it.BegQty = beginQty = (beginQty ?? it.BegQty);
decimal? QtyIn = it.QtyIn;
decimal? QtyOut = it.QtyOut;
beginQty += (QtyIn ?? 0m) - (QtyOut ?? 0m);
it.EndQty = beginQty;
}
return list;
}
So i guess the short answer is you cannot use the results of this page for a GI as it is built in the page only. You might want to look into adding what you need to this history page via a customization or make your own version of this page/graph/dac if the information you need is that important.

What is the preferred method of updating Money fields in Dynamics CRM 2013 via Web Services?

I have an entity in CRM that has some Money fields on them. At the time of the creation of the entity there is no value for those fields, so the entity is created and saved. However if I then have values and go to update them (either through the UI or Web Services) I basically get an error stating "The currency cannot be null".
In the UI:
I then get a red circle with an X through it if I go to update the value.
In the code (web service call):
var crmService = new CrmServiceReference.IFAAContext(new Uri(crmWebServicesUrl));
crmService.Credentials = System.Net.CredentialCache.DefaultCredentials;
var crmAccount = crmService.AccountSet.Where(a => a.AccountId == accountId).FirstOrDefault();
crmAccount.myitems_MoneyValueItem.Value = 10.0m;
crmService.UpdateObject(crmAccount);
crmService.SaveChanges()
I then get the "The currency cannot be null" on the .SaveChanges() call.
In the code (second attempt):
var crmService = new CrmServiceReference.IFAAContext(new Uri(crmWebServicesUrl));
crmService.Credentials = System.Net.CredentialCache.DefaultCredentials;
var crmAccount = crmService.AccountSet.Where(a => a.AccountId == accountId).FirstOrDefault();
var crmCurrency = crmService.TransactionCurrencySet.Where(c => c.ISOCurrencyCode == "AUD").First();
crmService.SetLink(crmAccount, "TransactionCurrency_Account", crmCurrency);
//crmService.SaveChanges() /* tried with this uncommented as well to try and "set currency" before setting value */
crmAccount.myitems_MoneyValueItem.Value = 10.0m;
crmService.UpdateObject(crmAccount);
crmService.SaveChanges()
So this attempt fails in the same way, however if I run it a second time (without the currency and SetLink) so that the currency was saved before then it does work - hence the atempt to do a second .SaveChanges() but really need it to sort of work the first time through.
In the code (third attempt):
var crmService = new CrmServiceReference.IFAAContext(new Uri(crmWebServicesUrl));
crmService.Credentials = System.Net.CredentialCache.DefaultCredentials;
var crmAccount = crmService.AccountSet.Where(a => a.AccountId == accountId).FirstOrDefault();
crmAccount.athos_MoneyValueItem= new CrmServiceReference.Money() { Value = 10.0m };
crmService.UpdateObject(crmAccount);
crmService.SaveChanges()
This doesn't appear to work either
When you create a new record containing a Money field (or updating an existing one where no Money fields are filled before) , you need to specify the currency, the right field to set is TransactionCurrencyId (logical name transactioncurrencyid), it's a lookup (so inside the code is an EntityReference) to the currency entity.
assuming you have the Guid of your currency stored inside the currencyId variable:
var crmAccount = crmService.AccountSet.Where(a => a.AccountId == accountId).FirstOrDefault();
crmAccount.TransactionCurrencyId = new EntityReference(TransactionCurrency.LogicalName, currencyId);
crmAccount.myitems_MoneyValueItem = new Money(10.0m); //better to update the money field with this syntax
crmService.UpdateObject(crmAccount);
crmService.SaveChanges()
CRM 2013 expects a Money object for a currency field.
crmAccount.myitems_MoneyValueItem = new Money(10.0m);
You shouldn't have to explicitly declare a currency unless your CRM organization doesn't have a default currency set (and since you need to set this when creating an organization, it should always be set).
It looks like if I do a lookup on currency (shame as an extra data call) and then use that to set the TransactionCurrencyId it then "works" ... seems like I shouldn't have to do this as I do have a default set for the organisation though. But this does appear to be working in that it results in being able to update those fields.
var crmCurrency = crmService.TransactionCurrencySet.Where(c => c.ISOCurrencyCode == currencyCode).First();
var crmService = new CrmServiceReference.IFAAContext(new Uri(crmWebServicesUrl));
crmService.Credentials = System.Net.CredentialCache.DefaultCredentials;
var crmAccount = crmService.AccountSet.Where(a => a.AccountId == accountId).FirstOrDefault();
crmAccount.TransactionCurrencyId = new CrmServiceReference.EntityReference() { Id = crmCurrency.TransactionCurrencyId, LogicalName = "transactioncurrency", Name = crmCurrency.CurrencyName };
crmAccount.myitems_MoneyValueItem.Value = 10.0m;
crmService.UpdateObject(crmAccount);
crmService.SaveChanges();
i am doing the same thing as you but unable to set the money field value during service.create.
I have already put in
entity.Attributes["transactioncurrencyid"] = currencyGuid;
entity.Attributes["new_moneyfield"] = new Money(123.45M);
but in the end, the record is created with the transactioncurrency field populated, while the money field is blank.
Its a custom workflow on CRM online 2016 by the way..