« Back

Faceted Search in Liferay 6.1

Company Blogs March 9, 2012 By Ray Augé Staff

It's been so long since I've written anything, I've been fealing rather guilty. Luckly recently we've been undertaking a huge effort to document features of Liferay, old and new.

One interesting but highly understated feature of Liferay 6.1 is the new Faceted Search support that I was lucky enough to get to work on. As I finished the first round of documentation (for my more eloquent peers to turn into a more polished and finished product) I thought that this would be a great bunch of info to place here for comment. It's a little more formal than a blog post should be, and probably much longer as well (there is even a toc!!!).. but what the hey!

 

Definitions

Before going through features, let us outline a set of definitions that are commonly used in discussion Faceted Search (or search in general).

indexed field: When we store documents in a search engine, we classify aspects of the document into fields. These fields represent the metadata about each document. Some typical fields are: name, creation date, author, type, tags, content, etc.

term : A term is a single value that can be searched which does not contain any whitespace characters. Terms may appear more than once in a document or appear in several documents, and are typically considered atomic units of search. Within the search engine, each indexed field (for example name) will have a list of known terms found within all the documents having that particular indexed field.

phrase: A phrase is a series of terms separated by spaces. The only way to use a phrase as a term in a search is to surround it with double quotes (").

multi value field: Some fields store more than one term at a time. For instance the "content" field may in fact contain hundreds of unique terms. Such fields are often referred to as "text" or "array" fields.

single value field: In contrast to multi-value fields, we have to logically assume that there is such a thing as single value field. Such fields always only contain a single term. These fields are often referred to as "token" or "string" fields.

frequency: The frequency value indicates how many times a term appears within a set of documents.

facet: A facet is a combination of the information about a specific indexed field, it's terms and their frequency. Facets are typically named by the field in question.

term result list: When a facet displays it's data, we call this the term result list.

frequency threshold: Some facet have a property called frequency threshold. This value indicates the minimum number for frequency of terms we want to show. If the frequency threshold of a facet is set to 1, a term appearing 1 or more times will appear in the term result list.

max terms: Some facet have a property called max terms. This value indicates the maximum number of terms that will be included in the term result list regardless of how many actual matching terms are found for the facet. This is done to keep the user interface under control and not to overwhelm the user with too much information.

order: The order property determines the default ordering used for the term result list. There are two possible modes: Order Hits Descending, or Order Value Ascending. The first, Order Hits Descending , means that results will be ordered by frequency in a descending order. The second, Order Value Ascending, means that the results will be ordered by value (i.e. "term") in ascending order. Both modes will fall back to the other mode as a secondary sort order when there are duplicates. (i.e. many terms with the same frequency will always be sorted by "value").

range: A range defines an interval within which all the matching terms' frequencies are summed. This means that if a facet defines a term range for the "creation time" field between the year 2008 to 2010, and another for 2011 to 2012, all matching documents having a creation time within one of these specified ranges will be returned as a sum for that range. Thus you may find 7 documents in the first range, and 18 documents in the second range. Ranges cannot be used with multi-value fields.

For End Users and Portal Administrators

Faceted search is a new feature in Liferay 6.1 (although some of the APIs were first introduced in Liferay 6.0 EE sp2). As such there is little relevance with previous versions other than in direct comparison with the old implementation of the Search Portlet which had no facet capabilities of any kind.

Although the new Faceted Search APIs are used transparently throughout the portal, primary exposure is surfaced through the new Search Portlet implementation.

What follows is a list of features provided by Faceted Search via the Search Portlet.

  1. Aggregation of assets into a single result set: Results from all portlets are returned as a single set and the relevance is normalized among the entire set, regardless of type (i.e. the best results among all types will be at the top). Searching has a more linear cost due to the fact that only a single query is performed. Searching is therefore faster, more intuitive, and more relevant.

    In previous versions of the portal, each Portlet implemented it's own search and returned a separate result set which resulted in several issues:
    • Each portlet invoked it's own query, and each portlet was called in turn resulting in a single portal request generating potentially N queries to the index each with it's own processing time. This lead to increased time to produce the final view.
    • Depending on the order of how portlet searches were called, the results near the bottom may be the most relevant and due to positioning could appear to have less value than those of portlets positioned physically higher up on the page. i.e. the relevance of results was not normalized across all the total results of all portlets.
  2. Default facets: Asset Type, Asset Tags, Asset Categories, and Modified Time range facets are provided by default. These defaults make finding content on the most common facets simple and powerful. Facets details are displayed in the left column of the search portlet and provide information in context of the current search.
    • Asset Type: Performing a search for the term "htc" may return Asset Type facet details which appear as follows:



      The value in parenthesis is the frequency with the term appearing on the left.

      You may notice that as you perform different searches, the Asset Type terms may disappear and re-appear. When a term does not appear it means; a) it was not be found among the results, b) it did not meet the frequency threshold property, or c) it was beyond the maxTerms property (these properties will be discussed more later).
    • Asset Tags: If tags have been applied to any document which appear in the result set, they may appear in the Asset Tag facet:



      Note: Not all tags may appear. In the example above, there are many more than the 10 tags that are listed, but the default configuration for this facet is to show the top 10 most frequently occuring terms as set by it's maxTerms property.
    • Asset Categories: If categories have been applied to any document which appear in the result set, they may appear in the Asset Categories facet:



      Note: Not all categories may appear. In the example above, there are many more than the 10 categories that are listed, but the default configuration for this facet is to show the top 10 most frequently occuring terms as set by it's maxTerms property.
    • Modified Time: All documents appearing in the result set should have an indexed field called "modified" which indicates when the document was created (or updated). The Modified Time facet is a range facet which provides several pre-configured ranges as well as an option for the user to specify a range. All results in the subsequent query should then fall within this range.
  3. Drill down: The next feature allows refining results by selecting terms from each facet thereby adding more criteria to the search to narrow results (referred to as "drilling down" into the results).

    Clicking on terms adds them to the search criteria (currently only one term per facet). They are then listed in what is known as "token style" just below the search input box for convenience and clarity. Clicking the any token's X removes it from the currently selected criteria.

    e.g. Selected the tag "liferay":


    e.g. Additionally, selected the type "Web Content":
  4. Advanced operations: These are supported directly in the search input box. Most of the advanced operations supported by Lucene are supported with only slight variations.

    For a full description of the Lucene syntax see: http://lucene.apache.org/core/old_versioned_docs/versions/3_0_3/queryparsersyntax.html

    Note: Many of the descriptions bellow are copied (almost word for word) from the above reference to account for the similarities but also to highlight the slight variations found between the two.
    • Searching in specific fields: By default, searches are performed against a long list of fields (this is different from Lucene which searches a single specific field by default). Sometimes you want results for a term within a particular field. This can be achieved using the field search syntax:

      <field>:<term> title:liferay

      Searching for a phrase within a field requires surrounding the term with double quotation marks:

      content:"Create categories"

      Note:The field is only valid for the term that it directly precedes, so the query

      content:Create categories will search for the term "Create" in the content field, and the term "categories" will be searched in "all" the default fields.
    • Wildcard Searches: The Search Portlet supports single and multiple character wildcard searches within single terms not within phrase queries.

      To perform a single character wildcard search use the "?" symbol.

      To perform a multiple character wildcard search use the "*" symbol.

      The single character wildcard search looks for terms that match that with the single character replaced. For example, to search for "text" or "test" you can use the search:

      te?t Multiple character wildcard searches looks for 0 or more characters. For example, to search for test, tests or tester, you can use the search:

      test* You can also use the wildcard searches in the middle of a term.

      te*t Note: You cannot use a "*" or "?" symbol as the first character of a search.
    • Fuzzy Searches : Search supports fuzzy searches based on the Levenshtein Distance, or Edit Distance algorithm. To do a fuzzy search use the tilde, "~", symbol at the end of a single word term.

      For example to search for a term similar in spelling to "roam" use the fuzzy search:

      roam~ This search will find terms like foam and roams.

      An additional (optional) parameter can specify the required similarity. The value is between 0 and 1, with a value closer to 1 only terms with a higher similarity will be matched. For example:

      roam~0.8 The default that is used if the parameter is not given is 0.5.
    • Range Searches: Ranges allow one to match documents whose field(s) values are between the lower and upper bound specified by the range. Ranges can be inclusive or exclusive of the upper and lower bounds. Sorting is done lexicographically.

      modified:[20020101000000 TO 20030101000000] This will find documents whose modified fields have values between 2002/01/01 and 2003/01/01, inclusive.

      Note: Liferay's date fields are always formatted according to the value of the property index.date.format.pattern. The format used should be a sortable pattern. The default date format pattern used is yyyyMMddHHmmss. So, when comparing or searching by dates, this format must be used.

      You can also use ranges with non-date fields:

      title:{Aida TO Carmen} This will find all documents whose titles are between Aida and Carmen, but not including Aida and Carmen.

      Inclusive range queries are denoted by square brackets. Exclusive range queries are denoted by curly brackets.

      Note: Ranges can only be applied to single value fields.
    • Boolean Operators: Boolean operators allow terms to be combined through logical operations. The Search Portlet supports AND, "+", OR, NOT and "-" as Boolean operators.

      Note: Boolean operators must be ALL CAPS.
      • The OR operator is the default conjunction operator. This means that if there is no Boolean operator between two terms, the OR operator is used. The OR operator links two terms and finds a matching document if either of the terms exist in a document. This is equivalent to a union using sets. The symbol || can be used in place of the word OR.

        To search for documents that contain either "liferay portal" or just "liferay" use the query:

        "liferay portal" liferay or

        "liferay portal" OR liferay
      • The AND operator matches documents where both terms exist anywhere in the text of a single document. This is equivalent to an intersection using sets. The symbol && can be used in place of the word AND.

        To search for documents that contain "liferay portal" and "Apache Lucene" use the query:

        "liferay portal" AND "Apache Lucene"
      • The "+" or required operator requires that the term after the "+" symbol exist somewhere in a field of a single document.

        To search for documents that must contain "liferay" and may contain "lucene" use the query:

        +liferay lucene
      • The NOT operator excludes documents that contain the term after NOT. This is equivalent to a difference using sets. The symbol ! can be used in place of the word NOT.

        To search for documents that contain "liferay portal" but not "Apache Lucene" use the query:

        "liferay portal" NOT "Apache Lucene" Note: The NOT operator cannot be used with just one term. For example,the following search will return no results:

        NOT "liferay portal"
      • The "-" or prohibit operator excludes documents that contain the term after the "-" symbol.

        To search for documents that contain "liferay portal" but not "Apache Lucene" use the query:

        "liferay portal" -"Apache Lucene"
    • Grouping: Search supports using parentheses to group clauses to form sub queries. This can be very useful if you want to control the boolean logic for a query.

      To search for either "liferay" or "apache" and "website" use the query:

      (liferay OR apache) AND website This eliminates any confusion and makes sure that website must exist and either term liferay or apache may exist.
    • Field Grouping: Search supports using parentheses to group multiple clauses to a single field.

      To search for a title that contains both the word "return" and the phrase "pink panther" use the query:

      title:(+return +"pink panther")
    • Proximity Searches and Term Boosting are not supported.

