Issue
I am trying to execute a search query to get the DDM fields stored in Elasticsearch of web content but they are not being returned.
The same code was working in DXP 7.0, 7.1, and 7.2, returning the DDM fields as field attributes with this name pattern:
ddm__keyword__99999__myfield__en_US
ddm__text__99999__myfield__en_US
How can I adapt my code to get the DDM fields in DXP 7.3 and 7.4?
Environment
- DXP 7.3
- DXP 7.4
Resolution
After the LPS-103224 bug fix changes, the DDM fields are now indexed as a nested field named "ddmFieldArray", for more information about this change, see DXP 7.3 breaking changes.
By default, Elasticsearch doesn't return these nested fields in the query results.
You have to fetch it from the document source field that is stored in an internal "_source" field in Elasticsearch (see https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-fields.html ) including the source: true
flag so that Elasticsearch returns the results with that fields.
This flag is added to the searchContent invoking the fetchSource(true) method of SearchRequestBuilder.
- Example:
searchRequestBuilderFactory.builder(
searchContext
).fetchSource(
true
).build();
- After executing the search, you have to get the Document objects from the SearchResponse object. This SearchResponse is available in the searchContext using this code:
/* Execute search */
IndexSearcherHelperUtil.search(searchContext, query);
/* Get results from search response */
SearchResponse searchResponse = searchContext.getAttribute("search.response");
List resultHits = searchResponse.getSearchHits().getSearchHits();
/* Iterate */
for (SearchHit searchHit : resultHits) {
Document doc = searchHit.getDocument();
...your stuff...
}
You have some examples about fetching source and gettings SearchResponse in the following Liferay classes:
- com.liferay.document.library.search.test.DLSearchFixture, see method searchOnlyOneSearchHit where fetchSource(true) is set and the SearchResponse object is got from the searchContext
- com.liferay.account.internal.retriever.AccountOrganizationRetrieverImpl where some searches are done using SearchRequest and SearchResponse.
- com.liferay.asset.internal.util.AssetHelperImpl where searchRequestBuilderFactory is used.
Here there is a groovy script example that you can execute from Control Panel => Server Administration => Script
- Code for DXP 7.3
import com.liferay.registry.*;
import com.liferay.portal.kernel.search.*;
import com.liferay.portal.kernel.search.generic.*;
import com.liferay.portal.search.legacy.searcher.SearchRequestBuilderFactory;
import com.liferay.portal.search.searcher.SearchResponse;
import com.liferay.portal.search.hits.SearchHit;
import com.liferay.portal.search.document.Document;
/* Get SearchRequestBuilderFactory reference using RegistryUtil, because we cannot use "@Reference" in a groovy script */
Registry registry = RegistryUtil.getRegistry();
SearchRequestBuilderFactory searchRequestBuilderFactory = registry.getService(registry.getServiceReference(SearchRequestBuilderFactory.class.getName()));
/* Create SearchContext */
SearchContext searchContext = new SearchContext();
searchContext.setCompanyId(com.liferay.portal.kernel.util.PortalUtil.getCompany(actionRequest).getCompanyId());
searchContext.setStart(-1);
searchContext.setEnd(-1);
/* Line to fetch stored source of documents */
searchRequestBuilderFactory.builder(
searchContext
).fetchSource(
true
).build();
/* Get journal articles that are approved (status = 0) */
MatchQuery approvedQuery = new MatchQuery(Field.STATUS, String.valueOf(0));
MatchQuery journalArticleQuery = new MatchQuery("entryClassName", com.liferay.journal.model.JournalArticle.class.getName());
BooleanQuery query = new BooleanQueryImpl();
query.add(approvedQuery, BooleanClauseOccur.MUST.getName());
query.add(journalArticleQuery, BooleanClauseOccur.MUST.getName());
/* Execute search */
IndexSearcherHelperUtil.search(searchContext, query);
/* Get results from search response */
SearchResponse searchResponse = searchContext.getAttribute("search.response");
List resultHits = searchResponse.getSearchHits().getSearchHits();
/* Iterate */
for (SearchHit searchHit : resultHits) {
Document doc = searchHit.getDocument();
out.println("entryClassPK: " + doc.getValue("entryClassPK"));
out.println("ddmFieldArray: " + doc.getValues("ddmFieldArray"));
out.println("");
}
- Code for DXP 7.4
import com.liferay.portal.kernel.search.*;
import com.liferay.portal.kernel.search.generic.*;
import com.liferay.portal.search.legacy.searcher.SearchRequestBuilderFactory;
import com.liferay.portal.search.searcher.SearchResponse;
import com.liferay.portal.search.hits.SearchHit;
import com.liferay.portal.search.document.Document;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import com.liferay.portal.kernel.module.util.SystemBundleUtil;
/* Get SearchRequestBuilderFactory reference using the BundleContext, because we cannot use "@Reference" in a groovy script */
BundleContext bundleContext = SystemBundleUtil.getBundleContext();
SearchRequestBuilderFactory searchRequestBuilderFactory = bundleContext.getService(bundleContext.getServiceReferences(SearchRequestBuilderFactory.class.getName(), null)[0]);
/* Create SearchContext */
SearchContext searchContext = new SearchContext();
searchContext.setCompanyId(com.liferay.portal.kernel.util.PortalUtil.getCompany(actionRequest).getCompanyId());
searchContext.setStart(-1);
searchContext.setEnd(-1);
/* Line to fetch stored source of documents */
searchRequestBuilderFactory.builder(
searchContext
).fetchSource(
true
).build();
/* Get journal articles that are approved (status = 0) */
MatchQuery approvedQuery = new MatchQuery(Field.STATUS, String.valueOf(0));
MatchQuery journalArticleQuery = new MatchQuery("entryClassName", com.liferay.journal.model.JournalArticle.class.getName());
BooleanQuery query = new BooleanQueryImpl();
query.add(approvedQuery, BooleanClauseOccur.MUST.getName());
query.add(journalArticleQuery, BooleanClauseOccur.MUST.getName());
/* Execute search */
IndexSearcherHelperUtil.search(searchContext, query);
/* Get results from search response */
SearchResponse searchResponse = searchContext.getAttribute("search.response");
List resultHits = searchResponse.getSearchHits().getSearchHits();
/* Iterate */
for (SearchHit searchHit : resultHits) {
Document doc = searchHit.getDocument();
out.println("entryClassPK: " + doc.getValue("entryClassPK"));
out.println("ddmFieldArray: " + doc.getValues("ddmFieldArray"));
out.println("");
}
In your code, you should replace the RegistryUtil or BundleContext usage with the "@Reference" annotation.
"Entity content is too long" error executing the script
If you get an "entity content is too long" error executing the script:
2023-03-07 11:53:04.136 ERROR [http-nio-8080-exec-72][ElasticsearchIndexSearcher:164] null
java.lang.RuntimeException: java.io.IOException: entity content is too long [155989434] for the configured buffer limit [104857600]
at com.liferay.portal.search.elasticsearch7.internal.search.engine.adapter.search.SearchSearchRequestExecutorImpl.getSearchResponse(SearchSearchRequestExecutorImpl.java:99) ~[?:?]
{...}
Caused by: org.apache.http.ContentTooLongException: entity content is too long [155989434] for the configured buffer limit [104857600]
This is caused because the example query returns too many articles for a single request.
See article: Entity content is too long for the configured buffer limit
To avoid this error, change the example query to return fewer articles
Additional Information