Skip to content

Commit

Permalink
#24921 include in 23.01.8
Browse files Browse the repository at this point in the history
  • Loading branch information
erickgonzalez committed Oct 31, 2023
1 parent c69b4a4 commit bc29980
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 48 deletions.
Expand Up @@ -14,6 +14,8 @@
import java.util.List;
import java.util.Optional;

import static com.dotmarketing.portlets.contentlet.business.HostFactoryImpl.SITE_IS_LIVE_OR_STOPPED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class HostFactoryImplTest extends IntegrationTestBase {
Expand Down Expand Up @@ -73,4 +75,43 @@ public void test_findLiveAndStopped_shouldOnlyRetunLiveAndStoppedSites() throws
}

}

/**
* Method to test: {@link HostFactoryImpl#search(String, String, boolean, int, int, User, boolean)}
* Given Scenario: Create many (20+) sites that have the same text in them
* example1.test.com, example2.test.com..., then just test.com
* ExpectedResult: Exact matches should be at the top of the search results.
*
*/
@Test
public void test_search_shouldReturnExactMatchesFirst() throws DotDataException, DotSecurityException {
// Initialization
final int limit = 15;
final int offset = 0;
final HostFactoryImpl hostFactory = new HostFactoryImpl();
final long systemMilis = System.currentTimeMillis();

final String baseName = "test.com";

// generate 20 sites with the name test.com
for (int i = 0; i < 20; i++) {
new SiteDataGen().name("example"+i+"-"+systemMilis+"."+baseName).nextPersisted(true);
}

//get the site with the name test.com
Host testSite = APILocator.getHostAPI().findByName(baseName, APILocator.systemUser(), false);

//validate if the site is null
//if is null create a new site with the name test.com
if (testSite == null) {
testSite = new SiteDataGen().name(baseName).nextPersisted(true);
}

// test the method search at class HostFactoryImpl where the filter is "test.com" and should return it first in list
final Optional<List<Host>> hostsList = hostFactory.search(baseName, SITE_IS_LIVE_OR_STOPPED,false ,limit, offset, APILocator.systemUser(), false);

//validations
assertTrue( "Test site is not contained in list", hostsList.get().contains(testSite));
assertEquals("Test site is not the first in the list", testSite, hostsList.get().get(0));
}
}
Expand Up @@ -37,6 +37,7 @@
import com.dotmarketing.portlets.templates.model.Template;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.UtilMethods;
import com.google.common.annotations.VisibleForTesting;
import com.liferay.portal.model.User;
import io.vavr.control.Try;
import org.apache.commons.lang3.concurrent.ConcurrentUtils;
Expand Down Expand Up @@ -74,61 +75,69 @@ public class HostFactoryImpl implements HostFactory {
private final String AND = " AND ";
private final String ORDER_BY = " ORDER BY ? ";

private static final StringBuilder SELECT_SYSTEM_HOST = new StringBuilder()
.append("SELECT id FROM identifier WHERE id = '").append(Host.SYSTEM_HOST).append("' ");
private static final String SELECT_SYSTEM_HOST = "SELECT id FROM identifier WHERE id = '"+ Host.SYSTEM_HOST+"' ";

private static final StringBuilder FROM_JOINED_TABLES = new StringBuilder()
.append("INNER JOIN identifier i ")
.append("ON c.identifier = i.id AND i.asset_subtype = '").append(Host.HOST_VELOCITY_VAR_NAME).append("' ")
.append("INNER JOIN contentlet_version_info cvi ")
.append("ON c.inode = cvi.working_inode ");
private static final String FROM_JOINED_TABLES = "INNER JOIN identifier i " +
"ON c.identifier = i.id AND i.asset_subtype = '" + Host.HOST_VELOCITY_VAR_NAME + "' " +
"INNER JOIN contentlet_version_info cvi " +
"ON c.inode = cvi.working_inode ";

private static final StringBuilder SELECT_SITE_INODE = new StringBuilder().append("SELECT c.inode FROM contentlet" +
" c ").append(FROM_JOINED_TABLES);
private static final String SELECT_SITE_INODE = "SELECT c.inode FROM contentlet" +
" c " +
FROM_JOINED_TABLES;
private static final String SELECT_SITE_INODE_AND_ALIASES = "SELECT c.inode, %s" +
" AS aliases FROM contentlet c " +
FROM_JOINED_TABLES;

private static final StringBuilder SELECT_SITE_INODE_AND_ALIASES = new StringBuilder().append("SELECT c.inode, %s" +
" AS aliases FROM contentlet c ").append(FROM_JOINED_TABLES);
private static final String POSTGRES_ALIASES_COLUMN = ContentletJsonAPI
.CONTENTLET_AS_JSON +
"->'fields'->'" + Host.ALIASES_KEY + "'->>'value' ";

private static final StringBuilder POSTGRES_ALIASES_COLUMN = new StringBuilder(ContentletJsonAPI
.CONTENTLET_AS_JSON).append("->'fields'->'").append(Host.ALIASES_KEY).append("'->>'value' ");
private static final String MSSQL_ALIASES_COLUMN = "JSON_VALUE(c." +
ContentletJsonAPI.CONTENTLET_AS_JSON + ", '$.fields." + Host.ALIASES_KEY + ".value') ";
private static final String ALIASES_COLUMN = "c.text_area1";

private static final StringBuilder MSSQL_ALIASES_COLUMN = new StringBuilder("JSON_VALUE(c.").append
(ContentletJsonAPI.CONTENTLET_AS_JSON).append(", '$.fields.").append(Host.ALIASES_KEY).append(".value') ");
private static final String SITE_NAME_LIKE = "LOWER(%s) LIKE ? ";

private static final StringBuilder ALIASES_COLUMN = new StringBuilder("c.text_area1");
private static final String SITE_NAME_EQUALS ="LOWER(%s) = ? ";

private static final StringBuilder SITE_NAME_LIKE = new StringBuilder().append("LOWER(%s) LIKE ? ");
private static final String POSTGRES_SITENAME_COLUMN = ContentletJsonAPI
.CONTENTLET_AS_JSON +
"->'fields'->'" + Host.HOST_NAME_KEY + "'->>'value' ";

private static final StringBuilder SITE_NAME_EQUALS = new StringBuilder().append("LOWER(%s) = ? ");
private static final String MSSQL_SITENAME_COLUMN = "JSON_VALUE(c." +
ContentletJsonAPI.CONTENTLET_AS_JSON + ", '$.fields." + Host.HOST_NAME_KEY + ".value')" +
" ";

private static final StringBuilder POSTGRES_SITENAME_COLUMN = new StringBuilder(ContentletJsonAPI
.CONTENTLET_AS_JSON).append("->'fields'->'").append(Host.HOST_NAME_KEY).append("'->>'value' ");
private static final String SITENAME_COLUMN = "c.text1";

private static final StringBuilder MSSQL_SITENAME_COLUMN = new StringBuilder("JSON_VALUE(c.").append
(ContentletJsonAPI.CONTENTLET_AS_JSON).append(", '$.fields.").append(Host.HOST_NAME_KEY).append(".value')" +
" ");
private static final String ALIAS_LIKE = "LOWER(%s) LIKE ? ";

private static final StringBuilder SITENAME_COLUMN = new StringBuilder("c.text1");
private static final String EXCLUDE_SYSTEM_HOST = "i.id <> '" + Host
.SYSTEM_HOST +
"' ";

private static final StringBuilder ALIAS_LIKE = new StringBuilder().append("LOWER(%s) LIKE ? ");
private static final String SITE_IS_LIVE = "cvi.live_inode IS NOT NULL";
@VisibleForTesting
protected static final String SITE_IS_LIVE_OR_STOPPED = "cvi.live_inode IS NOT null or " +
"(cvi.live_inode IS NULL AND cvi.deleted = false )";
private static final String SITE_IS_STOPPED = "cvi.live_inode IS NULL AND cvi" +
".deleted = " +
getDBFalse();

private static final StringBuilder EXCLUDE_SYSTEM_HOST = new StringBuilder().append("i.id <> '").append(Host
.SYSTEM_HOST).append("' ");
private static final String SITE_IS_STOPPED_OR_ARCHIVED = "cvi.live_inode IS NULL";

private static final StringBuilder SITE_IS_LIVE = new StringBuilder().append("cvi.live_inode IS NOT NULL");
private static final StringBuilder SITE_IS_LIVE_OR_STOPPED = new StringBuilder().append("cvi.live_inode IS NOT null or " +
"(cvi.live_inode IS NULL AND cvi.deleted = false )");
private static final StringBuilder SITE_IS_STOPPED = new StringBuilder().append("cvi.live_inode IS NULL AND cvi" +
".deleted = ").append(getDBFalse());
private static final String SITE_IS_ARCHIVED = "cvi.live_inode IS NULL AND cvi" +
".deleted = " +
getDBTrue();

private static final StringBuilder SITE_IS_STOPPED_OR_ARCHIVED = new StringBuilder().append("cvi.live_inode IS NULL");
private static final String SELECT_SITE_COUNT = "SELECT COUNT(cvi.working_inode) " +
"FROM contentlet_version_info cvi, identifier i " + "WHERE i.asset_subtype = '" +
Host.HOST_VELOCITY_VAR_NAME + "' " + " AND cvi.identifier = i.id ";

private static final StringBuilder SITE_IS_ARCHIVED = new StringBuilder().append("cvi.live_inode IS NULL AND cvi" +
".deleted = ").append(getDBTrue());

private static final StringBuilder SELECT_SITE_COUNT = new StringBuilder().append("SELECT COUNT(cvi.working_inode) ")
.append("FROM contentlet_version_info cvi, identifier i ").append("WHERE i.asset_subtype = '")
.append(Host.HOST_VELOCITY_VAR_NAME).append("' ").append(" AND cvi.identifier = i.id ");
// query that Exact matches should be at the top of the search results.
private static final String PRIORITIZE_EXACT_MATCHES =
"ORDER BY CASE WHEN LOWER(%s) = ? THEN 0 ELSE 1 END";

/**
* Default class constructor.
Expand Down Expand Up @@ -170,7 +179,7 @@ public Host bySiteName(final String siteName) {
final DotConnect dc = new DotConnect();
final StringBuilder sqlQuery = new StringBuilder().append(SELECT_SITE_INODE)
.append(WHERE);
sqlQuery.append(getSiteNameColumn(SITE_NAME_EQUALS.toString()));
sqlQuery.append(getSiteNameColumn(SITE_NAME_EQUALS));
dc.setSQL(sqlQuery.toString());
dc.addParam(siteName.toLowerCase());
try {
Expand Down Expand Up @@ -212,9 +221,9 @@ public Host byAlias(String alias) {
String sql = sqlQuery.toString();
if (APILocator.getContentletJsonAPI().isJsonSupportedDatabase()) {
if (DbConnectionFactory.isPostgres()) {
sql = String.format(sql, POSTGRES_ALIASES_COLUMN.toString(), POSTGRES_ALIASES_COLUMN.toString());
sql = String.format(sql, POSTGRES_ALIASES_COLUMN, POSTGRES_ALIASES_COLUMN);
} else {
sql = String.format(sql, MSSQL_ALIASES_COLUMN.toString(), MSSQL_ALIASES_COLUMN.toString());
sql = String.format(sql, MSSQL_ALIASES_COLUMN, MSSQL_ALIASES_COLUMN);
}
} else {
sql = String.format(sql, ALIASES_COLUMN, ALIASES_COLUMN);
Expand Down Expand Up @@ -628,7 +637,7 @@ public Optional<Host> findDefaultHost(final String contentTypeId, final String c
@Override
public Optional<List<Host>> findLiveSites(final String siteNameFilter, final int limit, final int offset,
final boolean showSystemHost, final User user, final boolean respectFrontendRoles) {
return search(siteNameFilter, SITE_IS_LIVE.toString(), showSystemHost, limit, offset, user,
return search(siteNameFilter, SITE_IS_LIVE, showSystemHost, limit, offset, user,
respectFrontendRoles);
}

Expand All @@ -637,22 +646,22 @@ public Optional<List<Host>> findStoppedSites(final String siteNameFilter, final
final int limit, final int offset, final boolean showSystemHost,
final User user, final boolean respectFrontendRoles) {
final String condition =
includeArchivedSites ? SITE_IS_STOPPED_OR_ARCHIVED.toString() : SITE_IS_STOPPED.toString();
includeArchivedSites ? SITE_IS_STOPPED_OR_ARCHIVED : SITE_IS_STOPPED;
return search(siteNameFilter, condition, showSystemHost, limit, offset, user, respectFrontendRoles);
}

@Override
public Optional<List<Host>> findArchivedSites(final String siteNameFilter, final int limit, final int offset,
final boolean showSystemHost, final User user,
final boolean respectFrontendRoles) {
return search(siteNameFilter, SITE_IS_ARCHIVED.toString(), showSystemHost, limit, offset, user,
return search(siteNameFilter, SITE_IS_ARCHIVED, showSystemHost, limit, offset, user,
respectFrontendRoles);
}

@Override
public long count() throws DotDataException {
final DotConnect dc = new DotConnect();
dc.setSQL(SELECT_SITE_COUNT.toString());
dc.setSQL(SELECT_SITE_COUNT);
final List<Map<String, String>> dbResults = dc.loadResults();
final String total = dbResults.get(0).get("count");
return ConversionUtils.toLong(total, 0L);
Expand Down Expand Up @@ -682,15 +691,16 @@ public long count() throws DotDataException {
*
* @return The list of {@link Host} objects that match the specified search criteria.
*/
private Optional<List<Host>> search(final String siteNameFilter, final String condition, final boolean
@VisibleForTesting
protected Optional<List<Host>> search(final String siteNameFilter, final String condition, final boolean
showSystemHost, final int limit, final int offset, final User user, final boolean respectFrontendRoles) {
final DotConnect dc = new DotConnect();
final StringBuilder sqlQuery = new StringBuilder().append(SELECT_SITE_INODE);
sqlQuery.append(WHERE);
sqlQuery.append("cvi.identifier = i.id");
if (UtilMethods.isSet(siteNameFilter)) {
sqlQuery.append(AND);
sqlQuery.append(getSiteNameColumn(SITE_NAME_LIKE.toString()));
sqlQuery.append(getSiteNameColumn(SITE_NAME_LIKE));
}
if (UtilMethods.isSet(condition)) {
sqlQuery.append(AND);
Expand All @@ -700,9 +710,15 @@ private Optional<List<Host>> search(final String siteNameFilter, final String co
sqlQuery.append(AND);
sqlQuery.append(EXCLUDE_SYSTEM_HOST);
}
if (UtilMethods.isSet(siteNameFilter)) {
sqlQuery.append(getSiteNameColumn(PRIORITIZE_EXACT_MATCHES));
}
dc.setSQL(sqlQuery.toString());
if (UtilMethods.isSet(siteNameFilter)) {
// Add the site name filter parameter
dc.addParam("%" + siteNameFilter.trim() + "%");
// Add the site name filter parameter again, but this time for the exact match
dc.addParam(siteNameFilter.trim().replace("%", ""));
}
if (limit > 0) {
dc.setMaxRows(limit);
Expand Down
1 change: 1 addition & 0 deletions hotfix_tracking.md
Expand Up @@ -147,3 +147,4 @@ This maintenance release includes the following code fixes:
120. https://github.com/dotCMS/core/issues/25827 : Recreating a field with same name diff type uses the same id #25827
121. https://github.com/dotCMS/core/issues/25870 : Using showFields field variable replicates title to all items #25870
122. https://github.com/dotCMS/core/issues/26374 : Use of Png filter on images results in a 404 #26374
123. https://github.com/dotCMS/core/issues/24921 : Filtering does not put exact matches on the top of the site dropdown when searching #24921

0 comments on commit bc29980

Please sign in to comment.