Portlet Configuration

[Updated: Oct 16 2012] Search portlet configurations are currently scoped to the site page, which means that all Search Portlets used in the same site will have the same settings, regardless of their location or position on different pages will have their own configurations; this also includes any instances of the portlet embedded in themes, or other templates.

Display Settings:

  • Basic : This represents the most basic way of controlling the visible facets.

    Display Asset Type Facet: Display or not.
    Display Asset Tags Facet: Display or not.
    Display Asset Categories Facet: Display or not.
    Display Modified Range Facet: Display or not.
  • Advanced: This mode gives ultimate control over the display of facets and is where the true power lies in the Search Portlet. However, it is not for the faint of heart and requires creating a configuration in JSON format. (Future versions of Liferay will include a user friendly user interface for configuration of facets.)

    In it's default configuration, the Search Portlet configuration would equate to the following JSON text:
    {"facets": [
    	{
    		"className": "com.liferay.portal.kernel.search.facet.AssetEntriesFacet",
    		"data": {
    			"frequencyThreshold": 1,
    			"values": [
    				"com.liferay.portlet.bookmarks.model.BookmarksEntry",
    				"com.liferay.portlet.blogs.model.BlogsEntry",
    				"com.liferay.portlet.calendar.model.CalEvent",
    				"com.liferay.portlet.documentlibrary.model.DLFileEntry",
    				"com.liferay.portlet.journal.model.JournalArticle",
    				"com.liferay.portlet.messageboards.model.MBMessage",
    				"com.liferay.portlet.wiki.model.WikiPage",
    				"com.liferay.portal.model.User"
    			]
    		},
    		"displayStyle": "asset_entries",
    		"fieldName": "entryClassName",
    		"label": "asset-type",
    		"order": "OrderHitsDesc",
    		"static": false,
    		"weight": 1.5
    	},
    	{
    		"className": "com.liferay.portal.kernel.search.facet.MultiValueFacet",
    		"data": {
    			"maxTerms": 10,
    			"displayStyle": "list",
    			"frequencyThreshold": 1,
    			"showAssetCount": true
    		},
    		"displayStyle": "asset_tags",
    		"fieldName": "assetTagNames",
    		"label": "tag",
    		"order": "OrderHitsDesc",
    		"static": false,
    		"weight": 1.4
    	},
    	{
    		"className": "com.liferay.portal.kernel.search.facet.MultiValueFacet",
    		"data": {
    			"maxTerms": 10,
    			"displayStyle": "list",
    			"frequencyThreshold": 1,
    			"showAssetCount": true
    		},
    		"displayStyle": "asset_tags",
    		"fieldName": "assetCategoryTitles",
    		"label": "category",
    		"order": "OrderHitsDesc",
    		"static": false,
    		"weight": 1.3
    	},
    	{
    		"className": "com.liferay.portal.kernel.search.facet.ModifiedFacet",
    		"data": {
    			"ranges": [
    				{
    					"range": "[past-hour TO *]",
    					"label": "past-hour"
    				},
    				{
    					"range": "[past-24-hours TO *]",
    					"label": "past-24-hours"
    				},
    				{
    					"range": "[past-week TO *]",
    					"label": "past-week"
    				},
    				{
    					"range": "[past-month TO *]",
    					"label": "past-month"
    				},
    				{
    					"range": "[past-year TO *]",
    					"label": "past-year"
    				}
    			],
    			"frequencyThreshold": 0
    		},
    		"displayStyle": "modified",
    		"fieldName": "modified", 
    		"label": "modified",
    		"order": "OrderHitsDesc",
    		"static": false,
    		"weight": 1.1
    	}
    ]}
    
    The base definition consists of a JSON object with a field of type array named "facets":
    {"facets": []}
    This array must contain elements (which in JSON are called Objects) having the following mandatory structure:
    {
    	"className": ...,
    	"data": ...,
    	"displayStyle": ...,
    	"fieldName": ...,
    	"label": ...,
    	"order": ...,
    	"static": ...,
    	"weight": ...
    }
    
    • "className": This field must contain a string value which is the FQCN (fully qualified class name) of a java implementation class implementing the Facet interface. Liferay provides the following implementations by default:
      "com.liferay.portal.kernel.search.facet.AssetEntriesFacet"
      "com.liferay.portal.kernel.search.facet.ModifiedFacet"
      "com.liferay.portal.kernel.search.facet.MultiValueFacet"
      "com.liferay.portal.kernel.search.facet.RangeFacet"
      "com.liferay.portal.kernel.search.facet.ScopeFacet"
      "com.liferay.portal.kernel.search.facet.SimpleFacet"
      
    • "data": This field takes an arbitrary JSON "Object" (a.k.a. {}) for use by a specific facet implementation. As such, there is no fixed definition of the data field. Each implementation is free to structure it as needed.
    • "displayStyle": This field takes a value of type string and represents a particular template implementation which is used to render the facet. These templates are normally JSP pages (but can also be implemented as Velocity or Freemarker templates provided by a theme if the portal property theme.jsp.override.enabled is set to true). The method of matching the string to a JSP is simply done by prefixing the string with /html/portlet/search/facets/ and appending the .jsp extension.

      e.g. "displayStyle": "asset_tags"

      maps to the JSP

      /html/portlet/search/facets/asset_tags.jsp Armed with this knowledge a crafty developer could create custom display styles by deploying custom (new or overriding) JSPs using a JSP hook.
    • "fieldName": This field takes a string value and indicates the indexed field on which the facet will operate.

      e.g. "fieldName": "entryClassName"

      indicates that the specified facet implementation will operate on the entryClassName indexed field.

      Note: You can identify available indexed fields by checkmarking the Search Portlet's Display Results in Document Form configuration setting and then expanding individual results by clicking the [+] to the left of their title.
    • "label": This field takes a string value and represents the language key that will be used for localizing the title of the facet when rendered.
    • "order": This field takes a string value. There are two possible values:

      "OrderValueAsc" This tells the facet to sort it's results by the term values, in ascending order.

      "OrderHitsDesc" This tells the facet to sort it's results by the term frequency, in descending order.
    • "static": This field takes a boolean value (true or false). A value of true means that the facet should not actually be rendered in the UI. It also means that, rather than using inputs dynamically applied by the end user, it should use pre-set values (stored in it's "data" field). This allows for the creation of pre-configured result domain. The default value is false.

      Image Search Example: Imagine you would like to create a pre-configured search that returns only images (i.e. the indexed field "entryClassName" would be com.liferay.portlet.documentlibrary.model.DLFileEntry and the indexed field "extension" should contain one of bmp, gif, jpeg, jpg, odg, png, or svg). We would need two static facets, one with "fieldName": "entryClassName" and another with "fieldName": "extension". This could be represented using the following facet configuration:
      {
      	"displayStyle": "asset_entries",
      	"static": true,
      	"weight": 1.5,
      	"order": "OrderHitsDesc",
      	"data": {
      		"values": [
      			"com.liferay.portlet.documentlibrary.model.DLFileEntry"
      		],
      		"frequencyThreshold": 0
      	},
      	"className": "com.liferay.portal.kernel.search.facet.AssetEntriesFacet",
      	"label": "asset-type",
      	"fieldName": "entryClassName"
      },
      {
      	"displayStyle": "asset_entries",
      	"static": true,
      	"weight": 1.5,
         	"order": "OrderHitsDesc",
      	"data": {
      		"values": [
      			"bmp", "gif", "jpeg", "jpg", "odg", "png", "svg"
      		],
      		"frequencyThreshold": 0
         	},
      	"className": "com.liferay.portal.kernel.search.facet.MultiValueFacet",
      	"label": "images",
      	"fieldName": "extension"
      }
      
      			
    • "weight": This field takes a floating point (or double) value and is used to determine the ordering of the facets in the facet column of the search portlet. Facets are positioned with the largest values at the top (yes it's counter intuitive and perhaps should be reversed in future versions).

Other Settings

  • Display Results in Document Form: This configuration, if checked, will display each result with an expendable section you can reach by clicking the [+] to the left of the result's title. In Document Form, all of the result's indexed fields will be shown in the expandable section. This is for use in testing search behavior.

    Note: Even if enabled, for security reasons this ability is only available to the portal Administrator role because the raw contents of the index may expose protected information.
  • View in Context: This configuration, if checked, will produce results which have links that target the first identifiable application to which the result is native.

    For example, a Blog entry title will link (or attempt to link) to a Blogs Admin, Blogs, or Blogs Aggregator portlet somewhere in the current site. The exact method of location is defined by the result type's AssetRenderer implementation.
  • Display Main Query: This configuration, if checked, will output the complete query that was used to perform the search. This will appear directly bellow the result area, like this:

  • Display Open Search Results: In previous versions of the portal, the Search Portlet was implemented as a collection of com.liferay.portal.kernel.search.OpenSearch implementation classes which were executed in series. Due to the subsequent re-design of the Search Portlet, the portal itself no longer relies on these implementations for it's primary search. However, third party plugin developers may yet have Open Search implementations which they would like to continue to use. This configuration, if checked, will enable the execution of these third party Open Search implementations and results will appear bellow the primary portal search.

    Note: It is highly recommended that third parties re-design their search code to implement com.liferay.portal.kernel.search.Indexer or more simply to extend com.liferay.portal.kernel.search.BaseIndexer. Thus it will be possible to aggregate custom assets with native portal assets.

For Developers

Key Classes

When implementing a customized search, many of following API classes are important:

com.liferay.portal.kernel.search.SearchContext
com.liferay.portal.kernel.search.SearchContextFactory
com.liferay.portal.kernel.search.facet.config.FacetConfiguration
com.liferay.portal.kernel.search.facet.config.FacetConfigurationUtil
com.liferay.portal.kernel.search.facet.util.FacetFactoryUtil
com.liferay.portal.kernel.search.facet.Facet
com.liferay.portal.kernel.search.Indexer
com.liferay.portal.kernel.search.IndexerRegistryUtil
com.liferay.portal.kernel.search.BaseIndexer
com.liferay.portal.kernel.search.FacetedSearcher
com.liferay.portal.kernel.search.SearchEngineUtil
com.liferay.portal.kernel.search.Hits
com.liferay.portal.kernel.search.Document
com.liferay.portal.kernel.search.facet.collector.FacetCollector
com.liferay.portal.kernel.search.facet.collector.TermCollector

We'll briefly go through the general organization of the above to understand where each class fits into the greater scheme.

SearchContext

The first thing required is to setup a context within which to perform a search. The context defines things like company instance to search, the current user invoking the search, etc. This task is handled by the com.liferay.portal.kernel.search.SearchContext class. Since this class has a wide variety of context properties to deal with, the most effective way to get one is to call the getInstance(HttpServletRequest request) method of the com.liferay.portal.kernel.search.SearchContextFactory class.

SearchContext searchContext = SearchContextFactory.getInstance(request);

Context Properties

Once you have a SearchContext instance, we then can populate values like the pagination style, start and end:

searchContext.setAttribute("paginationType", "more");
searchContext.setEnd(mainSearchSearchContainer.getEnd());
searchContext.setStart(mainSearchSearchContainer.getStart());

There are number of other SearchContext properties that can be set. See the javadocs for a complete list.

Setting up Facets

After we have setup all the appropriate SearchContext properties, we are ready to add the Facets for which we want to collect information. We can add Facets either programatically or through configuration. Programatically adding facets allows the developer to tightly control how the search is used. The following example shows how to add two facets using some provided Facet classes:

Facet assetEntriesFacet = new AssetEntriesFacet(searchContext);
assetEntriesFacet.setStatic(true);
searchContext.addFacet(assetEntriesFacet);

Facet scopeFacet = new ScopeFacet(searchContext);
scopeFacet.setStatic(true);
searchContext.addFacet(scopeFacet);

Note: The above two Facet implementations are not re-usable in that they always operate on specific indexed fields; entryClassName, and groupId (and scopeGroupId) respectively. Other implementations can be re-used with any index fields as demonstrated previously in the Image Search Example.

As shown previously, facets can also be setup using a JSON definition. Using a JSON definition allows for the highest level of flexibility since the configuration can be changed at run-time. These definitions are parsed by the static method load(String configuration) on the com.liferay.portal.kernel.search.facet.config.FacetConfigurationUtil class. This method reads the JSON text and returns a list of com.liferay.portal.kernel.search.facet.config.FacetConfiguration instances.

List<FacetConfiguration> facetConfigurations = FacetConfigurationUtil.load(searchConfiguration);

for (FacetConfiguration facetConfiguration : facetConfigurations) {
	Facet facet = FacetFactoryUtil.create(searchContext, facetConfiguration);

	searchContext.addFacet(facet);
}

Facets as Filters

It should be noted that Facets are always created with reference to the SearchContext. Since facets also behave as the dynamic filter mechanism for narrowing search results, having the SearchContext allows a Facet implementation to observe and react to context changes such as looking for specific parameters which affect it's behavior.

Indexer Implementations

The next step involves obtaining a reference to an indexer implementation. The implementation obtained determines the type of results return from the search.

With respect to searching, there are two categories of Indexer implementations: Asset Specific Searchers and Aggregate Searchers.

Asset Specific Searchers

As the name implies, Asset Specific Searchers always deal with only one specific type of asset. These are the implementations that are provided by developers when creating/designing custom Asset types. Liferay provides the following Asset Specific Searchers:

com.liferay.portal.plugin.PluginPackageIndexer
com.liferay.portlet.blogs.util.BlogsIndexer
com.liferay.portlet.bookmarks.util.BookmarksIndexer
com.liferay.portlet.calendar.util.CalIndexer
com.liferay.portlet.documentlibrary.util.DLIndexer
com.liferay.portlet.journal.util.JournalIndexer
com.liferay.portlet.messageboards.util.MBIndexer
com.liferay.portlet.softwarecatalog.util.SCIndexer
com.liferay.portlet.usersadmin.util.OrganizationIndexer
com.liferay.portlet.usersadmin.util.UserIndexer
com.liferay.portlet.wiki.util.WikiIndexer

A developer tells the portal about Indexer implementations by declaring them in their liferay-portlet.xml file.

<indexer-class>com.liferay.portlet.calendar.util.CalIndexer</indexer-class>

Any number of such implementations may be provided.

Aggregate Searchers

Obtaining a reference to an Asset Specific Indexer requires calling either the getIndexer(Class<?> clazz) or getIndexer(String className) methods on the com.liferay.portal.kernel.search.IndexerRegistryUtil class.

Indexer indexer = IndexerRegistryUtil.getIndexer(PluginPackage.class);

Aggregate Searchers can return any of the asset types in the index according to the SearchContext and/or facet configuration. Liferay only provides a single aggregate searcher implementation:

com.liferay.portal.kernel.search.FacetedSearcher

Obtaining a reference to this searcher simply involves calling the static getInstance() method of the same class.

Indexer indexer = FacetedSearcher.getInstance();

Note<: When implementing Indexers it is highly recommended to extend the com.liferay.portal.kernel.search.BaseIndexer class.

SearchEngineUtil

Internally each Indexer will make calls to the SearchEngineUtil which handles all the intricacies of the engine implementation. For the purpose of this document, we won't delve into the internals of SearchEngineUtil. But suffice it to say that all traffic to and from the search engine implementation passes through this class, and so when debuging problems it is often beneficial to enable debugging level logging on this class.

Performing the Search

Once an Indexer instance has been obtained, searches are performed by calling its search(SearchContext searchContext) method.

Hits & Documents

The result of the search method is an instance of the com.liferay.portal.kernel.search.Hits class.

Hits hits = indexer.search(searchContext);

This object contains any search results in the form of an array (or list) of com.liferay.portal.kernel.search.Document instances.

Document[] docs = hits.getDocs();

OR

List<Document> docs = hits.toList();

The results display typically involves iterating over this array. Each Document is effectively a hash map of the indexed fields and values.

Facet Rendering

Facet rendering is done by getting Facets from the SearchContext after the search has completed and passing each to a template as defined by the FacetConfiguration:

Map<String, Facet> facets = searchContext.getFacets();
List<Facet> facetsList = ListUtil.fromCollection(facets.values());
facetsList = ListUtil.sort(
	facetsList, new PropertyComparator("facetConfiguration.weight", false, false));

for (Facet facet : facetsList) {
	if (facet.isStatic()) {
		continue;
	}

	FacetConfiguration facetConfiguration = facet.getFacetConfiguration();
	request.setAttribute("search.jsp-facet", facet);

%>

	<liferay-util:include page='<%= "/html/portlet/search/facets/" + facetConfiguration.getDisplayStyle() + ".jsp" %>' />

<%
}

Facet Details (Terms and Frequencies)

A Facet's details are obtained by calling it's getFacetCollector() method which returns an instance of com.liferay.portal.kernel.search.facet.collector.FacetCollector class.

FacetCollector facetCollector = facet.getFacetCollector();

The primary responsibility of this class is to in turn provide access to TermCollector instances primarily by calling the getTermCollectors() method, but also by getting a TermCollector by term value using the getTermCollector(String term) method. There will be a TermCollector for each term that matches the search criteria, as well as the facet configuration.

List<TermCollector> termCollectors = facetCollector.getTermCollectors();

OR

TermCollectorterm termCollector = facetCollector.getTermCollector(term);

And finally, the com.liferay.portal.kernel.search.facet.collector.TermCollector class provides the getFrequency() method.

<%= termCollector.getTerm() %> <span class="frequency">(<%= termCollector.getFrequency() %>)</span>

Rendered facet views (i.e. non-static facets) should result in UI code which allows dynamically passing facet parameters the interpretation by the implementation (see Facets as Filters). There are a number of examples in the /html/portlet/search/facets folder of the Search Portlet.

============================================================

Well, I hope that was useful information.

At the core of all content management lies search and so I'm really excited about the potential of this new search API. As we work on 6.2 and introduce even more innovative new search features, I hope to see Liferay become the most feature rich and extensible search integration platform available in the market. 

Threaded Replies Author Date
This is awesome Ray!!! Thanks for the post... Kamesh Sampath March 9, 2012 8:22 PM
We have abstracted our own Facet framework so... Ray Augé March 9, 2012 8:35 PM
Nice Blog Ray !!! Faceted Search is cool !! And... Tejas Kanani March 12, 2012 2:15 AM
Nice post! Jonas Yuan March 13, 2012 9:15 AM
Your posts are always interesting and... Manish Kumar Gupta March 14, 2012 8:11 AM
That is a lot of information. I think I'm going... Dana Oredson March 14, 2012 7:19 PM
Really great detailing about faceted search... Nagendra Kumar Busam March 14, 2012 10:47 PM
Thanks All. Ray Augé March 15, 2012 7:24 AM
Ray I always have an eagerness for reading your... Erin Caroll March 19, 2012 9:04 PM
Hi..i m new to liferay..i want to create search... Siva Ranjani March 21, 2012 11:14 PM
Hi Ray, I wanna thank you about this post... Firas BD March 22, 2012 1:42 AM
@Siva, the best sample code is the search... Ray Augé March 22, 2012 6:16 AM
@Ray Augé: the error is with the default search... Firas BD March 22, 2012 6:35 AM
Can you file a ticket for this which outlines... Ray Augé March 22, 2012 6:39 AM
@ Ray: http://issues.liferay.com/browse/LPS-26227 Firas BD March 22, 2012 6:53 AM
Hi Ray, I'm new to Liferay and need your... Vlad Kuzmyk March 26, 2012 9:16 AM
Hi, Any progress on this. I have modified... Suyash Bhalekar June 3, 2013 12:22 AM
Hi, Any progress on this. I have modified... Suyash Bhalekar June 3, 2013 12:22 AM
Nice post, and timely too. I was just about to... Joseph Toman March 24, 2012 12:45 PM
Never mind about that second question, found it... Joseph Toman March 24, 2012 3:50 PM
Hi Ray, Thanks for the article. I'm trying to... Jonathan Lloyd April 9, 2012 9:09 AM
Try +(+assetCategoryIds:11703... Ray Augé April 9, 2012 9:20 AM
AND should be used more specifically like so: ... Ray Augé April 9, 2012 9:20 AM
Hi Ray, Thanks for the quick reply! I have... Jonathan Lloyd April 9, 2012 9:34 AM
Ray, just found this post after looking into... Dave Weitzel April 27, 2012 1:41 PM
Hi Ray, I have a problem with create... Salvador Baena May 10, 2012 3:25 AM
It is possible that Liferay can index content... Amit Doshi May 22, 2012 12:53 AM
@Salvador, make your "noticia" facet static... Ray Augé May 22, 2012 7:07 AM
Wow, incredible article! I would like to know... Florencia Gadea May 23, 2012 7:53 AM
It's true that the search portlet doesn't start... Ray Augé May 23, 2012 8:21 AM
Thanks Ray for the prompt reply. I just added a... Florencia Gadea May 23, 2012 8:43 AM
Thank you very much Ray. Solved. Facet to... Salvador Baena May 23, 2012 10:00 AM
Hi, I would like to know how drill down can be... ASI Register May 25, 2012 2:42 AM
Yeah, this was deferred to a later version.... Ray Augé May 25, 2012 12:35 PM
Hi Ray, I would like to know what is the best... Matteo S June 28, 2012 8:44 AM
Facets are for single fields only (both Bobo... Ray Augé June 28, 2012 8:57 AM
BTW, the "Multi" in the name MultiValueFacet is... Ray Augé June 28, 2012 9:08 AM
Hi Ray, I have the requirements to pass... Himanshu Modi June 29, 2012 3:27 AM
Typo--(assetCategoryNames:topic2... Himanshu Modi June 29, 2012 4:24 AM
The correct way to do that would be by passing... Ray Augé July 20, 2012 6:16 AM
Hi Ray, is there any way to integrate a custom... Kim Anna Kunc August 16, 2012 1:16 AM
I figured it it out, my Indexer extends... Kim Anna Kunc August 16, 2012 1:45 AM
Ok, great! I'm glad you managed to figure it... Ray Augé August 16, 2012 7:40 AM
Ray, thanks for your reply! What would think of... Kim Anna Kunc August 28, 2012 12:57 AM
That would be cool (JIRA feature request?). Ray Augé August 29, 2012 9:41 AM
Hi Ray, A really useful article. Can you tell... Denis Signoretto September 11, 2012 4:30 AM
Unfortunately not at the moment (JIRA feature... Ray Augé September 11, 2012 6:25 AM
Hi Ray, I do understand your point about... Matteo S September 12, 2012 8:50 AM
Thanks for this detailed blog about Faceted... Gwowen Fu October 6, 2012 9:33 AM
The data is not cached so much as it may be... Ray Augé October 16, 2012 1:52 PM
@Matteo, As I stated previously, you can use... Ray Augé October 16, 2012 2:01 PM
Hi Ray, Thank you for very informative and... Yogesh Sharma October 24, 2012 11:20 PM
@Yogesh, that is correct! Thanks for pointing... Ray Augé October 29, 2012 8:39 AM
Hi Ray, Is there a way to exclude certain... Bart Simpson November 21, 2012 4:50 AM
A facet by it's very nature can only search one... Ray Augé November 21, 2012 8:01 AM
Hi Ray, Thank you for very informative blog. ... Jonas Yuan November 21, 2012 9:00 AM
Jonas, categoryId is better in this case. Ray Augé November 21, 2012 9:03 AM
Hi Ray, I'm new to Liferay in my custom theme... Matteo Galletti December 14, 2012 9:30 AM
Ok, I think I see the problem. First, the... Ray Augé December 14, 2012 10:19 AM
Hi Ray, This info. is really good. i have some... Saurabh Gupta January 7, 2013 4:11 AM
Ray, Is putting the default search... Mike Ring January 29, 2013 12:50 PM
Hi Ray, I've implemented a custom Facet and... Gunther Verhemeldonck February 21, 2013 4:49 AM
I the case of Facet class implementations, we... Ray Augé February 22, 2013 7:44 AM
We have a problem with the facet search. If... Gunther Verhemeldonck February 26, 2013 5:13 AM
Sorry to hear that. Can you enable showing the... Ray Augé February 26, 2013 6:36 AM
We believe it might be a bug. The... Gunther Verhemeldonck February 27, 2013 1:06 AM
Confirmed as a bug. Gunther Verhemeldonck March 21, 2013 8:32 AM
Hello Ray, Nice explanation. In... Yagneswar Macherla March 1, 2013 8:01 AM
Hi, it seems that with 6.1.1 CE boolean... Test Test April 8, 2013 4:17 AM
I have a problem with the fuzzy search. I have... haikel thamri April 26, 2013 7:24 AM
AssetTags are not indexed! your entryClassName... Ray Augé April 26, 2013 7:27 AM
I have implemented a custom indexer in my... haikel thamri April 26, 2013 7:33 AM
Ok, can you give me some examples of indexed... Ray Augé April 26, 2013 7:35 AM
With luke this query for example... haikel thamri April 26, 2013 7:42 AM
excuse me the query is +(name:routar~)... haikel thamri April 26, 2013 7:42 AM
excuse me the query is +(name:routar~)... haikel thamri April 26, 2013 7:42 AM
Ok, as soon as I get a chance will play with... Ray Augé April 26, 2013 7:45 AM
Done! LPS-34774 Thank you :) haikel thamri April 26, 2013 8:13 AM
Hi Ray, Great post!! I'm having a few issues... Brian Scott Schupbach May 2, 2013 2:23 PM
for anyone interested...This has been confirmed... Brian Scott Schupbach June 12, 2013 11:29 AM
by "this" I mean "I can't seem to filter by... Brian Scott Schupbach June 12, 2013 11:30 AM
Hi Ray, I have implemented a custom indexer in... haikel thamri April 26, 2013 7:37 AM
Sure, I got that part. Can you give me some... Ray Augé April 26, 2013 7:40 AM
I am using sorting by a field in Webcontent... Sultan Zahir Khan June 12, 2013 10:17 AM
Hello Ray, Thanks for the great post. I' m... Huage Chen June 14, 2013 8:28 AM
Is it possible to configure the faceted Search... Bert Godon September 4, 2013 4:21 AM
yes it can be done by selecting only these... Himanshu Modi September 4, 2013 10:35 AM
Please see... Ray Augé September 4, 2013 11:29 AM
H Ray, I tried to add ... Nabil Bahtat October 24, 2013 1:22 PM
It must be an entity which is indexed. Ray Augé October 24, 2013 1:24 PM
Hi Ray, How to make Polls portlet an indexed... Nabil Bahtat October 28, 2013 11:21 PM
Minimally, you need to create and register an... Ray Augé October 29, 2013 9:23 AM
Hi Ray, I managed to do all the tasks about... Nabil Bahtat January 12, 2014 11:24 AM
I have a question about how the search is... Robert Zahm December 2, 2013 3:14 PM
Is it possible to highlight the searchresult... lili samanta March 13, 2014 7:00 AM
Hi Ray , we are passing facets as json . but... deepthi p March 26, 2014 3:14 AM
Hi Ray, Could you please expand on the... Adam Steiner April 15, 2014 2:36 PM
Hi Ray, We have requirement for search only... Nitin Yeola May 19, 2014 3:14 AM
Good Morning. I need to make through faceted... Miguel González October 28, 2014 1:10 AM
Hi Ray, I have installed liferay 6.2 GA2... Alba Garcia March 20, 2015 6:38 AM
Because I believe what you are looking for is ... Ray Augé March 21, 2015 1:10 PM
Thanks Ray! I have imported the... Alba Garcia March 29, 2015 11:32 PM
Hi Ray, In Liferay 7 M7, I found following... Fuad Efendi August 24, 2015 1:15 PM

This is awesome Ray!!! Thanks for the post
Just couple of questions around the frameworks used did we try to use Solor or still continued with Lucene and wrote our facet search framework.
Posted on 3/9/12 8:22 PM.
We have abstracted our own Facet framework so that we could build it more or less on any engine. For Lucene, facet support is implemented using the Bobo engine (http://code.google.com/p/bobo-browse/) and in the case of Solr we just use it's features directly. In both cases we wrap the underlying technology with our own API. The idea being that you could plug any engine in back there and our front end APIs don't need to change. The API is very simple for facet support.
Posted on 3/9/12 8:35 PM in reply to Kamesh Sampath.
Nice Blog Ray !!!
Faceted Search is cool !!
And this blog covers lots of details regarding Search.
Thanks for the post.
Posted on 3/12/12 2:15 AM.
Posted on 3/13/12 9:15 AM.
Your posts are always interesting and informative. Thanks for sharing this with everyone Ray!!!
Posted on 3/14/12 8:11 AM.
That is a lot of information. I think I'm going to need to read it several times in order to hold it all in my head. Nice work. It looks like a lot of time and effort went into this post.
Posted on 3/14/12 7:19 PM.
Really great detailing about faceted search feature.
Posted on 3/14/12 10:47 PM.
Posted on 3/15/12 7:24 AM in reply to Nagendra Kumar Busam.
Ray I always have an eagerness for reading your posts and articles because I know all your articles are informative as well as knowledgeable..
Posted on 3/19/12 9:04 PM.
Hi..i m new to liferay..i want to create search portlet using lucene..can u suggest steps for me..any sample code...

Thank you..
Posted on 3/21/12 11:14 PM in reply to Erin Caroll.
Hi Ray,
I wanna thank you about this post because it's so informative, but I have just a problem: when for example I am searching for a word, and results are in more than one page when I click to go to the next page (I click "More") I got error and no results appear ..
Posted on 3/22/12 1:42 AM in reply to Siva Ranjani.
@Siva, the best sample code is the search portlet itself: https://github.com/liferay/liferay-portal/blob/master/portal-web/docroot/html/po­rtlet/search/main_search.jspf

@Firas, is the error with the default search portlet or with custom code?
Posted on 3/22/12 6:16 AM in reply to Firas BD.
@Ray Augé:
the error is with the default search portlet in Liferay 6.1: and this is the error syntax:

12:06:15,737 ERROR [IncludeTag:154] com.liferay.portal.kernel.search.SearchException: java.lang.IllegalArgumentException: fromIndex(20) > toIndex(5)
at com.liferay.portal.kernel.search.FacetedSearcher.search(FacetedSearcher.java:106­)
at org.apache.jsp.html.portlet.search.search_jsp._jspService(search_jsp.java:1073)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilt­erChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.­java:210)
at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(In­vokerFilterChain.java:72)
at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilter.doFilter(Invoker­Filter.java:70)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilt­erChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.­java:210)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java­:684)
at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.j­ava:593)
at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.jav­a:530)
at com.liferay.taglib.util.IncludeTag.include(IncludeTag.java:323)
at com.liferay.taglib.util.IncludeTag._doInclude(IncludeTag.java:418)
at com.liferay.taglib.util.IncludeTag.doEndTag(IncludeTag.java:92)
Caused by: java.lang.IllegalArgumentException: fromIndex(20) > toIndex(5)
at java.util.SubList.<init>(AbstractList.java:604)
at java.util.RandomAccessSubList.<init>(AbstractList.java:758)
Posted on 3/22/12 6:35 AM in reply to Ray Augé.
Can you file a ticket for this which outlines the exact steps you used to reproduce it? I'll take a look as soon as I get a chance.
Posted on 3/22/12 6:39 AM in reply to Firas BD.
@ Ray:
http://issues.liferay.com/browse/LPS-26227
Posted on 3/22/12 6:53 AM in reply to Ray Augé.
Nice post, and timely too. I was just about to implement search in a project I'm working on. A couple of questions: you said that proximity search and term boosting aren't supported. Is that just in the search portlet, or in the underlying API as well?
Also, in the development part of the article your code refers to something called mainSearchContainer . What is it, and where'd you get it from? Thanks.
Posted on 3/24/12 12:45 PM.
Never mind about that second question, found it in main_search.jspf .
Posted on 3/24/12 3:50 PM in reply to Joseph Toman.
Hi Ray,

I'm new to Liferay and need your advise.

I've created a hook to a search portlet and have a couple of questions:
1. The start page is view.jsp and conains only <liferay-ui:search />. how would I bypass it so it would go directly to main_search.jsp?

2. In my Document library I have categories Monthly Reports and Quarterly Reports. I want to place two links(like facets) with category names and when user clicks it would process the search only for selected category. How would you suggest to approach this task?

Or maybe i need to create a custom search portlet based on a search portlet. Is it a good way to copy all the code from custom portlet into my portlet and modify it?

Thank you in advance
Posted on 3/26/12 9:16 AM in reply to Firas BD.
Hi Ray,

Thanks for the article. I'm trying to get a faceted search to work using an "AND" condition for the assetCategoryIds field, however it never returns any results. I enabled debug output on both the SearchEngineUtils and LuceneIndexSearcherImpl classes and the query string output is "+(+(companyId:10154) +(assetCategoryIdsemoticon11703 AND 11804)) +createDate:[19700101000000 TO 20121231235959] +((+(entryClassName:com.liferay.portlet.journal.model.JournalArticle) +(status:0))))".

To test the lucene syntax, I also used the code below to produce a Query object and it works fine. Do you have any ideas on what might be happening? Thanks in advance for the help.

Query query = StringQueryFactoryUtil.create("+(+(companyId:10154) +(assetCategoryIdsemoticon11703 AND 11804)) +createDate:[19700101000000 TO 20121231235959] +((+(entryClassName:com.liferay.portlet.journal.model.JournalArticle) +(status:0))))");

Hits hits = SearchEngineUtil.search(searchContext.getCompanyId(), query, QueryUtil.ALL_POS, QueryUtil.ALL_POS);
Posted on 4/9/12 9:09 AM.
Try

+(+assetCategoryIds:11703 +assetCategoryIds:11804)

it's easier on the parser and means the same thing.
Posted on 4/9/12 9:20 AM in reply to Jonathan Lloyd.
AND should be used more specifically like so:

assetCategoryIds:11703 AND assetCategoryIds:11804
Posted on 4/9/12 9:20 AM in reply to Ray Augé.
Hi Ray,

Thanks for the quick reply! I have used "+" instead of "AND", but still get the same result.

I'm not very familiar with lucene, but it seems like the problem is that based on the way I am setting the attribute values in the SearchContext object and have the facet defined, the objects that build the query string will only output the "assetCategoryIds" text just once. For example, the query string would look like +(assetCategoryIds:+11703 +11804) instead of +(+assetCategoryIds:11703 +assetCategoryIds:11804).

As noted above, I'm using the SearchContext object to set the attributes of the assetCategoryIds field, but I don't see a way to include multiple arguments into that one field with out concatenating them together myself. Something like the code below. I've also tried setting the andSearch property on the SearchContext with no success.

searchContext.setAttribute("assetCategoryIds", convertCategoryIdsToAndString(categoryIds));
Posted on 4/9/12 9:34 AM in reply to Ray Augé.
Ray, just found this post after looking into Search results and facets in more detail for our customization work.

I have one question though? can we get the facet tags and categories localized?(displayed in the facets/asset_tags.jsp as well as under relevant search item).
When you have multi-lingual site it looks odd if the categories are in the base locale for the site. The categories are translated when displayed within the relevant portlet (eg document library).
I understand these are now facets in a search engine result list but the user isnt going to understand that,
Posted on 4/27/12 1:41 PM.
Hi Ray,

I have a problem with create pre-configured to search only returns That tag "noticia"
The search returns all the content I type "com.liferay.portlet.journal.model.JournalArticle"

My code is this:

{facets: [
{className: 'com.liferay.portal.kernel.search.facet.AssetEntriesFacet',
data: {frequencyThreshold: 1,
values: [
'com.liferay.portlet.journal.model.JournalArticle'
]},
displayStyle: 'asset_entries',
fieldName: 'entryClassName',
label: 'asset-type',
order: 'OrderHitsDesc',
static: false,
weight: 1.5},
{className: 'com.liferay.portal.kernel.search.facet.MultiValueFacet',
data: {frequencyThreshold: 1,
values: [
'noticia'
]},
displayStyle: 'asset_entries',
fieldName: 'assetTagNames',
label: 'tag',
order: 'OrderHitsDesc',
static: false,
weight: 1.5},
{className: 'com.liferay.portal.kernel.search.facet.MultiValueFacet',
data: {displayStyle: 'list',
frequencyThreshold: 1,
maxTerms: 10,
showAssetCount: true},
displayStyle: 'asset_tags',
fieldName: 'assetTagNames',
label: 'tag',
order: 'OrderHitsDesc',
static: false,
weight: 1.4},
{className: 'com.liferay.portal.kernel.search.facet.MultiValueFacet',
data: {displayStyle: 'list',
frequencyThreshold: 1,
maxTerms: 10,
showAssetCount: true},
displayStyle: 'asset_tags',
fieldName: 'assetCategoryNames',
label: 'category',
order: 'OrderHitsDesc',
static: false,
weight: 1.3},
{className: 'com.liferay.portal.kernel.search.facet.RangeFacet',
data: {frequencyThreshold: 1,
ranges: [{label:'modified',
range:'[19700101000000 TO *]'}]},
displayStyle: 'modified',
fieldName: 'modified',
label: 'modified',
order: 'OrderHitsDesc',
static: false,
weight: 1.1}]}

Thank you very much.
Best regards.
Posted on 5/10/12 3:25 AM.
It is possible that Liferay can index content of PDF documents so it can show up in the search ?
Posted on 5/22/12 12:53 AM in reply to Salvador Baena.
@Salvador, make your "noticia" facet static since you are forcing passing a single value.

@Amit, Liferay does it's best attempt to index PDFs automatically (it can't get content from a PDF filled only with scanned images for instance).
Posted on 5/22/12 7:07 AM in reply to Amit Doshi.
Wow, incredible article! I would like to know what is the best way to let the user filter by asset type before hand, before perfoming the search. For example, the user could enter the word "water", and then, from a checkboxes list, choose the asset type to look into, for example, Blog entries. And that would search the word water in blog entries only.
Posted on 5/23/12 7:53 AM in reply to Ray Augé.
It's true that the search portlet doesn't start in the mode having performed a default search (a search with no keywords). If it did, then you would see all the facets available, and then you would see and could select the asset type you want to search within and then add keywords. I think that addresses the scenario you are asking about. Perhaps it would be possible to add that behavior is a configuration option of the portlet. It wouldn't be hard. Can you make a feature request in JIRA (http://issues.liferay.com)?
Posted on 5/23/12 8:21 AM in reply to Florencia Hernández.
Thanks Ray for the prompt reply. I just added a request: http://issues.liferay.com/browse/LPS-27514
Posted on 5/23/12 8:43 AM in reply to Ray Augé.
Thank you very much Ray. Solved.

