Property allowedtype cq:pagecontent giving better search results

jamesm77050123 07-09-2018

I'm working on refining an existing search functionality on the site. The search works but returns too many irrelevant results. I'm working on refining the search results to make them more relevant to the search term.

One of the configuration settings being used to create the search predicate is the Property allowedtypes. The current configuration has cq:Page and dam:Asset as the allowedtypes. From trial and error I found out that changing cq:Page to cq:PageContent ends up in better search results. I really don't know why this would be the case so I'd really like to know setting  cq:PageContent as an allowedtype results in better search results. Also, for those who've worked on search functionality, what other suggestions would you give that can help finetune the search?

Accepted Solutions (1)

Accepted Solutions (1)

aneeta45259594 02-10-2018

Hello jamesm77050123

Any further tweaks to the query itself might now be scalable, or efficient. For example, using jcr:like will impact your performance, and I think the only tweak you can make being at query level is something like

"Sorry we didn't find any results for your searched term.

You can try changing the scope of the search term by adding an asterisk, such as bank* fee*, or wrapping your search in quotes and try again."

And a better alternative would be to try exploring the advanced search options for an index itself. For example, there are synonyms available for an index where what you basically have to do is figure out the index used by your query, and add an analyzer to your index with something called "Synonyms". In essence, you create a file, list some possible synonyms for your commonly searched terms and the index will now analyze any available synomyms for the search term input by the user.

It's true that this will be some manual work but considering your business and commonly used search terms, you can start this file with a few common words. You can read about this in Jackrabbit Oak – Lucene Index  and look for the word "Synonym".

While the Synonyms is some manual work, Suggestions and Spellcheck in the index are also available. You can take a look at the link Jackrabbit Oak – Lucene Index for further insight into this.

Some detailed explanation is also listed in https://aemcorner.com/search-in-aem/

I hope this helps.

Best Regards,

Aneet Arora

Answers (9)

Answers (9)

jamesm77050123 03-10-2018

Thanks for the answers. Unfortunately I can't implement an index without management support and bureaucracy and stepping on other peoples toes (company politics)

What I'm wondering, if there a performance difference if i create a JCR_SQL2 query instead of using query builder? Something like

select = SELECT * FROM [jcr:content] AS s WHERE CONTAINS(s.*, '"my/search-expression"') AND s.* LIKE '%my/search-expression%';

//some code

Query query = queryManager.createQuery(select, Query.JCR_SQL2); 

Arun_Patidar
MVP
02-10-2018

Hi,

In QueryBuilder there is no like operation for full text search.

If you know the properties for value then you can use like operation

e.g.

path=/content

1_property=sling:resourceType

1_property.value=foundation/components/text

1_property.operation=like

orderby=path

Arun_Patidar
MVP
02-10-2018

Hi,

Try with SQL2

select * from [nt:base] as s where isdescendantnode([/content]) AND s.[*] LIKE '%bank fee%' AND (s.[jcr:primaryType] = 'cq:Page'  OR (s.[jcr:primaryType] = 'dam:Asset'  AND  s.[jcr:content/metadata/dc:format] = 'application/pdf' ))

jamesm77050123 01-10-2018

I figured out what was wrong. I'm doing a fulltext search (the name says it all), so if searching for a phrase like bank fees, the seach returns all pages with bank and fees. The only other option is to put quotes on 'bank fees', the problem with this approach is that the search returns pages with the exact phrase but pages with 'bank fee' are omitted because they don't match the search phrase. Is there any way of working around this? The Xpath query generated is below: Is it possible to create an Xpath query using Predicates that uses jcr:like instead of jcr:contains?

[

(jcr:contains(., '"bank fees"' ))

and (((@jcr:primaryType = 'cq:Page' ))

or ((@jcr:primaryType = 'dam:Asset' )

and (jcr:content/metadata/@dc:format = 'application/pdf' )))

]

Arun_Patidar
MVP
10-09-2018

Hi,

Its look like you are trying to do full text search under some path(with excluded resource type)within cq:Page or dam:Asset.

Could you try with nt:base or/and nt:unstructured type because page content is not saved only cq:PageContent but nt:unstructured node also.

Could you please correct me if I am wrong and let me know in short what you are trying to do with this query?

jamesm77050123 10-09-2018

Below is the search configconfig.png

Below is the query

ROOT=group: limit=30, guessTotal=true, offset=0[

    {1_path=path: path=/content/site/desktop/gt/en}

    {2_group=group: or=true[

        {1_fulltext=fulltext: fulltext=tax}

    ]}

    {3_group=group: not=true, or=true[

        {1_path=path: path=excludedpaths}

        {2_property=property: property=jcr:content/sling:resourceType, value=foundation/components/redirect}

        {3_property=property: property=jcr:content/sling:resourceType, value=site/components/pages/sightly/coza/desktop/listingpage}

        {4_property=property: property=jcr:content/sling:resourceType, value=site/components/pages/sightly/coza/desktop/flexilistingpage}

        {5_property=property: property=jcr:content/sling:resourceType, value=site/components/pages/sightly/coza/desktop/thankyoupage}

        {6_property=property: property=jcr:content/sling:resourceType, value=site/components/pages/sightly/coza/desktop/errorpage}

        {7_property=property: property=jcr:content/sling:resourceType, value=site/components/pages/sightly/coza/desktop/landingpage}

        {8_property=property: property=jcr:content/sling:resourceType, value=site/components/pages/sightly/coza/desktop/formintroductionpage}

        {9_property=property: property=jcr:content/sling:resourceType, value=site/components/pages/sightly/coza/desktop/listingpagegeneric}

        {10_property=property: property=jcr:content/excludeFromSearch, value=true}

    ]}

    {4_group=group: or=true[

        {1_path=path: path=}

    ]}

    {5_group=group: or=true[

        {1_group=group: or=false[

            {1_type=type: type=cq:Page}

        ]}

        {2_group=group: or=false[

            {1_type=type: type=dam:Asset}

            {2_group=group: or=true[

                {1_property=property: property=jcr:content/metadata/dc:format, value=application/pdf}

            ]}

        ]}

    ]}

]


