I'm using an :order query param to pass an order argument to my function. Unfortunately, it seems not to have an effect on the output.
The request debugging output shows the order argument is parsed correctly:
Parameter #2(cf_sql_varchar) = posts.createdAt ASC
Yet it still makes no difference to output. If I hard code the argument (ORDER BY ..., #arguments.order#), it works fine.
Any ideas?
public any function getPost(required numeric postId, string order)
{
switch(arguments.order)
{
case "new":
arguments.order = "posts.createdAt DESC";
break;
case "old":
arguments.order = "posts.createdAt ASC";
break;
default:
arguments.order = "posts.score DESC";
}
local.post = new Query(dataSource=variables.wheels.class.connection.datasource);
local.post.setSql("
SELECT *
FROM
WHERE posts.id = :postId OR posts.parentId = :postId
ORDER BY posts.postTypeId ASC, :order"
);
local.post.addParam(name="postId", cfsqltype="cf_sql_integer", value=arguments.postId, maxlength=10);
local.post.addParam(name="order", cfsqltype="cf_sql_varchar", value=arguments.order, maxlength=20);
local.post = local.post.execute().getResult();
return local.post;
}
If I remember correctly, queryparams won't work anywhere except in the where clause. So you're not dealing with a bug, but a limitation.
Related
I'm not sure why I'm having such a hard time finding an answer for this, but I have a list that I need to get the value from where the key matches certain criteria. The keys are all unique. In the example below, I want to get the color where the name equals "headache". Result should be "4294930176".
//Example list
String trendName = 'headache';
List trendsList = [{name: fatigue, color: 4284513675}, {name: headache, color: 4294930176}];
//What I'm trying
int trendIndex = trendsList.indexWhere((f) => f.name == trendName);
Color trendColor = Color(int.parse(trendsList[trendIndex].color));
print(trendColor);
Error I get: Class '_InternalLinkedHashMap' has no instance getter 'name'. Any suggestions?
EDIT:
Here's how I'm adding the data to the list, where userDocuments is taken from a Firestore collection:
for (int i = 0; i < userDocument.length; i++) {
var trendColorMap = {
'name': userDocument[i]['name'],
'color': userDocument[i]['color'].toString(),
};
trendsList.add(trendColorMap);
}
I guess, I got what the problem was. You were making a little mistake, and that was, you're trying to call the Map element as an object value.
A HashMap element cannot be called as f.name, it has to be called f['name']. So taking your code as a reference, do this, and you are good to go.
String trendName = 'headache';
List trendsList = [{'name': 'fatigue', 'color': 4284513675}, {'name': headache, 'color': 4294930176}];
//What I'm trying
// You call the name as f['name']
int trendIndex = trendsList.indexWhere((f) => f['name'] == trendName);
print(trendIndex) // Output you will get is 1
Color trendColor = Color(int.parse(trendsList[trendIndex]['color'])); //same with this ['color'] not x.color
print(trendColor);
Check that out, and let me know if that helps you, I am sure it will :)
I am trying to use the cursor to implement pagination but when I try to use the endCursor that is returned after my first query (queries 10 records), it gives me an error "invalid encoding". By the way I have a total of 16 records. I am expecting that on my next query, it will give me the last 6 records
Here's my code:
router.get("/scan/history/query", async (req: Request, resp: Response) => {
const userId = resp.locals.user && resp.locals.user.sub
const pageCursor = req.query.cursor
if (userId) {
let mainQuery = dataStoreClient.createQuery(process.env.GOOGLE_DATASTORE_KIND_SCAN_RESULTS)
.filter("userId", QUERY_FILTER_OPERATORS.EQUAL, userId)
.filter("isDeletedDocument", QUERY_FILTER_OPERATORS.EQUAL, false)
.select(["__key__", "scanDate", "scanKeyword", "scanFilter",
"hasRecord", "scanThreatStatus", "scanDuration",
"scanType", "scanStatus", "domainName"])
.order("scanDate", { descending: true })
.limit(10)
if (pageCursor) {
mainQuery = mainQuery.start(pageCursor)
}
const results = await mainQuery.run()
const entities = results[0]
const info = results[1]
const hasNextPage = info.moreResults !== "NO_MORE_RESULTS"
const pageResult = new PageResult(entities, info.endCursor, hasNextPage)
return HttpResult.Ok(resp, pageResult)
}
return HttpResult.UriNotFound(resp)
})
UPDATE:
I tried this with thousands of records and my limit is still 10. It works perfectly for like 2 or 3 queries but when I tried to query for the fourth time, it throws me an error "invalid encoding"
I know this is old, but in case anyone else comes across this issue (as I just did), I was able to resolve it by encoding the cursor value using encodeURIComponent(). It looks like the cursor value occasionally contains a + character, which causes issues when not escaped in the URL
I`ve encountered a problem:
Is it possible, to use regexp as a key in hashmap?
For example:
def unitsMap=[
(~/(?i).*ABC.*nM.*/):'AAA',
(~/(?i).*DEF.*nM.*/):'DDD'
]
println unitsH3HashMap['ABC (122344345P)']
Of course, that returns null value.
Best regards
No, but you can use it in a switch:
def unitsMap(key) {
switch(key) {
case ~/(?i).*ABC.*/: return 'AAA'
case ~/(?i).*DEF.*/: return 'DDD'
}
}
println unitsMap('ABC (122344345P)')
In order to get what you want you would have to write your own implementation of Map that uses pattern matching to perform getAt, putAt, contains, etc. However, it seems to me that the algorithmic complexity for lookups in such a scenario would always be O(n). Not very good compared to HashMap (O(0)) or TreeMap (O(log n)).
#tim_yates 's solution solves the raw outline of the problem as presented, but would not allow you to add new keys (cases) on-the-fly, as would your original desired code. If on-the-fly changes to your "map" are not important to you, then you should definitely use his solution (which I will endorse by giving it +1 now). If not, the you might be able to adapt his code to generate and invoke a script from a Map that will do what you desire.
This script:
// generate unitMapper
def generateUnitMapper(baseMap) {
def script = '''{ key ->
switch (key) {
'''
script += baseMap.collect { k, v ->
""" case ~/${k}/: return '${v}'
"""
}.join("")
script += ''' default: return null
}
}
'''
}
// notice this map is just using the regular expression STRINGS as keys,
// not the PATTERN objects from the original poster's code
def starterMap = [
/(?i).*ABC.*[\dA-Z]+.*/:'AAA',
/(?i).*DEF.*[\dA-Z]+.*/:'DDD'
]
def closureScript = generateUnitMapper(starterMap)
def unitsClosure = Eval.me(closureScript)
println closureScript
println unitsClosure('ABC (122344345P)')
println()
// regenerate map and closure and rerun
def changedMap = [ /ABC .*/:'000' ] + starterMap
closureScript = generateUnitMapper(changedMap)
unitsClosure = Eval.me(closureScript)
println closureScript
println unitsClosure('ABC (122344345P)')
Yields this output:
{ key ->
switch (key) {
case ~/(?i).*ABC.*[\dA-Z]+.*/: return 'AAA'
case ~/(?i).*DEF.*[\dA-Z]+.*/: return 'DDD'
default: return null
}
}
AAA
{ key ->
switch (key) {
case ~/ABC .*/: return '000'
case ~/(?i).*ABC.*[\dA-Z]+.*/: return 'AAA'
case ~/(?i).*DEF.*[\dA-Z]+.*/: return 'DDD'
default: return null
}
}
000
When I have an array of Sitecore IDs, for example TargetIDs from a MultilistField, how can I query the ContentSearchManager to return all the SearchResultItem objects?
I have tried the following which gives an "Only constant arguments is supported." error.
using (var s = Sitecore.ContentSearch.ContentSearchManager.GetIndex("sitecore_master_index").CreateSearchContext())
{
rpt.DataSource = s.GetQueryable<SearchResultItem>().Where(x => f.TargetIDs.Contains(x.ItemId));
rpt.DataBind();
}
I suppose I could build up the Linq query manually with multiple OR queries. Is there a way I can use Sitecore.ContentSearch.Utilities.LinqHelper to build the query for me?
Assuming I got this technique to work, is it worth using it for only, say, 10 items? I'm just starting my first Sitecore 7 project and I have it in mind that I want to use the index as much as possible.
Finally, does the Page Editor support editing fields somehow with a SearchResultItem as the source?
Update 1
I wrote this function which utilises the predicate builder as dunston suggests. I don't know yet if this is actually worth using (instead of Items).
public static List<T> GetSearchResultItemsByIDs<T>(ID[] ids, bool mustHaveUrl = true)
where T : Sitecore.ContentSearch.SearchTypes.SearchResultItem, new()
{
Assert.IsNotNull(ids, "ids");
if (!ids.Any())
{
return new List<T>();
}
using (var s = Sitecore.ContentSearch.ContentSearchManager.GetIndex("sitecore_master_index").CreateSearchContext())
{
var predicate = PredicateBuilder.True<T>();
predicate = ids.Aggregate(predicate, (current, id) => current.Or(p => p.ItemId == id));
var results = s.GetQueryable<T>().Where(predicate).ToDictionary(x => x.ItemId);
var query = from id in ids
let item = results.ContainsKey(id) ? results[id] : null
where item != null && (!mustHaveUrl || item.Url != null)
select item;
return query.ToList();
}
}
It forces the results to be in the same order as supplied in the IDs array, which in my case is important. (If anybody knows a better way of doing this, would love to know).
It also, by default, ensures that the Item has a URL.
My main code then becomes:
var f = (Sitecore.Data.Fields.MultilistField) rootItem.Fields["Main navigation links"];
rpt.DataSource = ContentSearchHelper.GetSearchResultItemsByIDs<SearchResultItem>(f.TargetIDs);
rpt.DataBind();
I'm still curious how the Page Editor copes with SearchResultItem or POCOs in general (my second question), am going to continue researching that now.
Thanks for reading,
Steve
You need to use the predicate builder to create multiple OR queries, or AND queries.
The code below should work.
using (var s = Sitecore.ContentSearch.ContentSearchManager.GetIndex("sitecore_master_index").CreateSearchContext())
{
var predicate = PredicateBuilder.True<SearchResultItem>();
foreach (var targetId in f.Targetids)
{
var tempTargetId = targetId;
predicate = predicate.Or(x => x.ItemId == tempTargetId)
}
rpt.DataSource = s.GetQueryable<SearchResultItem>().Where(predicate);
rpt.DataBind();
}
We have a named query like this:
UPDATE Foo f SET f.x = 0 WHERE f.x = :invoiceId
Foo in this case is an entity with a superclass, using the table-per-class inheritance strategy.
The SQL that EclipseLink generates is:
UPDATE foo_subclass SET x = ?
WHERE EXISTS(SELECT t0.id
FROM foo_superclass t0, foo_subclass t1
WHERE ((t1.x = ?) AND ((t1.id = t0.id) AND (t0.DTYPE = ?)))
(The ? slots are correctly filled in.)
On Informix 11.70, we get an error that the subquery cannot access the table being changed.
Here is the documentation that I was able to find on subquery restrictions on Informix: http://publib.boulder.ibm.com/infocenter/idshelp/v115/index.jsp?topic=%2Fcom.ibm.sqls.doc%2Fids_sqs_2005.htm
Other databases also feature restrictions on subqueries like this, so although this is manifesting as an Informix issue, I'm sure that if we ran this against, say, MySQL, we would get a similar error.
How can I get EclipseLink to honor these restrictions? Is there a better query I should be using?
Instead of:
UPDATE foo_subclass SET x = ?
WHERE EXISTS(SELECT t0.id
FROM foo_superclass t0, foo_subclass t1
WHERE ((t1.x = ?) AND ((t1.id = t0.id) AND (t0.DTYPE = ?)))
do this:
UPDATE
foo_subclass SET x = ?
WHERE
foo_subclass.x = ? AND
EXISTS(SELECT t0.id
FROM foo_superclass t0
WHERE ((foo_subclass.id = t0.id) AND (t0.DTYPE = ?))
Note that on 11.50 you cannot use alias for foo_subclass. It's allowed in 11.70.
I'm assuming "id" are primary keys (or at least unique identifiers).
Found the answer. Looks like EclipseLink had to handle this case for MySQL, which also has similar issues.
The answer is that in your InformixPlatform subclass, you need to override the following methods to solve this problem:
supportsLocalTemporaryTables(): this needs to return true
shouldAlwaysUseTempStorageForModifyAll(): this needs to return true
dontBindUpdateAllQueryUsingTempTables needs to return true
getCreateTempTableSqlPrefix(): this needs to return CREATE TEMP TABLE
getCreateTempTableSqlSuffix(): this needs to return WITH NO LOG
isInformixOuterJoin(): needs to return false
getTempTableForTable(DatabaseTable): this needs to do this:
return new DatabaseTable("TL_" + table.getName(), "" /* no table qualifier */, table.shouldUseDelimiters(), this.getStartDelimiter(), this.getEndDelimiter());
In addition, you need to override the following methods as well for proper InformixPlatform behavior:
appendBoolean(Boolean, Writer): the stock Informix platform does not write out boolean literals properly. Yours needs to do this:
if (Boolean.TRUE.equals(booleanValue)) {
writer.write("'t'");
} else {
writer.write("'f'");
}
You need to override writeUpdateOriginalFromTempTableSql so that it contains the same code as the H2Platform's override does:
#Override
public void writeUpdateOriginalFromTempTableSql(final Writer writer, final DatabaseTable table, final Collection pkFields, final Collection assignedFields) throws IOException {
writer.write("UPDATE ");
final String tableName = table.getQualifiedNameDelimited(this);
writer.write(tableName);
writer.write(" SET ");
final int size = assignedFields.size();
if (size > 1) {
writer.write("(");
}
writeFieldsList(writer, assignedFields, this);
if (size > 1) {
writer.write(")");
}
writer.write(" = (SELECT ");
writeFieldsList(writer, assignedFields, this);
writer.write(" FROM ");
final String tempTableName = this.getTempTableForTable(table).getQualifiedNameDelimited(this);
writer.write(tempTableName);
writeAutoJoinWhereClause(writer, null, tableName, pkFields, this);
writer.write(") WHERE EXISTS(SELECT ");
writer.write(((DatabaseField)pkFields.iterator().next()).getNameDelimited(this));
writer.write(" FROM ");
writer.write(tempTableName);
writeAutoJoinWhereClause(writer, null, tableName, pkFields, this);
writer.write(")");
}
Lastly, your constructor needs to call this.setShouldBindLiterals(false).
With these changes, it seems that Informix is happy.