Facet to filter by tag:

{facets: [
{className: 'com.liferay.portal.kernel.search.facet.AssetEntriesFacet',
data: {frequencyThreshold: 1,
values: [
'com.liferay.portlet.journal.model.JournalArticle'
]},
displayStyle: 'asset_entries',
fieldName: 'entryClassName',
label: 'asset-type',
order: 'OrderHitsDesc',
static: false,
weight: 1.5},
{className: 'com.liferay.portal.kernel.search.facet.MultiValueFacet',
data: {frequencyThreshold: 1,
values: [
'noticia'
]},
displayStyle: 'asset_entries',
fieldName: 'assetTagNames',
label: 'tag',
order: 'OrderHitsDesc',
static: true,
weight: 1.5}]}

Best regards
Posted on 5/23/12 10:00 AM in reply to Florencia Hernández.
Hi,

I would like to know how drill down can be allowed... Even I select a tag in result view, each tag is replaced when I choose another one.

Thanks for your answer emoticon
Posted on 5/25/12 2:42 AM.
Yeah, this was deferred to a later version. Technically the backend code can handle any number of arguments per facet, but in order to get a first cut, simple to use UI we opted to limit it to only a single argument per facet. BUT, since it's only a limitation from the UI, you can easily create custom facet view template (jsp hook) that overrides the default and allows multiple selection per facet.

