#evolverocks
SEARCH ALL THE THINGS:
OMNISEARCH IN AEM 6.2
JUSTIN EDELSON & OSCAR BOLAÑOS
August 31st, 2016
#evolverocks
#evolverocks 3
ABOUT US
Twitter
twitter.com/justinedelson
Linkledin
linkedin.com/in/justinedelson
Justin Edelson
Team Lead
Linkledin
linkedin.com/in/orbolanos
Oscar Bolaños
Project Lead
#evolverocks4
What is Omnisearch and How do I use it?
Extensibility
Writing a new OmniSearchHandler
UI Implementation
AGENDA
#evolverocks5
Unified Author-Side Content Searching
Consistent User Experience
Consistent Access
Console/Tool Access
WHAT IS OMNISEARCH?
#evolverocks6
1.Open
Slash Key
Search Icon
2.Start Typing
HOW TO USE OMNISEARCH?
#evolverocks7
CROSS-CONTENT TYPE SEARCHES
#evolverocks8
LOCATIONS
#evolverocks9
#evolverocks10
PREDICATES
#evolverocks11
GO TO…
#evolverocks 12
CONSOLE FILTERS
#evolverocks
DEMO
13
#evolverocks 14
LET’S SEE SOME CODE
#evolverocks15
Locations Map 1:1 to implementations of OmniSearchHandler
ADDING A LOCATION
getID() : String
getModuleConf g() : Resource
getResults() : SearchResults
getSuggestionQuery() : Query
getSpellCheckQuery() : Query
getPredicateSuggestions() : List<PredicateSuggestion>
<<interface>>
OmniSearchHandler
#evolverocks16
getID()
Return unique identifier for the search handler
getModuleConfig()
Return a Resource with the configuration of the handler
CONFIGURATION METHODS
#evolverocks17
@Component
@Service
public final class ContentFragmentOmniSearchHandler
implements OmniSearchHandler {
@Override
public Resource getModuleConfig(ResourceResolver resourceResolver) {
return resourceResolver.getResource(
"/apps/aem-omnisearch-content-fragments/content/metadata");
}
@Override
public String getID() {
return "custom-cfm";
}
}
START OF IMPLEMENTATION
#evolverocks18
getResults()
Return the actual search results
getSuggestions()
Return a Query to get the list of suggestions
getSpellCheck()
Return a Query to get the list of spell check suggestions
getPredicateSuggestions()
Get a list of predicate suggestions based on a search term
SEARCH METHODS
#evolverocks 19
Requires configuration of Lucene indexes
e.g. /oak:index/damAssetLucene
Properties can have these two flags:
useInSuggest
useInSpellcheck
Suggestion list is updated every 10 minutes by default, but configurable.
Path restriction support is limited
Property restriction support is non-existing
OAK SUGGESTIONS & SPELL CHECK
A BRIEF DIGRESSION
#evolverocks20
@Override
public SearchResult getResults(ResourceResolver resourceResolver,
Map<String, Object> predicateParameters, long limit, long offset) {
Map<String, String> predicates = new HashMap<String, String>();
predicates.put("path", DamConstants.MOUNTPOINT_ASSETS);
predicates.put("type", DamConstants.NT_DAM_ASSET);
predicates.put("property", "jcr:content/contentFragment");
predicates.put("property.value", "true");
if (predicateParameters.containsKey("fulltext")) {
String[] ft = (String[]) predicateParameters.get("fulltext");
predicates.put("fulltext", ft[0]);
}
PredicateGroup predicatesGroup = PredicateGroup.create(predicates);
com.day.cq.search.Query query = queryBuilder.createQuery(predicatesGroup,
resourceResolver.adaptTo(Session.class));
if (limit != 0) {
query.setHitsPerPage(limit);
}
if(offset != 0) {
query.setStart(offset);
}
SearchResult queryResult = query.getResult();
return queryResult;
}
SEARCH METHOD
THE NAÏVE VERSION
#evolverocks 21
boolean addedPath = false;
boolean addedType = false;
for (Map.Entry<String, Object> param : predicateParameters.entrySet()) {
if (param.getValue() instanceof String[]) {
String[] values = (String[]) param.getValue();
if (values.length == 1) {
if ((param.getKey().equals("path") || param.getKey().endsWith("_path"))
&& values[0].length() > 0) {
addedPath = true;
}
if (param.getKey().equals("type") || param.getKey().endsWith("_type")) {
addedType = true;
}
predicates.put(param.getKey(), values[0]);
}
}
}
if (!addedPath) {
predicates.put("path", DamConstants.MOUNTPOINT_ASSETS);
}
if (!addedType) {
predicates.put("type", DamConstants.NT_DAM_ASSET);
}
predicates.put("999_property", "jcr:content/contentFragment");
predicates.put("999_property.value", "true");
SEARCH METHOD
THE REAL VERSION
#evolverocks 22
public Query getSuggestionQuery(ResourceResolver resourceResolver,
String term) {
String queryStr = "SELECT [rep:suggest()] FROM [dam:Asset] as s " +
" WHERE SUGGEST($term) AND ISDESCENDANTNODE([/content/dam])";
try {
Query query = createQuery(resourceResolver, term, queryStr);
return query;
} catch (RepositoryException e) {
log.error("Unable to create suggestions query", e);
return null;
}
}
SUGGESTIONS METHOD
#evolverocks 23
@Override
public List<PredicateSuggestion> getPredicateSuggestions(
ResourceResolver resourceResolver, I18n i18n, String term) {
List<PredicateSuggestion> matchedPredicates =
new ArrayList<PredicateSuggestion>();
List<PredicateSuggestion> allPredicateSuggestions =
getAllPredicateSuggestions(resourceResolver);
for (PredicateSuggestion suggestion : allPredicateSuggestions) {
if (suggestion.getOptionTitle().toLowerCase().
contains(term.toLowerCase())) {
matchedPredicates.add(suggestion);
}
}
return matchedPredicates;
}
PREDICATE SUGGESTIONS
#evolverocks24
• Control the UI of the search module
• Each handler needs to have one
• Default ones are at /libs/granite/omnisearch/content/metadata
MODULE CONFIGURATION NODES
#evolverocks25
jcr:title
The display title of the search location
listOrder
The order of the location
cardPath
The resource type used to render results in the card view
listItemPath
The resource type used to render results in the list view
clientlibs
Any custom clientlib that needs to be loaded when the location is shown. This is
typically used to handle custom actions when an item is selected.
MODULE CONFIG NODE PROPERTIES
#evolverocks26
actions/selection
Actions that are loaded once an item from that
location is selected. Different actions could be
enabled during the Omnisearch here, as opposed
to the console.
views/list
View configuration that is used while in list view.
This is specially important for the List View
because allows to configure the columns that are
shown.
MODULE CONFIG NODE CHILD NODES
#evolverocks27
<coral-card data-sly-use.data="data.js"
class="foundation-collection-navigator"
data-foundation-collection-navigator-href="${data.navigationHref}"
itemscope="itemscope" itemtype="http://schema.org/WebPage"
colorhint="#ffffff">
<coral-card-asset>
<img src="${data.thumbnailUrl}">
</coral-card-asset>
<coral-card-content>
<coral-card-context>FRAGMENT</coral-card-context>
<coral-card-title class="foundation-collection-item-title"
value="${data.title}">${data.title}</coral-card-title>
</coral-card-content>
<coral-card-propertylist></coral-card-propertylist> <!-- see next slides -->
<link rel="properties" href="${data.navigationHref}">
<meta class="foundation-collection-quickactions" data-foundation-collection-quickactions-rel="">
<coral-quickactions></coral-quickactions> <!-- see next slides -->
</coral-card>
CARD COMPONENT
#evolverocks28
<coral-card-propertylist data-sly-test=${data.lastModified}>
<coral-card-property icon="edit">
<time datetime="${data.lastModified}">
${data.formattedRelativeTime}
</time>
</coral-card-property>
</coral-card-propertylist>
CARD COMPONENT CONTINUED
#evolverocks29
<coral-quickactions target="_prev" alignmy="left top" alignat="left top">
<coral-quickactions-item icon="check”
class="foundation-collection-item-activator">
Select
</coral-quickactions-item>
<coral-quickactions-item icon="edit”
class="foundation-anchor”
data-foundation-anchor-href="${data.navigationHref}">
Edit
</coral-quickactions-item>
<coral-quickactions-item icon="infoCircle" class="foundation-anchor”
data-foundation-anchor-href="${data.propertiesHref}”
data-contextpath = "/assetdetails.html">
View Properties
</coral-quickactions-item>
</coral-quickactions>
CARD COMPONENT CONTINUED
#evolverocks 30
<tr data-sly-use.data="data.js"
data-item-title="${data.title}"
data-foundation-collection-navigator-href="${data.navigationHref}”
data-foundation-collection-item-id="${data.path}"
class="foundation-collection-item foundation-collection-navigator"
is="coral-tr">
<td is="coral-td" coral-tr-select>
<img class="foundation-collection-item-thumbnail" src="${data.thumbnailUrl}"
alt="${data.title}">
</td>
<td class="foundation-collection-item-title" is="coral-td">${data.title}</td>
<td is="coral-td">
<time datetime="${data.lastModified}">
<coral-icon icon="edit" size="xs"></coral-icon> ${data.formattedRelativeTime}</time>
</td>
<td is="coral-td">${data.description}
<meta class="foundation-collection-quickactions"
data-foundation-collection-quickactions-rel="">
</td>
</tr>
ROW ITEM COMPONENT
#evolverocks
DEMO
31
#evolverocks 32
https://github.com/justinedelson/aem-omnisearch-cfm
CODE
#evolverocks33
• Wizard support
• i.e. “Create” -> “Create Page” (suggestion) -> Create Page Wizard
• Tags as Global Predicate
• Recent Search Support
• Improved Save Search Support
FUTURE CONCEPTS
#evolverocks
THANK YOU!

Omnisearch in AEM 6.2 - Search All the Things

Editor's Notes

  • #14 Oscar – usage walkthrough
  • #15 Justin – introduce custom module
  • #23 The Spell check method is essentially the same using rep:spellcheck instead of rep:suggest. Note that you wouldn’t generally need to write this if you extended AbstractOmniSearchHandler
  • #26 You will also see icon and simpleCardPath in some of the OOTB nodes. These aren’t currently used but are planned optimizations in the future.
  • #32 Oscar – usage walkthrough