below the main method being executed for search.

private SearchResult executeSearch(String resourcePath, String[] searchTags, String[] orTags, String searchPath, String[] resultTypes, long offset, long hitsPerPage, Session session, Predicate fulltextPredicate) {

//todo: Handle searchTag and searchPath


SearchFactoryConfig config = getSearchConfig(resourcePath);

if (config == null) {

log.error("no configuration found for the path {}", resourcePath);

return nulpl;

  }


try {

//Search query description will be passed as a hash map

Map<String, String> map = new HashMap<String, String>();

map.put("p.guessTotal", "true");

//map.put("orderby", " @jcr:title");

boolean searchingForTags = false;

PredicateGroup mainGroup = PredicateGroup.create(map);

log.info("resultTypes init = " + resultTypes);

//mainGroup.setAllRequired(false);


if (searchPath != null && !searchPath.isEmpty()) {

mainGroup.add(new Predicate("path").set("path", searchPath));

  }


PredicateGroup andTagsroup = new PredicateGroup();

andTagsroup.setAllRequired(true);

if (searchTags != null && searchTags.length > 0) {

for (String searchTag : searchTags) {

if (!searchTag.isEmpty()) {

andTagsroup.add(new Predicate("tagid").set("tagid", searchTag).set("property", "jcr:content/cq:tags"));

  }

  }

if (andTagsroup.size() > 0) {

mainGroup.add(andTagsroup);

  }

  }


PredicateGroup includedGroup = new PredicateGroup();

includedGroup.setAllRequired(false);


if (orTags != null && orTags.length > 0) {

for (String orTag : orTags) {

if (!orTag.isEmpty()) {

includedGroup.add(new Predicate("tagid").set("tagid", orTag).set("property", "jcr:content/cq:tags"));

  }

  }

  }


if (fulltextPredicate != null) {

includedGroup.add(fulltextPredicate);

  }


mainGroup.add(includedGroup);


PredicateGroup excludedGroup = new PredicateGroup();


excludedGroup.setNegated(true);

excludedGroup.setAllRequired(false);


for (Iterator<String> i = config.getExcludedPaths().iterator(); i.hasNext(); ) {

String excludedPath = i.next();

if (!excludedPath.isEmpty()) {

excludedGroup.add(new Predicate("path").set("path", excludedPath));

  }

  }


for (Iterator<String> i = config.getExcludedTypes().iterator(); i.hasNext(); ) {

String excludedType = i.next();

Predicate propertyPredicate = new Predicate("property").set("property", "jcr:content/sling:resourceType");

propertyPredicate.set("value", excludedType);

excludedGroup.add(propertyPredicate);

  }


Predicate propertyPredicate = new Predicate("property").set("property", "jcr:content/excludeFromSearch");

propertyPredicate.set("value", "true");

excludedGroup.add(propertyPredicate);


mainGroup.add(excludedGroup);


PredicateGroup allowedPathsGroup = new PredicateGroup();

allowedPathsGroup.setAllRequired(false);


PredicateGroup allowedTypesGroup = new PredicateGroup();

allowedTypesGroup.setAllRequired(false);


if (searchingForTags) {


for (Iterator<String> i = config.getTagPaths().iterator(); i.hasNext(); ) {

String tagPath = i.next();

allowedPathsGroup.add(new Predicate("path").set("path", tagPath));

  }


allowedTypesGroup.add(new Predicate("type").set("type", "cq:Tag"));


  } else {


for (Iterator<String> i = config.getAllowedPaths().iterator(); i.hasNext(); ) {

String allowedPath = i.next();

allowedPathsGroup.add(new Predicate("path").set("path", allowedPath));

  }

for (Iterator<String> i = config.getAllowedTypes().iterator(); i.hasNext(); ) {

String allowedType = i.next();

PredicateGroup allowedTypeGroup = new PredicateGroup();

allowedTypeGroup.setAllRequired(true);


if (allowedType.contentEquals("dam:Asset") && config.getAssetMimeTypes().size() > 0) {

PredicateGroup mimeTypesGroup = new PredicateGroup();

mimeTypesGroup.setAllRequired(false);

for (String assetMimeType : config.getAssetMimeTypes()) {

Predicate mimeTypePredicate = new Predicate("property").set("property", "jcr:content/metadata/dc:format");

mimeTypePredicate.set("value", assetMimeType);

mimeTypesGroup.add(mimeTypePredicate);

  }

allowedTypeGroup.add(mimeTypesGroup);

  }


allowedTypesGroup.add(allowedTypeGroup);


  }


  }

mainGroup.add(allowedPathsGroup);

mainGroup.add(allowedTypesGroup);

log.info("mainGroup final init = " + allowedTypesGroup);


Query query = builder.createQuery(mainGroup, session);


query.setStart(offset);


if (hitsPerPage > 0) {

query.setHitsPerPage(hitsPerPage);

  } else {

query.setHitsPerPage(config.getPageSize());

  }


log.info("Query: " + query.getPredicates().toString());

SearchResult result = query.getResult();


return result;

  } catch (Exception e) {

log.error("Error occurred ", e);

  }


return null;

  }

smacdonald2008 07-09-2018

Please provide more details on your search implementation. What API are you using - QueryBuilder?  Can you provide the syntax you are using for the search. This way - the community can help you better.