Spellcheck not working in search in AEM 6.1 - lucene

Spell check is not working in AEM 6.1 even after creating the suggested indexes as specified in --
https://docs.adobe.com/docs/en/aem/6-1/deploy/platform/queries-and-indexing.html and
https://jackrabbit.apache.org/oak/docs/query/lucene.html
As per the docs spellcheck has been implemented in OAK since versions 1.1.17 and 1.0.13. My OAK version is 1.22 so it should work.The AEM version is 6.1.0.20150507
Created an index like -
/oak:index/lucene-spellcheck
- jcr:primaryType = "oak:QueryIndexDefinition"
- compatVersion = 2
- type = "lucene"
- async = "async"
+ indexRules
- jcr:primaryType = "nt:unstructured"
+ nt:base
+ properties
- jcr:primaryType = "nt:unstructured"
+ jcr:title
- propertyIndex = true
- analyzed = true
- useInSpellcheck = true
I get zero results when I run this query using CRX-DE's query tool as well as using query manager from a jsp.
SELECT [rep:spellcheck()] FROM nt:base WHERE [jcr:path] = '/content/abc' AND SPELLCHECK('tetspage')
('testpage' is a page; misspelled it as 'tetspage')
Running this query as mentioned in the docs
SELECT [rep:spellcheck()] FROM nt:base WHERE [jcr:path] = '/' AND SPELLCHECK('jackrabit')
returns a single node.
Am I doing anything wrong; is this index working in anyone's AEM 6.1 ?

This query works for me
SELECT [rep:spellcheck()] FROM [nt:base] WHERE SPELLCHECK('tetspage') AND ISDESCENDANTNODE('/content/abc')

I had a similar problem with a "rep:suggest()" query, instead of a "rep:spellcheck()", but maybe the solution works for you.
When I tried the query in the CRX DE directly, the result was a single node, and I couldn't see anything on that node.
However, executing the query from my code I found out that the results of the query were stored as "rows". To process that "rows" I used this code, hope it helps you:
String sql = "SELECT [rep:suggest()] FROM cq:PageContent WHERE ISDESCENDANTNODE('" + path + "/') AND SUGGEST('" + text + "')";
Query q = qm.createQuery(sql, Query.SQL);
List<String> results = Lists.newArrayList(); // <--- Actual query results
RowIterator it = q.execute().getRows();
while (it.hasNext()) {
Row row = it.nextRow();
results.add(row.getValue("rep:suggest()").getString());
}

Related

How can use executeQueryWithParameters with SQLBuilderSelectExpression to join an x++/sql statement in Microsoft Dynamics?