Can you open a feature request so we can track this for future versions?
Posted on 5/25/12 12:35 PM in reply to ASI Register.
Hi Ray,
I would like to know what is the best way to add a custom facet implementation.
I am trying to implement drilled-down search, and I need to produce an AND query for MultiValueFacet.
Looking at source, I believe that this can't be done, because termQuery has booleanClause hardcoded (added request http://issues.liferay.com/browse/LPS-28228).

Thanks in advance
Posted on 6/28/12 8:44 AM in reply to Ray Augé.
Facets are for single fields only (both Bobo [our lucene facet impl] and Solr only have APIs for collecting facet on single fields at a time). But if you need to refine the underlying query so that it reflects some custom logic, then you can do that by passing an array of BooleanClauses to the searchContext before calling search in the FacetedSearcher.

SearchContext searchContext = .. // setup the context
Indexer indexer = FacetedSearcher.getInstance();
searchContext.setBooleanClauses(BooleanClause[] booleanClauses)
Hits hits = indexer.search(searchContext);

Note: An AND is simply a MUST "Occur" clause around a number of other Query instances (or Clauses).
Posted on 6/28/12 8:57 AM in reply to Matteo S.
BTW, the "Multi" in the name MultiValueFacet is not to reflect how it is used with respect to the number of fields to collect data from, but rather to indicate the "type" of field it can be used with. In this case fields with "multiple" values.

