/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.dynamic.data.lists.search.test; import com.liferay.arquillian.extension.junit.bridge.junit.Arquillian; import com.liferay.dynamic.data.lists.helper.DDLRecordSetTestHelper; import com.liferay.dynamic.data.lists.helper.DDLRecordTestHelper; import com.liferay.dynamic.data.lists.model.DDLRecordSet; import com.liferay.dynamic.data.lists.service.DDLRecordLocalServiceUtil; import com.liferay.dynamic.data.mapping.model.DDMForm; import com.liferay.dynamic.data.mapping.model.DDMFormField; import com.liferay.dynamic.data.mapping.model.DDMStructure; import com.liferay.dynamic.data.mapping.model.LocalizedValue; import com.liferay.dynamic.data.mapping.model.Value; import com.liferay.dynamic.data.mapping.storage.DDMFormFieldValue; import com.liferay.dynamic.data.mapping.storage.DDMFormValues; import com.liferay.dynamic.data.mapping.storage.StorageType; import com.liferay.dynamic.data.mapping.test.util.DDMFormTestUtil; import com.liferay.dynamic.data.mapping.test.util.DDMFormValuesTestUtil; import com.liferay.dynamic.data.mapping.test.util.DDMStructureTestHelper; import com.liferay.portal.kernel.model.Group; import com.liferay.portal.kernel.model.GroupConstants; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.search.Hits; import com.liferay.portal.kernel.search.SearchContext; import com.liferay.portal.kernel.search.SearchEngine; import com.liferay.portal.kernel.search.SearchEngineHelperUtil; import com.liferay.portal.kernel.security.auth.PrincipalThreadLocal; import com.liferay.portal.kernel.security.permission.PermissionChecker; import com.liferay.portal.kernel.security.permission.PermissionThreadLocal; import com.liferay.portal.kernel.service.UserLocalServiceUtil; import com.liferay.portal.kernel.test.rule.AggregateTestRule; import com.liferay.portal.kernel.test.rule.DeleteAfterTestRun; import com.liferay.portal.kernel.test.rule.Sync; import com.liferay.portal.kernel.test.rule.SynchronousDestinationTestRule; import com.liferay.portal.kernel.test.util.GroupTestUtil; import com.liferay.portal.kernel.test.util.RandomTestUtil; import com.liferay.portal.kernel.test.util.SearchContextTestUtil; import com.liferay.portal.kernel.test.util.TestPropsValues; import com.liferay.portal.kernel.test.util.UserTestUtil; import com.liferay.portal.kernel.util.LocaleUtil; import com.liferay.portal.kernel.util.PortalUtil; import com.liferay.portal.kernel.workflow.WorkflowConstants; import com.liferay.portal.security.permission.SimplePermissionChecker; import com.liferay.portal.test.rule.LiferayIntegrationTestRule; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Set; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; /** * @author Marcellus Tavares * @author André de Oliveira */ @RunWith(Arquillian.class) @Sync public class DDLRecordSearchTest { @ClassRule @Rule public static final AggregateTestRule aggregateTestRule = new AggregateTestRule( new LiferayIntegrationTestRule(), SynchronousDestinationTestRule.INSTANCE); @Before public void setUp() throws Exception { setUpPermissionThreadLocal(); setUpPrincipalThreadLocal(); _group = GroupTestUtil.addGroup(); _user = UserTestUtil.addUser(); DDLRecordSet recordSet = addRecordSet(); _recordTestHelper = new DDLRecordTestHelper(_group, recordSet); _searchContext = getSearchContext(_group, _user, recordSet); } @After public void tearDown() { PermissionThreadLocal.setPermissionChecker(_originalPermissionChecker); PrincipalThreadLocal.setName(_originalName); } @Test public void testBasicSearchWithDefaultUser() throws Exception { long companyId = TestPropsValues.getCompanyId(); User user = UserLocalServiceUtil.getDefaultUser(companyId); Group group = GroupTestUtil.addGroup( companyId, user.getUserId(), GroupConstants.DEFAULT_PARENT_GROUP_ID); DDLRecordSetTestHelper recordSetTestHelper = new DDLRecordSetTestHelper( group); DDMStructureTestHelper ddmStructureTestHelper = new DDMStructureTestHelper( PortalUtil.getClassNameId(DDLRecordSet.class), group); DDMStructure ddmStructure = ddmStructureTestHelper.addStructure( createDDMForm(LocaleUtil.US), StorageType.JSON.toString()); DDLRecordSet recordSet = recordSetTestHelper.addRecordSet(ddmStructure); SearchContext searchContext = getSearchContext(group, user, recordSet); DDLRecordTestHelper recordTestHelper = new DDLRecordTestHelper( group, recordSet); DDMFormValues ddmFormValues = createDDMFormValues(LocaleUtil.US); Map<Locale, String> values = new HashMap<>(); values.put(LocaleUtil.US, "Joe Bloggs"); DDMFormFieldValue nameDDMFormFieldValue = createLocalizedDDMFormFieldValue("name", values); ddmFormValues.addDDMFormFieldValue(nameDDMFormFieldValue); values = new HashMap<>(); values.put(LocaleUtil.US, "Simple description"); DDMFormFieldValue descriptionDDMFormFieldValue = createLocalizedDDMFormFieldValue("description", values); ddmFormValues.addDDMFormFieldValue(descriptionDDMFormFieldValue); recordTestHelper.addRecord( ddmFormValues, WorkflowConstants.ACTION_PUBLISH); searchContext.setKeywords("Simple description"); Hits hits = DDLRecordLocalServiceUtil.search(searchContext); Assert.assertEquals(hits.toString(), 1, hits.getLength()); } @Test public void testBasicSearchWithJustOneTerm() throws Exception { addRecord("Joe Bloggs", "Simple description"); addRecord("Bloggs", "Another description example"); addRecord(RandomTestUtil.randomString(), RandomTestUtil.randomString()); assertSearch("example", 1); assertSearch("description", 2); } @Test public void testExactPhrase() throws Exception { addRecord("Joe Bloggs", "Simple description"); addRecord("Bloggs", "Another description example"); addRecord(RandomTestUtil.randomString(), RandomTestUtil.randomString()); assertSearch("\"Joe Bloggs\"", 1); assertSearch("Bloggs", 2); } @Test public void testExactPhraseMixedWithWords() throws Exception { addRecord("One Two Three Four Five Six", RandomTestUtil.randomString()); addRecord(RandomTestUtil.randomString(), RandomTestUtil.randomString()); assertSearch("\"Two Three\" Five", 1); assertSearch("\"Two Three\" Nine", 0); assertSearch("\"Two Four\" Five", 0); assertSearch("\"Two Four\" Nine", 0); assertSearch("Three \"Five Six\"", 1); assertSearch("Zero \"Five Six\"", 0); assertSearch("Three \"Four Six\"", 0); assertSearch("Zero \"Four Six\"", 0); assertSearch("One \"Three Four\" Six ", 1); assertSearch("Zero \"Three Four\" Nine", 0); assertSearch("One \"Three Five\" Six ", 0); assertSearch("Zero \"Three Five\" Nine", 0); } @Test public void testLocales() throws Exception { long companyId = TestPropsValues.getCompanyId(); User user = UserLocalServiceUtil.getDefaultUser(companyId); Group group = GroupTestUtil.addGroup( companyId, user.getUserId(), GroupConstants.DEFAULT_PARENT_GROUP_ID); DDLRecordSetTestHelper recordSetTestHelper = new DDLRecordSetTestHelper( group); DDMStructureTestHelper ddmStructureTestHelper = new DDMStructureTestHelper( PortalUtil.getClassNameId(DDLRecordSet.class), group); Set<Locale> locales = DDMFormTestUtil.createAvailableLocales( new Locale[] {LocaleUtil.US, LocaleUtil.JAPAN}); DDMForm ddmForm = DDMFormTestUtil.createDDMForm(locales, LocaleUtil.US); DDMFormField nameDDMFormField = DDMFormTestUtil.createTextDDMFormField( "name", true, false, false); LocalizedValue label = nameDDMFormField.getLabel(); label.addString(LocaleUtil.JAPAN, "名"); nameDDMFormField.setIndexType("keyword"); ddmForm.addDDMFormField(nameDDMFormField); DDMStructure ddmStructure = ddmStructureTestHelper.addStructure( ddmForm, StorageType.JSON.toString()); DDLRecordSet recordSet = recordSetTestHelper.addRecordSet(ddmStructure); SearchContext searchContext = getSearchContext(group, user, recordSet); searchContext.setLocale(LocaleUtil.JAPAN); DDMFormValues ddmFormValues = DDMFormValuesTestUtil.createDDMFormValues( ddmForm); Map<Locale, String> nameMap = new HashMap<>(); nameMap.put(LocaleUtil.US, "simple text"); nameMap.put(LocaleUtil.JAPAN, "単純なテキスト"); DDMFormFieldValue nameDDMFormFieldValue = createLocalizedDDMFormFieldValue("name", nameMap); ddmFormValues.addDDMFormFieldValue(nameDDMFormFieldValue); _recordTestHelper.addRecord( ddmFormValues, WorkflowConstants.ACTION_PUBLISH); DDLRecordTestHelper recordTestHelper = new DDLRecordTestHelper( group, recordSet); recordTestHelper.addRecord( ddmFormValues, WorkflowConstants.ACTION_PUBLISH); searchContext.setKeywords("単純なテキスト"); Hits hits = DDLRecordLocalServiceUtil.search(searchContext); Assert.assertEquals(hits.toString(), 1, hits.getLength()); } @Test public void testPunctuationInExactPhrase() throws Exception { addRecord("Joe? Bloggs!"); addRecord("Joe! Bloggs?"); addRecord("Joe Bloggs"); addRecord("Bloggs"); assertSearch("\"Joe? Bloggs!\"", 3); assertSearch("\"Joe! Bloggs?\"", 3); } @Test public void testQuestionMarksVersusStopwords1() throws Exception { addRecord(RandomTestUtil.randomString()); addRecord("how ? create ? coupon"); assertSearch("\"how ? create ? coupon\"", 1); assertSearch("\"how to create a coupon\"", 0); assertSearch("\"how with create the coupon\"", 0); } @Test public void testQuestionMarksVersusStopwords2() throws Exception { Assume.assumeTrue(isExactPhraseQueryImplementedForSearchEngine()); addRecord(RandomTestUtil.randomString()); addRecord("how with create the coupon"); assertSearch("\"how ? create ? coupon\"", 0); assertSearch("\"how to create a coupon\"", 1); assertSearch("\"how with create the coupon\"", 1); } @Test public void testQuestionMarksVersusStopwords3() throws Exception { Assume.assumeTrue(isExactPhraseQueryImplementedForSearchEngine()); addRecord(RandomTestUtil.randomString()); addRecord("how to create a coupon"); assertSearch("\"how ? create ? coupon\"", 0); assertSearch("\"how to create a coupon\"", 1); assertSearch("\"how with create the coupon\"", 1); } @Test public void testQuestionMarksVersusStopwords4() throws Exception { addRecord(RandomTestUtil.randomString()); addRecord("how ! create ! coupon"); assertSearch("\"how ? create ? coupon\"", 1); assertSearch("\"how to create a coupon\"", 0); assertSearch("\"how with create the coupon\"", 0); } @Test public void testStopwords() throws Exception { addRecord(RandomTestUtil.randomString()); addRecord(RandomTestUtil.randomString(), "Another description example"); assertSearch("Another The Example", 1); } @Test public void testStopwordsInExactPhrase() throws Exception { Assume.assumeTrue(isExactPhraseQueryImplementedForSearchEngine()); addRecord("how to create a coupon"); addRecord("Joe Of Bloggs"); addRecord("Joe Bloggs"); addRecord("Bloggs"); assertSearch("\"how to create a coupon\"", 1); assertSearch("\"how with create the coupon\"", 1); assertSearch("\"how Liferay create Liferay coupon\"", 0); assertSearch("\"how create coupon\"", 0); assertSearch("\"Joe Of Bloggs\"", 1); assertSearch("\"Joe The Bloggs\"", 1); assertSearch("\"Bloggs A\"", 3); assertSearch("\"Of Bloggs\"", 3); assertSearch("\"The Bloggs\"", 3); } protected static SearchContext getSearchContext( Group group, User user, DDLRecordSet recordSet) throws Exception { SearchContext searchContext = SearchContextTestUtil.getSearchContext( group.getGroupId()); searchContext.setAttribute("recordSetId", recordSet.getRecordSetId()); searchContext.setAttribute("status", WorkflowConstants.STATUS_ANY); searchContext.setUserId(user.getUserId()); return searchContext; } protected void addRecord(Map<Locale, String> name) throws Exception { Map<Locale, String> description = new HashMap<>(); for (Map.Entry<Locale, String> entry : name.entrySet()) { description.put(entry.getKey(), RandomTestUtil.randomString()); } addRecord(name, description); } protected void addRecord( Map<Locale, String> name, Map<Locale, String> description) throws Exception { Locale[] locales = new Locale[name.size()]; name.keySet().toArray(locales); DDMFormValues ddmFormValues = createDDMFormValues(locales); DDMFormFieldValue nameDDMFormFieldValue = createLocalizedDDMFormFieldValue("name", name); ddmFormValues.addDDMFormFieldValue(nameDDMFormFieldValue); DDMFormFieldValue descriptionDDMFormFieldValue = createLocalizedDDMFormFieldValue("description", description); ddmFormValues.addDDMFormFieldValue(descriptionDDMFormFieldValue); _recordTestHelper.addRecord( ddmFormValues, WorkflowConstants.ACTION_PUBLISH); } protected void addRecord(String name) throws Exception { Map<Locale, String> nameMap = new HashMap<>(); nameMap.put(LocaleUtil.US, name); addRecord(nameMap); } protected void addRecord(String name, String description) throws Exception { Map<Locale, String> nameMap = new HashMap<>(); nameMap.put(LocaleUtil.US, name); Map<Locale, String> descriptionMap = new HashMap<>(); descriptionMap.put(LocaleUtil.US, description); addRecord(nameMap, descriptionMap); } protected DDLRecordSet addRecordSet() throws Exception { DDLRecordSetTestHelper recordSetTestHelper = new DDLRecordSetTestHelper( _group); DDMStructureTestHelper ddmStructureTestHelper = new DDMStructureTestHelper( PortalUtil.getClassNameId(DDLRecordSet.class), _group); DDMStructure ddmStructure = ddmStructureTestHelper.addStructure( createDDMForm(LocaleUtil.US), StorageType.JSON.toString()); return recordSetTestHelper.addRecordSet(ddmStructure); } protected void assertSearch(String keywords, int length) throws Exception { _searchContext.setKeywords(keywords); Hits hits = DDLRecordLocalServiceUtil.search(_searchContext); Assert.assertEquals(hits.toString(), length, hits.getLength()); } protected DDMForm createDDMForm(Locale... locales) { DDMForm ddmForm = DDMFormTestUtil.createDDMForm( DDMFormTestUtil.createAvailableLocales(locales), locales[0]); DDMFormField nameDDMFormField = DDMFormTestUtil.createTextDDMFormField( "name", true, false, false); nameDDMFormField.setIndexType("keyword"); ddmForm.addDDMFormField(nameDDMFormField); DDMFormField descriptionDDMFormField = DDMFormTestUtil.createTextDDMFormField( "description", true, false, false); descriptionDDMFormField.setIndexType("text"); ddmForm.addDDMFormField(descriptionDDMFormField); return ddmForm; } protected DDMFormValues createDDMFormValues(Locale... locales) throws Exception { DDLRecordSet recordSet = _recordTestHelper.getRecordSet(); DDMStructure ddmStructure = recordSet.getDDMStructure(); return DDMFormValuesTestUtil.createDDMFormValues( ddmStructure.getDDMForm(), DDMFormValuesTestUtil.createAvailableLocales(locales), locales[0]); } protected DDMFormFieldValue createLocalizedDDMFormFieldValue( String name, Map<Locale, String> values) { Value localizedValue = new LocalizedValue(LocaleUtil.US); for (Map.Entry<Locale, String> value : values.entrySet()) { localizedValue.addString(value.getKey(), value.getValue()); } return DDMFormValuesTestUtil.createDDMFormFieldValue( name, localizedValue); } protected boolean isExactPhraseQueryImplementedForSearchEngine() { SearchEngine searchEngine = SearchEngineHelperUtil.getSearchEngine( SearchEngineHelperUtil.getDefaultSearchEngineId()); String vendor = searchEngine.getVendor(); if (vendor.equals("Elasticsearch") || vendor.equals("Solr")) { return false; } return true; } protected void setUpPermissionThreadLocal() throws Exception { _originalPermissionChecker = PermissionThreadLocal.getPermissionChecker(); PermissionThreadLocal.setPermissionChecker( new SimplePermissionChecker() { { init(TestPropsValues.getUser()); } @Override public boolean hasOwnerPermission( long companyId, String name, String primKey, long ownerId, String actionId) { return true; } }); } protected void setUpPrincipalThreadLocal() throws Exception { _originalName = PrincipalThreadLocal.getName(); PrincipalThreadLocal.setName(TestPropsValues.getUserId()); } @DeleteAfterTestRun private Group _group; private String _originalName; private PermissionChecker _originalPermissionChecker; private DDLRecordTestHelper _recordTestHelper; private SearchContext _searchContext; @DeleteAfterTestRun private User _user; }