In Dynamics 365 for Finance and Operations, they describe a method of creating SQL statements "as objects, as opposed to text", but this is somewhat of a lie. They use the objects to create the text which then populates str sqlStatement = selectExpr.getExpression(null);
This sqlStatement would then feed the obsolete statement.executeQuery(sqlStatement);.
I can make the warning go away by using executeQueryWithParameters() with an empty map (SqlParams::create()) as the second parameter, but this seems to be "cheating".
Is there a way I can/should refactor the following to populate the map correctly?
SQLBuilderSelectExpression selectExpression = SQLBuilderSelectExpression::construct();
selectExpression.parmUseJoin(true);
SQLBuilderTableEntry vendTable = selectExpression.addTableId(tableNum(VendTable));
SQLBuilderTableEntry dirPartyTable = vendTable.addJoinTableId(tableNum(DirPartyTable));
SQLBuilderFieldEntry accountNum = vendTable.addFieldId(fieldNum(VendTable, AccountNum));
SQLBuilderFieldEntry name = dirPartyTable.addFieldId(fieldNum(DirPartyTable, Name));
SQLBuilderFieldEntry dataAreaId = vendTable.addFieldId(fieldNum(VendTable, dataAreaId));
SQLBuilderFieldEntry blocked = vendTable.addFieldId(fieldNum(VendTable, Blocked));
vendTable.addRange(dataAreaId, curext());
vendTable.addRange(blocked, CustVendorBlocked::No);
selectExpression.addSelectFieldEntry(SQLBuilderSelectFieldEntry::newExpression(accountNum, 'AccountNum'));
selectExpression.addSelectFieldEntry(SQLBuilderSelectFieldEntry::newExpression(name, 'Name'));
str sqlStatement = selectExpression.getExpression(null);
// FIXME:
ResultSet resultSet = statement.executeQueryWithParameters(sqlStatement, SqlParams::create());
Below is how you would write your code as a standard X++ query. However, I must note that what you're doing may not be the best approach.
DirPartyTable is a special table in AX as it supports inheritance, so you should make sure you fully understand the framework. See:
https://learn.microsoft.com/en-us/dynamicsax-2012/appuser-itpro/implementing-the-global-address-book-framework-white-paper
https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/fin-ops/organization-administration/overview-global-address-book
Code:
VendTable vendTable;
DirPartyTable dirPartyTable;
while select AccountNum from vendTable
where vendTable.Blocked == CustVendorBlocked::No
// DataAreaId along with Partition, are automatically included in the query context depending
// on the company context you're executing the code from
// && vendTable.dataAreaId == curext()
join Name from dirPartyTable
where dirPartyTable.RecId == vendTable.Party
{
info(strFmt("Account: %1; Name: %2", vendTable.AccountNum, dirPartyTable.Name));
}
Regarding an AOT query, look in the AOT at \Queries\VendTableListPage and expand the data sources and learn from it.
Regardless of what OP is trying to do with the query, the answer to the question of "how do I correctly replace executeQuery with executeQueryWithParameters" can be found in the following article.
https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/dev-ref/query-with-parameters
The new *WithParameters APIs were introduced as a way to mitigate sql injection attacks which may occur when building up sql strings manually with un-sanitized sql parameters as input.
Snippet of the code example from above doc shows how to correctly populate the map to match the sql statement:
str sql = #"
UPDATE Wages
SET Wages.Wage = Wages.Wage * #percent
WHERE Wages.Level = #Level";
Map paramMap = SqlParams::create();
paramMap.add('percent', 1.1); // 10 percent increase
paramMap.add('Level', 'Manager'); // Management increase
int cnt = statement.executeUpdateWithParameters(sql, paramMap);

Pyodbc and Access with query parameter that contains a period