In indexing engines, fields generally fall into one of two broad classes:
- single value fields (like a number, fixed string token)
- multi-value fields (like text, or arrays of values)

There are certain types of operations that can take place on each of those two classes of fields. For instance, you can't do a Range query on a multi-value field. you generally can't sort a result set on a multi-value field, single value fields must generally be exact matches (setting aside regex matching obviously), etc.
Posted on 6/28/12 9:08 AM in reply to Ray Augé.
Hi Ray,
I have the requirements to pass multiple categories names while searching. But Faceted searcher is not giving the results for multiple categories.
For example while I pass assetCategoryNames field value as comm separated (say topic2,COUNTRY). The full query that is built in FacetedSearcher.java class looks like ----

"+(+(companyId:10154) +(assetCategoryNames:topic2 COUNTRY) +((+(entryClassName:com.liferay.portlet.bookmarks.model.BookmarksEntry)) (+(entryClassName:com.liferay.portlet.blogs.model.BlogsEntry)) (+(entryClassName:com.liferay.portlet.calendar.model.CalEvent)) (+(entryClassName:com.liferay.portlet.documentlibrary.model.DLFileEntry) +(status:0)) ..........".

Above if you see the part of query---" (assetCategoryNames:topic2 COUNTRY) ", you will notice that category names are coming but there is no boolean operator added by the system. I have gone through all the above threads, I found ur comments but could not get it completely as what needs to be done to select multiple categories at one time while searching.
If any booleanClause need to be set in serachContext then how and what it should be .

Please let me know the solution if you are aware of this scenario.

Thanks in advance,
Himanshu Modi
Posted on 6/29/12 3:27 AM in reply to Ray Augé.
Typo--(assetCategoryNames:topic2 assetCategoryNames:COUNTRY) is the correct query I'm getting.
Posted on 6/29/12 4:24 AM in reply to Himanshu Modi.
The correct way to do that would be by passing a set of additional BooleanClauses to the searchContext.setBooleanClauses(BooleanClause[] clauses) method just before making the search. Facets are not designed for doing filtering only for collecting metrics.
Posted on 7/20/12 6:16 AM in reply to Himanshu Modi.
Hi Ray,
is there any way to integrate a custom portlet into the asset entries facet ?
My portlet has it's custom Indexer that is registered via liferay-portlet.xml and I added my model in the search-portlet's configuration. If I add an OpenSearch implementation the results are displayed at the bottom of the search, but I can't get it to work with the faceted search. What am I missing?
Posted on 8/16/12 1:16 AM in reply to Ray Augé.
I figured it it out, my Indexer extends BaseIndexer but somehow not all keywords are set for the facet query to match my documents. I was missing fields "COMPANY_ID" and "GROUP_ID" that are only set in BaseIndexer if your model is an instance of "AuditedModel" which apparently my model class is not.
Posted on 8/16/12 1:45 AM in reply to Kim A Kunc.
Ok, great! I'm glad you managed to figure it out. Yes, AuditedModel is a helper interface around entities designed to support multi-tenancy.

AuditedModel interface will be automatically added to your Model when the entity definition contains the fields: companyId, createDate, modifiedDate, userId, userName.

Similarly, the GroupedModel interface provides support for scoping models to groups (a.k.a. Sites) and is applied if the entity is an AuditedModel + has the groupId field.

There are several other automatically applied interfaces derived from entity columns, like workflow, attached, resourced, etc.

Apparently this is a subject still in need of documentation.
Posted on 8/16/12 7:40 AM in reply to Kim A Kunc.
Ray, thanks for your reply!
What would think of a new feature in the service builder definition that would automatically create these audit fields i.e. a new property "auditedModel=true/false" ?
Posted on 8/28/12 12:57 AM in reply to Ray Augé.
That would be cool (JIRA feature request?).
Posted on 8/29/12 9:41 AM in reply to Kim A Kunc.
Hi Ray,

A really useful article. Can you tell me if the same functionalities are available through web services or JSON requests (via portal-client.jar) ?

Thanks,
Denis.
Posted on 9/11/12 4:30 AM in reply to Ray Augé.
Unfortunately not at the moment (JIRA feature request?).
Posted on 9/11/12 6:25 AM in reply to Denis Signoretto.
Hi Ray,
I do understand your point about facet's design. However, as others have pointed out, there's a strong use case regarding drilled down search.
As of 6.1GA2, I don't see any possible solution which can be developed by using a jsp hook. We can't use multiple facets on the same term, and current implementation has BooleanClauseOccur.SHOULD hardcoded.
MultiValueFacet (btw thanks for explaining its name) does handle multiple terms, so why you say that ain't the right place to edit the query?

Look at @Himanshu case as example:
- requirement: filter search with multiple terms on the same element
- terms:topic2,COUNTRY
- current implementation produces (assetCategoryNames:topic2 assetCategoryNames:COUNTRY): result is OR between these clauses
- my proposal http://issues.liferay.com/browse/LPS-28228: let admin decide facet clause
example result with (AND -BooleanClauseOccur.MUST ) -> (assetCategoryNames:topic2 AND assetCategoryNames:COUNTRY)

I don't want to use a single facet to search on multiple fields, instead I need the option to produce and AND booleanQuery.
I'm already using this approach on a couple of projects, because of deadlines, but I'm more than open to suggestions on a better way to implement this. Your explanations are really valuable.

Thanks for your response, and sorry for my English..
Matteo
Posted on 9/12/12 8:50 AM in reply to Ray Augé.
Thanks for this detailed blog about Faceted Search API.
I am having an issue. The search is returning articles with old articleID.

I have a portlet to add articles with articleID set to article title, later I change the portlet to delete all articles and add articles with articleID generated by Liferay.

Problem happened, the data returned is the old article created before. The article is still using title as articleID instead of system generated ID.

I tried to clear cache from Server Administration and tried to restart server several times and still the Faceted Search API returns old article. I checked the database and it is not there.