I recently found a bug with some Access SQL queries that I can't seem to track down. I have a fairly straightforward SQL query that I use to retrieve data from an access database that's "managed" in an older application (ie the data is already in the database and I have no real control over what's in there).
import pyodbc
MDB = '******.MDB'
DRV = '{Microsoft Access Driver (*.mdb)}'
PWD = ''
con = pyodbc.connect('DRIVER={};DBQ={};PWD={}'.format(DRV, MDB, PWD))
sql = ('SELECT Estim.PartNo, Estim.Descrip, Estim.CustCode, Estim.User_Text1, Estim.Revision, ' +
'Estim.Comments, Routing.PartNo AS RPartNo, Routing.StepNo, Routing.WorkCntr, Routing.VendCode, ' +
'Routing.Descrip AS StepDescrip, Routing.SetupTime, Routing.CycleTime, ' +
'Routing.WorkOrVend, ' +
'Materials.PartNo as MatPartNo, Materials.SubPartNo, Materials.Qty, ' +
'Materials.Unit, Materials.TotalQty, Materials.ItemNo, Materials.Vendor ' +
'FROM (( Estim ' +
'INNER JOIN Routing ON Estim.PartNo = Routing.PartNo ) ' +
'INNER JOIN Materials ON Estim.PartNo = Materials.PartNo )')
if 'PartNo' in kwargs:
key = kwargs['PartNo']
sql = sql + 'WHERE Estim.PartNo=?'
cursor = con.cursor().execute(sql, key)
# use this for debuging only
num = 0
for row in cursor.fetchall():
num += 1
return num
This works fine for all PartNo except when PartNo contains a decimal point. Curiously, when PartNo contains a decimal point AND a hyphen, I get the appropriate record(s).
kwargs['PartNo'] = "100.100-2" # returns 1 record
kwargs['PartNo'] = "200.100" # returns 0 records
Both PartNos exist when viewed in the other application, so I know there should be records returned for both queries.
My first thought was to ensure kwargs['PartNo'] is a string key = str(kwargs['PartNo']) with no change.
I also tried to places quotes around the 'PartNo' value with no success. key = '\'' + kwargs['PartNo'] + '\''
Finally, I tried to escape the . with no success (I realize this would break most queries, but I'm just trying to track down the issue with a single period) key = str(kwargs['partNo']).replace('.', '"."')
I know using query parameters should handle all the escaping for me, but at this point, I'm just trying to figure out what's going on. Any thoughts on this?
So the issue isn't with the query parameters - everything works as it should. The problem is with the SQL statement. I incorrectly assumed - and never checked - that there was a record in the Materials table that matched PartNo.
INNER JOIN Materials ON Estim.PartNo = Materials.PartNo
will only return a record if PartNo is found in both tables, which in this particular case it is not.
Changing it to
LEFT OUTER JOIN Materials ON Estim.PartNo = Materials.PartNo
produces the expected results. See this for info on JOINS. https://msdn.microsoft.com/en-us/library/bb243855(v=office.12).aspx
As for print (repr(key)) - flask handles the kwarg type upstream properly
api.add_resource(PartAPI, '/api/v1.0/part/<string:PartNo>'
so when I ran this in the browser, I got the "full length" strings. When run in the cmd line using python -c ....... I was not handling the argument type properly as Gord pointed out, so it was truncating the trailing zeros. I didn't think the flask portion was relevant, so I never added that in the original question.

Lucene Search 2 fields

I tried to search the best matching product (bounty paper towel) from a certain retailer, my query is the following, but the query returns 0 hit.
BooleanQuery.Builder combine = new BooleanQuery.Builder();
Query q1 = new QueryParser("product", new StandardAnalyzer()).parse(QueryParser.escape("product:" + "bounty paper towel"));
combine.add(q1, BooleanClause.Occur.SHOULD); // find best name match
Query q2 = new QueryParser("retailer", new StandardAnalyzer()).parse(QueryParser.escape("retailer:" + "Target"));
combine.add(q2, BooleanClause.Occur.MUST); // Must from this retailer
searcher.search(combine.build(), hitsPerPage).scoreDocs;
Is there anything wrong with the way I build the query?
You are escaping things you don't want to escape. You pass the string "product:bounty paper towel" to the escape method, which will escape the colon, which you don't want to escape. In effect, that query, after escaping and analysis, will look like this:
product:product\:bounty product:paper product:towels
You should escape the search terms, not the entire query. Something like:
parser.parse("product:" + QueryParse.escape("bounty paper towels"));
Also, it looks like you are looking for a phrase query there, in which case, it should be surrounded by quotes:
parser.parse("product:\"" + QueryParse.escape("bounty paper towels") + "\"");
The way your building your boolean query looks fine. You could leverage the query parser syntax to accomplish the same thing, if you prefer, like this:
parser.parse(
"product:\"" + QueryParse.escape("bounty paper towels") + "\""
+ "+retailer:" + QueryParse.escape("Target")
);
But again, there is nothing wrong with BooleanQuery.Builder instead.
Used Lucene too many years ago, but let me try...
Rewrite you parse part as follow:
...
Query q1 = new QueryParser("product", new StandardAnalyzer())
.parse("bounty paper towel");
...
Query q2 = new QueryParser("retailer", new StandardAnalyzer())
.parse("Target"));
...
So your query should contain only target information, but not a column name - since it is already referenced before.

How to search JCR nodes with a version label

Following code segment is used to add a label to a node using ModeShape. What query string I could use to query this node with the QueryManager? It seems that the nodes from versionHistory side cannot be queried with the QueryManager.
VersionHistory history = session.getWorkspace().getVersionManager()
.getVersionHistory(node.getPath());
history.addVersionLabel(version.getName(), "label", true);
I cannot find any nodes with the following query relating to version history. Is there anything missing from this query?
Query query = queryManager
.createQuery("select * from [nt:base]", Query.JCR_SQL2);
Thanks!
All of the version information is stored on the nt:versionHistory nodes under the /jcr:system/jcr:versionStorage area of the workspace (shared amongst all workspaces in a repository). The structure of this area looks something like this:
+ jcr:system
+ jcr:versionStorage {jcr:primaryType = mode:versionStorage}
+ b4 {jcr:primaryType = mode:versionHistoryFolder}
+ 6d {jcr:primaryType = mode:versionHistoryFolder}
+ de {jcr:primaryType = mode:versionHistoryFolder}
+ 298905f76361779339fa3ccacc4f47664255 {jcr:primaryType = nt:versionHistory}
+ jcr:versionLabels {jcr:primaryType = nt:versionLabels}
+ jcr:rootVersion {jcr:primaryType = nt:version}
- jcr:uuid = ...
- jcr:created = ...
+ jcr:frozenNode {jcr:primaryType = nt:frozenNode}
- jcr:frozenUuid
- jcr:frozenPrimaryType
- jcr:frozenMixinTypes
The jcr:versionLabels node contains a property for each label, where the name of the property is the label string and the value of the property is a REFERENCE to the version to which that label applies.
In order to find the labeled version of a versionable node, you'll actually have to query the contents of this "/jcr:system/jcr:versionStorage" area, using the appropriate types and join constraints.
For example, to find the version with a particular label "foo", you'd do something like this:
SELECT versioned.* FROM [nt:frozenNode] AS versioned JOIN [nt:version] AS version ON ISCHILDNODE(versioned,version) JOIN [nt:versionLabels] AS label ON label.myLabel = version.[jcr:uuid]
This search all of the nt:versionLabels nodes looking for a property named 'myLabel' (which is the name of your label), and then finds the nt:version node to which that points, and the nt:frozenNode that is the copy of the versionable node for that version.
As you can see, the version history representations as prescribed by the JCR specification are quite difficult to query.
Alternatively, you could do this in two steps:
use a query to find the jcr:uuid of the version(s) that has a particular label
for each of those identifiers, find the UUID of the versionable node and use the VersionManager to look up the version history for that node and find the labeled version.

Hibernate createSQLQuery and Toad SQL query return different results - parameter problems?

I'm a newbie at Hibernate so excuse me if some of this is glaringly obvious but it's been a very, very long day. I am trying to create and execute a simple query in Hibernate and Toad/Oracle.
The Toad/Oracle sql reads:
select
count(*)
from
fpps_owner.fee_claim_payment_lines l,
fpps_owner.fee_claim_payments p
where
l.fee_claim_payment_id = p.fee_claim_payment_id and
p.claim_index = 87167895
The above returns 10 records, which is correct
The following Java code returns 0 records, which is NOT correct
String sLinesAvailable =
"select count(*) from " +
"fpps_owner.fee_claim_payment_lines l, fpps_owner.fee_claim_payments p " +
"where " +
"l.fee_claim_payment_id = p.fee_claim_payment_id and p.claim_index = :id";
Query qLinesAvailable = em.createNativeQuery(sLinesAvailable);
qLinesAvailable.setParameter("id", "87167895"); // fails
qLinesAvailable.setParameter("id", 87167895); // fails
List<Object> out = (List<Object>) qLinesAvailable.getResultList();
BigDecimal x = (BigDecimal) out.get(0);
Returns 0 records. Using .getSingleResult() also returns 0 records.
What am I missing here?
Any help would be GREATLY appreciated!
If you are not seeing any malformed query errors, it seems like the parameter is not binding correctly.
To debug, I'd print out that SQL statement the line after you set the parameter. This is the only way you can see the SQL after the parameter is set in order to compare it with Toad.
What does your binding file look like? Maybe you have a different name in there for the ID, so it's not able to find it based on the name. Trying binding with the parameter's order value, just as a test.
This might give some ideas: http://www.mkyong.com/hibernate/hibernate-parameter-binding-examples/
Best of luck! We've all been there :)
What happens when you try:
(Number) query.getSingleResult();
Your query isn't returning a list, but rather just a count.
Good luck.