Where is the data cached?
Posted on 10/6/12 9:33 AM.
The data is not cached so much as it may be that the indexes are out of sync with the real data (in the Demoticon. Try reindexing the search engines (you can do that all at once via the Admin portlet, or individually by portlet through the plugins configurations portlet).
Posted on 10/16/12 1:52 PM in reply to Gwowen Fu.
@Matteo, As I stated previously, you can use the searchContext.setBooleanClauses(BooleanClause[] clauses) method to add more filtering. Such clauses can implement whatever complex logic you wish to add without causing the facet collector to process for that data (which if all you want to do is filter, is really the wrong mechanism).
Posted on 10/16/12 2:01 PM in reply to Ray Augé.
Hi Ray, Thank you for very informative and helpful blog.

I would like to add something more in detail here:
We sometime keep data in web content structure fields, and custom fields. Following are naming pattern of these fields:

Structure fields can be referred as:
"web_content/structure-field-name"
Here structure-field-name name of field in webcontent structure.

Custom fields can be referred as:
"expando/custom_fields/custom_field_key"
Here custom_field_key is key of custom field for entity.

These can be used in facet configuration with additional display style (via hook).
Posted on 10/24/12 11:20 PM in reply to Ray Augé.
@Yogesh, that is correct! Thanks for pointing that out. We wanted to make sure any field in the index was accessible for facet collection, including custom fields, including document type fields which will be encoded a little differently as "ddm/<ddmStructureId>/<fieldName>". Sadly, it's true that the use of <ddmStructureId> is not ideal and there has already been discussion to perhaps provide an alternative that is more "usable" in future versions.
Posted on 10/29/12 8:39 AM in reply to Yogesh Sharma.
Hi Ray,

Is there a way to exclude certain fields? Here it only gives option to specify fields and values that are to be searched. What about the scenario : search all the facets except for one facet. Or search all except fieldX with valueY? I suppose this is only possible for now with PostProcessorHook?
Posted on 11/21/12 4:50 AM in reply to Ray Augé.
A facet by it's very nature can only search one field.. so I'm not clear on that question.

It's also possible to apply an array of QueryClauses on the SearchContext to filter the reqults. This is how I would implement the: "Or search all except fieldX with valueY?" req. But that would require a hook at the moment.
Posted on 11/21/12 8:01 AM in reply to Bart Simpson.
Hi Ray, Thank you for very informative blog.

Question:
suppose that there are two categories / tags with same name in Global group and current group; how could we distinguish them in facets by names? is it better to use category ID / tag ID?
Posted on 11/21/12 9:00 AM in reply to Ray Augé.
Jonas, categoryId is better in this case.
Posted on 11/21/12 9:03 AM in reply to Jonas Yuan.
Hi Ray, I'm new to Liferay

in my custom theme I put search portlet at runtime with the following code:

$velocityPortletPreferences.setValue("portlet-setup-show-borders","false")
$velocityPortletPreferences.setValue("advancedConfiguration","true")
$velocityPortletPreferences.setValue("searchConfiguration","'facets':[{'displayS­tyle':'asset_entries','weight':1.5,'s[......]")
$theme.runtime("3_INSTANCE_kw01","",$velocityPortletPreferences.toString())
$velocityPortletPreferences.reset()

where in "searchConfiguration", I insert a new line to make sure that the portlet can search a custom entity .

But when in the portal I push the search command, these configurations are not observed.
Posted on 12/14/12 9:30 AM in reply to Ray Augé.
Ok, I think I see the problem. First, the search portlet is not instanceable, which means you can remove the "_INSTANCE_kw01" portion of the portletId. Secondly, because the portlet is not instanceable, you have to use a different technique to set it's preferences. See this gist https://gist.github.com/4287391 (there are actually 2 different preferences, the preferences, and the setup). ;)
Posted on 12/14/12 10:19 AM in reply to Matteo Galletti.
Hi Ray, This info. is really good. i have some requirement but i don't know how to do this. My requirement is "suppose with search portlet there is 3 radio button 1st company tag, 2nd is for message board and 3rd for site. after selecting any radio button where and which value i need set or what code modification i need to do."

Thank you in Advance
Posted on 1/7/13 4:11 AM in reply to Ray Augé.
Ray,
Is putting the default search configuration in a custom theme (in the link you posted the portal_normal.vm file) the preferred way to change the default search configuration for all search portlets? I couldn't get it to work and was wondering if there's a better way. I'm also trying to add a custom entity to the advanced search configuration for all search portlets in my app.
Thanks.
Posted on 1/29/13 12:50 PM in reply to Ray Augé.
Hi Ray,
I've implemented a custom Facet and registered a JSP in the hook for it. After registering the facet in the Search Portlet I receive a ClassNotFoundException.
How would you get a custom Facet class on the portal classpath? Should I repackage it and drop it in the Liferay classpath? The class is now contained within the hook project.
Thanks
Posted on 2/21/13 4:49 AM.
I the case of Facet class implementations, we don't currently have a real "hook" mechanism for those, sorry. You'll have to do as you suggested and drop that code into the portal classpath (ROOT/WEB-INF/lib/your.jar or ROOT/WEB-INF/classes/your.class).

(Alternatively you could use an EXT plugin emoticon sorry about that).
Posted on 2/22/13 7:44 AM in reply to Gunther Verhemeldonck.
We have a problem with the facet search. If we perform a search and drill-down on a Category, it seems the search doesn't take into account the search string. Instead of receiving less hits, we receive MORE hits that are not related to the search term we entered.

If we trace, the query does seem to take into account both the search query as well as the Category we clicked to drill-down.
In the traces we also noticed that the BaseIndexer prints out the Search engine ID for each facet, but in our case this ID is always empty.

Does anybody have an idea what can be the root cause, knowing that search is out-of-the-box and we have all known EE fixes installed.

Thanks in advance.
Posted on 2/26/13 5:13 AM in reply to Ray Augé.
Sorry to hear that. Can you enable showing the query (in the portlet configuration) and then list the query without and then with the category selection?
Posted on 2/26/13 6:36 AM in reply to Gunther Verhemeldonck.
We believe it might be a bug. The categoryTitle is added to the Lucene Query in the search term related part of the query, so as a result the it finds all items related to the category not taking into account the entered search term (since it is OR).

Query 1:
+(+(companyId:10154) +
(
(+(entryClassName:com.liferay.portlet.bookmarks.model.BookmarksEntry))
(+(entryClassName:com.liferay.portlet.blogs.model.BlogsEntry))
(+(entryClas­sName:com.liferay.portlet.documentlibrary.model.DLFileEntry) +(status:0))
(+(entryClassName:com.liferay.portlet.journal.model.JournalArticl­e) +(status:0))
) +
(+(groupId:10405) +(scopeGroupId:10405))
) +
(assetCategoryTitles:*test* assetCategoryTitles_nl_BE:*test* assetTagNames:*test* ....


Query 2 (drill-down on 'Enterprise'-category):

+(+(companyId:10154) +(assetCategoryTitles:Enterprise) +
(
(+(entryClassName:com.liferay.portlet.bookmarks.model.BookmarksEntry))
­ (+(entryClassName:com.liferay.portlet.blogs.model.BlogsEntry))
(+(entryClassN­ame:com.liferay.portlet.documentlibrary.model.DLFileEntry) +(status:0))
(+(entryClassName:com.liferay.portlet.journal.model.JournalArticl­e) +(status:0))
) +
(+(groupId:10405) +(scopeGroupId:10405))
) +
(assetCategoryTitles:*Enterprise* assetCategoryTitles_nl_BE:*test* assetTagNames:*test*........

Thank you in advance.
Posted on 2/27/13 1:06 AM in reply to Ray Augé.
Hello Ray,
Nice explanation. In CE 6.1, searching with "test_tag" among TAGS shows me no results and if i search with either "test" or "tag", no results are shown whereas single word tags are searched. After being through the search query, I found keywords with special characters are escaped and are searched as multiple keywords. The same search is working perfectly in 6.1EE.
Regards, Dileep.
Posted on 3/1/13 8:01 AM.
Confirmed as a bug.
Posted on 3/21/13 8:32 AM in reply to Gunther Verhemeldonck.
Hi,
it seems that with 6.1.1 CE boolean operators stopped working.
With 6.1.0 following statements worked as expected:
my* NOT admin@mysite.com NOT service@mysite.com
Both users wasn't returned in the result set 6.1.0, but will be part of the result set in version 6.1.1.
Could anyone confirm this behaviour?

Best regards
Niklas
Posted on 4/8/13 4:17 AM.
I have a problem with the fuzzy search.
I have implemented an indexer to index all the AssetTag.

the query is:
+(name:rembours~) +(entryClassName:com.liferay.portlet.asset.model.AssetTag)
I used the BooleanQueryFactoryUtil to create my BooleanQuery.

To test the lucene query, I also used luke and it works fine, it return 2 results.
But with liferay, the same query (and the same file index) return 0 results.
I used searchEngineUtil.search.
searchEngineUtil.search(searchContext.getSearchEngineId(­), PortalUtil.getDefaultCompanyId(), contextQuery, sorts, 0, nbrTag);
Posted on 4/26/13 7:24 AM.
AssetTags are not indexed! your entryClassName value is not one which is ever indexed.
Posted on 4/26/13 7:27 AM in reply to haikel thamri.
I have implemented a custom indexer in my custom Portlet, it index All AssetTags, I have tested it, it works fine and I tested my query with lucene!
Posted on 4/26/13 7:33 AM in reply to Ray Augé.
Ok, can you give me some examples of indexed values that should be "near" enough to match?
Posted on 4/26/13 7:35 AM in reply to haikel thamri.
Hi Ray,

I have implemented a custom indexer in my custom Portlet, it index All AssetTags, I have tested it, it works fine and I tested my query with lucene!

I have Implemented this method in my indexer
@Override
protected Document doGetDocument(Object obj) throws Exception {
AssetTag tag = (AssetTag) obj;

Document document = getBaseModelDocument(PORTLET_ID, tag);

document.addKeyword(Field.NAME, tag.getName());
document.addKeyword("tagId", tag.getTagId());

return document;
}
Posted on 4/26/13 7:37 AM in reply to Ray Augé.
Sure, I got that part. Can you give me some examples of indexed values that "should" match the fuzzy term query above?
Posted on 4/26/13 7:40 AM in reply to haikel thamri.
With luke
this query for example
+(name:routar*) +(entryClassName:com.liferay.portlet.asset.model.AssetTag)
return the AssetTag whose the name is "routard"
Posted on 4/26/13 7:42 AM in reply to Ray Augé.
excuse me the query is
+(name:routar~) +(entryClassName:com.liferay.portlet.asset.model.AssetTag)
Posted on 4/26/13 7:42 AM in reply to haikel thamri.
excuse me the query is
+(name:routar~) +(entryClassName:com.liferay.portlet.asset.model.AssetTag)
Posted on 4/26/13 7:42 AM in reply to haikel thamri.
Ok, as soon as I get a chance will play with this to see where the issue might be.

In fact, if you have a ticket for this issue can you give me the ID for it? If there isn't one, can you create one with all the details here? (including the content and query example?

Thank you
Posted on 4/26/13 7:45 AM in reply to haikel thamri.
Done!
LPS-34774
Thank you emoticon
Posted on 4/26/13 8:13 AM in reply to Ray Augé.
Hi Ray,

Great post!! I'm having a few issues though... I can't seem to filter by categoryIds when I do a keyword search. It looks like this guy is having the same issue:

http://www.liferay.com/community/forums/-/message_boards/message/24311870

a­ny ideas?

Thanks in advance.

Brian
Posted on 5/2/13 2:23 PM in reply to haikel thamri.
Hi,

Any progress on this. I have modified liferay's default Blog portlet via EXT to meet up my requirement.
In control panel when I click on Blogs portlet and try to search for a Blog with say "test" then I can see the list of Blogs with proper pagination.
For example , for 44 entries of corresponding Blogs I can see 3 pages i.e 4 links:
1 2 3 Next
But when I follow same step for my customized Blog portlet then for same results I can see 5 links:
1 2 3 4 Next
Now when I click on 4, no display appears with backend error saying :
java.lang.IllegalArgumentException: fromIndex(60) > toIndex(44)

Can anybody help me to find the exact cause. emoticon

Thanks
Posted on 6/3/13 12:22 AM in reply to Firas BD.
Hi,

Any progress on this. I have modified liferay's default Blog portlet via EXT to meet up my requirement.
In control panel when I click on Blogs portlet and try to search for a Blog with say "test" then I can see the list of Blogs with proper pagination.
For example , for 44 entries of corresponding Blogs I can see 3 pages i.e 4 links:
1 2 3 Next
But when I follow same step for my customized Blog portlet then for same results I can see 5 links:
1 2 3 4 Next
Now when I click on 4, no display appears with backend error saying :
java.lang.IllegalArgumentException: fromIndex(60) > toIndex(44)

Can anybody help me to find the exact cause. emoticon

Thanks
Posted on 6/3/13 12:22 AM in reply to Firas BD.
I am using sorting by a field in Webcontent structure which is multi world but the results get sorted by single terms and not the whole word
Posted on 6/12/13 10:17 AM.
for anyone interested...This has been confirmed to be a bug in 6.1 GA2
Posted on 6/12/13 11:29 AM in reply to Brian Scott Schupbach.
by "this" I mean "I can't seem to filter by categoryIds when I do a keyword search."
Posted on 6/12/13 11:30 AM in reply to Brian Scott Schupbach.
Hello Ray,
Thanks for the great post.
I' m trying with the "Image Search Example" in your post but I don't get the image-only result. Any clues?

thanks
Posted on 6/14/13 8:28 AM.
Is it possible to configure the faceted Search so that the user can find both journal-articles and only PDF-files from Documents and Media Library?
Posted on 9/4/13 4:21 AM.
yes it can be done by selecting only these assests in search portlet's configuration
Posted on 9/4/13 10:35 AM in reply to Bert Godon.
Please see https://www.liferay.com/web/raymond.auge/blog/-/blogs/faceted-search-and-customi­zed-filtering for how advanced filtering should be done!

HTH emoticon
Posted on 9/4/13 11:29 AM in reply to Bert Godon.
H Ray,
I tried to add "com.liferay.portlet.polls.model.PollsQuestion" in Advanced Search Configuration, but my Polls are not returned in the search results.
Can you please show in an example how to do it for Polls ?

Thanks a lot.
Posted on 10/24/13 1:22 PM.
It must be an entity which is indexed.
Posted on 10/24/13 1:24 PM in reply to Nabil Bahtat.
Hi Ray,
How to make Polls portlet an indexed entity ?
Sorry for this question, I'm new to Liferay.

Thanks alot for your help.
Nabil
Posted on 10/28/13 11:21 PM in reply to Ray Augé.
Minimally, you need to create and register an Indexer impl responsible for indexing and handling the indexed documents. Then you need to create a ServiceWrapper on the pols service which extends that service to index the polls when they are added/updated (or to retroactively index existing polls).

I won't lie, this is a fairly advanced (set of) task(s).
Posted on 10/29/13 9:23 AM in reply to Nabil Bahtat.
I have a question about how the search is secured. I set up a test case as follows for a particular user:
--- Folder (no access)
------ Document (has access)

I confirmed that the user was unable to find the document in a search, but was able to see that a document existed with a facet count of 1.

Is this because two fundamentally different queries take place? The BaseIndexer.getFacetedSearch() method looks to be using the SearchPermissionChecker directly in the query, while it appears that the "regular" search is checking the permissions one-by-one by iterating over the hits in the BaseIndexer.filterSearch() method.

We are looking to implement our own search functionality, and are considering storing the View role IDs within the index itself to improve the search performance. Do you see any issues with this that we might want to be aware of, or are there specific "gotchas" that lead to the decision to check each document's permissions after the hits were found? Is this somehow less secure, and thus acceptable to display a facet hit count, but not to enforce overall document security?
Posted on 12/2/13 3:14 PM.
Hi Ray,
I managed to do all the tasks about indexer you explained in your answer.
The problem is I don't know how to make my code availlable in Liferay.
What kind of Liferay project should I create (hook, ext plugin ...) ?

Thanks alot.
Posted on 1/12/14 11:24 AM in reply to Ray Augé.
Is it possible to highlight the searchresult words with the components are related to searchContext or facets?
Posted on 3/13/14 7:00 AM.
Hi Ray ,
we are passing facets as json . but range facet is not . even though date is passed as per the required syntax .
Can you please help on this :


stringBundler
.append("{"
+ "static: false, "
+ "order: 'OrderHitsDesc', "
+ "data: { "
+ "frequencyThreshold: 0, "
+ "ranges: [ {label:'modified', range:'[20140315000000 TO *]'}"
+ " ] "
+ "}, "
+ "className: 'com.liferay.portal.kernel.search.facet.RangeFacet', "
+ "fieldName: 'modified', " + "label: 'modified', " + "}");

stringBundler.append("]}");
Posted on 3/26/14 3:14 AM in reply to lili samanta.
Hi Ray,
Could you please expand on the "paginationType" attribute for SearchContext, and what its possible values are/mean (seriously, what does "more" mean?)
Posted on 4/15/14 2:36 PM.
Hi Ray,

We have requirement for search only users and in search result we need have select box to filter users on selected country.
Can you give any idea as I am new in liferay??

regards,
Nitin
Posted on 5/19/14 3:14 AM.
Good Morning.

I need to make through faceted search, search Journal Articles with an specific structureId. I am trying as follows, but it does not quite work. Can you help me out?


{"facets": [
{
"displayStyle": "scopes",
"weight": 1.7,
"static": false,
"order": "OrderHitsDesc",
"data": {
"maxTerms": 10,
"frequencyThreshold": 1,
"showAssetCount": true
},
"label": "site",
"className": "com.liferay.portal.kernel.search.facet.ScopeFacet",
"fieldName": "groupId"
},
{
"displayStyle": "asset_entries",
"weight": 1.5,
"static": true,
"order": "OrderHitsDesc",
"data": {
"values": ["com.liferay.portlet.journal.model.JournalArticle"],
"frequencyThreshold": 0
},
"label": "asset-type",
"className": "com.liferay.portal.kernel.search.facet.AssetEntriesFacet",
"fieldName": "entryClassName"
},
{
"displayStyle": "asset_entries",
"weight": 1.5,
"static": true,
"order": "OrderHitsDesc",
"data": {
"values": ["36707"],
"frequencyThreshold": 0
},
"label": "asset-type",
"className": "com.liferay.portal.kernel.search.facet.MultiValueFacet",
"fieldName": "structureId"
}
]}



Thank you very much.
A greeting.
Posted on 10/28/14 1:10 AM.
Hi Ray,
I have installed liferay 6.2 GA2 version bundled eith Tomcat and I'm trying to configure the Search portlet to be able to find Calendar Events.

I'm trying to find <indexer-class>com.liferay.portlet.calendar.util.CalIndexer</indexer-class> line inside liferay-portlet.xml within the sorce code of my portal but I haven't found it.

In the portal source code version I can see that class inside com.liferay.portlet.calendar.util package.

Could anybody give me a hand?

Has anybody faced this problem?

Thanks!!
Posted on 3/20/15 6:38 AM in reply to Miguel González.
Because I believe what you are looking for is

https://github.com/liferay/liferay-plugins/blob/master/portlets/calendar-portlet­/docroot/WEB-INF/liferay-portlet.xml#L10

In 6.2 the calendar was heavily changed and mostly became a plugin.
Posted on 3/21/15 1:10 PM in reply to Alba Garcia.
Thanks Ray!

I have imported the calendar-portlet from the plugin-sdk "liferay-plugins" on GitHub but I'm facing this issue related to Ivy:

"The ivy file '/home/usuario/github/liferay-plugins/portlets/calendar-portlet/ivy.xml' could not be parsed: Problem occurred while parsing ivy file: Unable to parse included ivy file for com.liferay#com.liferay.sdk;latest.integration in file:/home/usuario/github/liferay-plugins/portlets/calendar-portlet/ivy.xml"

Do you know what could be happening?

I'm not sure but Do you think that I could use the liferay-portlet.xml with the 6.2 GA2 calendar portlet version?

Thanks in advance!
Posted on 3/29/15 11:32 PM in reply to Ray Augé.
Hi Ray,

In Liferay 7 M7, I found following shippet:
search.asset.type=com.liferay.portlet.blogs.model.BlogsEntry

OSGI-specifi­c. How to do it with classic portlets? Please see discussion at https://dev.liferay.com/web/liferay-7-community-expedition/feedback/-/message_bo­ards/message/560858

Thank you!
Posted on 8/24/15 1:15 PM.