Merge pull request #9125 from tdonohue/add_isNotMemberOf_endpoints

Add `isNotMemberOf` searches for Groups and EPersons (for improved performance on Edit Group pages)
This commit is contained in:
Tim Donohue
2023-11-10 13:01:59 -06:00
committed by GitHub
15 changed files with 1194 additions and 108 deletions

View File

@@ -83,13 +83,14 @@ public abstract class AbstractHibernateDSODAO<T extends DSpaceObject> extends Ab
if (CollectionUtils.isNotEmpty(metadataFields) || StringUtils.isNotBlank(additionalWhere)) {
//Add the where query on metadata
query.append(" WHERE ");
// Group the 'OR' clauses below in outer parentheses, e.g. "WHERE (clause1 OR clause2 OR clause3)".
// Grouping these 'OR' clauses allows for later code to append 'AND' clauses without unexpected behaviors
query.append("(");
for (int i = 0; i < metadataFields.size(); i++) {
MetadataField metadataField = metadataFields.get(i);
if (StringUtils.isNotBlank(operator)) {
query.append(" (");
query.append("lower(STR(" + metadataField.toString()).append(".value)) ").append(operator)
.append(" lower(:queryParam)");
query.append(")");
if (i < metadataFields.size() - 1) {
query.append(" OR ");
}
@@ -102,6 +103,7 @@ public abstract class AbstractHibernateDSODAO<T extends DSpaceObject> extends Ab
}
query.append(additionalWhere);
}
query.append(")");
}
}

View File

@@ -184,32 +184,98 @@ public class EPersonServiceImpl extends DSpaceObjectServiceImpl<EPerson> impleme
@Override
public List<EPerson> search(Context context, String query, int offset, int limit) throws SQLException {
try {
List<EPerson> ePerson = new ArrayList<>();
EPerson person = find(context, UUID.fromString(query));
if (person != null) {
ePerson.add(person);
}
return ePerson;
} catch (IllegalArgumentException e) {
List<EPerson> ePersons = new ArrayList<>();
UUID uuid = UUIDUtils.fromString(query);
if (uuid == null) {
// Search by firstname & lastname (NOTE: email will also be included automatically)
MetadataField firstNameField = metadataFieldService.findByElement(context, "eperson", "firstname", null);
MetadataField lastNameField = metadataFieldService.findByElement(context, "eperson", "lastname", null);
if (StringUtils.isBlank(query)) {
query = null;
}
return ePersonDAO.search(context, query, Arrays.asList(firstNameField, lastNameField),
Arrays.asList(firstNameField, lastNameField), offset, limit);
ePersons = ePersonDAO.search(context, query, Arrays.asList(firstNameField, lastNameField),
Arrays.asList(firstNameField, lastNameField), offset, limit);
} else {
// Search by UUID
EPerson person = find(context, uuid);
if (person != null) {
ePersons.add(person);
}
}
return ePersons;
}
@Override
public int searchResultCount(Context context, String query) throws SQLException {
MetadataField firstNameField = metadataFieldService.findByElement(context, "eperson", "firstname", null);
MetadataField lastNameField = metadataFieldService.findByElement(context, "eperson", "lastname", null);
if (StringUtils.isBlank(query)) {
query = null;
int result = 0;
UUID uuid = UUIDUtils.fromString(query);
if (uuid == null) {
// Count results found by firstname & lastname (email is also included automatically)
MetadataField firstNameField = metadataFieldService.findByElement(context, "eperson", "firstname", null);
MetadataField lastNameField = metadataFieldService.findByElement(context, "eperson", "lastname", null);
if (StringUtils.isBlank(query)) {
query = null;
}
result = ePersonDAO.searchResultCount(context, query, Arrays.asList(firstNameField, lastNameField));
} else {
// Search by UUID
EPerson person = find(context, uuid);
if (person != null) {
result = 1;
}
}
return ePersonDAO.searchResultCount(context, query, Arrays.asList(firstNameField, lastNameField));
return result;
}
@Override
public List<EPerson> searchNonMembers(Context context, String query, Group excludeGroup, int offset, int limit)
throws SQLException {
List<EPerson> ePersons = new ArrayList<>();
UUID uuid = UUIDUtils.fromString(query);
if (uuid == null) {
// Search by firstname & lastname (NOTE: email will also be included automatically)
MetadataField firstNameField = metadataFieldService.findByElement(context, "eperson", "firstname", null);
MetadataField lastNameField = metadataFieldService.findByElement(context, "eperson", "lastname", null);
if (StringUtils.isBlank(query)) {
query = null;
}
ePersons = ePersonDAO.searchNotMember(context, query, Arrays.asList(firstNameField, lastNameField),
excludeGroup, Arrays.asList(firstNameField, lastNameField),
offset, limit);
} else {
// Search by UUID
EPerson person = find(context, uuid);
// Verify EPerson is NOT a member of the given excludeGroup before adding
if (person != null && !groupService.isDirectMember(excludeGroup, person)) {
ePersons.add(person);
}
}
return ePersons;
}
@Override
public int searchNonMembersCount(Context context, String query, Group excludeGroup) throws SQLException {
int result = 0;
UUID uuid = UUIDUtils.fromString(query);
if (uuid == null) {
// Count results found by firstname & lastname (email is also included automatically)
MetadataField firstNameField = metadataFieldService.findByElement(context, "eperson", "firstname", null);
MetadataField lastNameField = metadataFieldService.findByElement(context, "eperson", "lastname", null);
if (StringUtils.isBlank(query)) {
query = null;
}
result = ePersonDAO.searchNotMemberCount(context, query, Arrays.asList(firstNameField, lastNameField),
excludeGroup);
} else {
// Search by UUID
EPerson person = find(context, uuid);
// Verify EPerson is NOT a member of the given excludeGroup before counting
if (person != null && !groupService.isDirectMember(excludeGroup, person)) {
result = 1;
}
}
return result;
}
@Override

View File

@@ -460,17 +460,17 @@ public class GroupServiceImpl extends DSpaceObjectServiceImpl<Group> implements
}
@Override
public List<Group> search(Context context, String groupIdentifier) throws SQLException {
return search(context, groupIdentifier, -1, -1);
public List<Group> search(Context context, String query) throws SQLException {
return search(context, query, -1, -1);
}
@Override
public List<Group> search(Context context, String groupIdentifier, int offset, int limit) throws SQLException {
public List<Group> search(Context context, String query, int offset, int limit) throws SQLException {
List<Group> groups = new ArrayList<>();
UUID uuid = UUIDUtils.fromString(groupIdentifier);
UUID uuid = UUIDUtils.fromString(query);
if (uuid == null) {
//Search by group name
groups = groupDAO.findByNameLike(context, groupIdentifier, offset, limit);
groups = groupDAO.findByNameLike(context, query, offset, limit);
} else {
//Search by group id
Group group = find(context, uuid);
@@ -483,12 +483,12 @@ public class GroupServiceImpl extends DSpaceObjectServiceImpl<Group> implements
}
@Override
public int searchResultCount(Context context, String groupIdentifier) throws SQLException {
public int searchResultCount(Context context, String query) throws SQLException {
int result = 0;
UUID uuid = UUIDUtils.fromString(groupIdentifier);
UUID uuid = UUIDUtils.fromString(query);
if (uuid == null) {
//Search by group name
result = groupDAO.countByNameLike(context, groupIdentifier);
result = groupDAO.countByNameLike(context, query);
} else {
//Search by group id
Group group = find(context, uuid);
@@ -500,6 +500,44 @@ public class GroupServiceImpl extends DSpaceObjectServiceImpl<Group> implements
return result;
}
@Override
public List<Group> searchNonMembers(Context context, String query, Group excludeParentGroup,
int offset, int limit) throws SQLException {
List<Group> groups = new ArrayList<>();
UUID uuid = UUIDUtils.fromString(query);
if (uuid == null) {
// Search by group name
groups = groupDAO.findByNameLikeAndNotMember(context, query, excludeParentGroup, offset, limit);
} else if (!uuid.equals(excludeParentGroup.getID())) {
// Search by group id
Group group = find(context, uuid);
// Verify it is NOT a member of the given excludeParentGroup before adding
if (group != null && !isMember(excludeParentGroup, group)) {
groups.add(group);
}
}
return groups;
}
@Override
public int searchNonMembersCount(Context context, String query, Group excludeParentGroup) throws SQLException {
int result = 0;
UUID uuid = UUIDUtils.fromString(query);
if (uuid == null) {
// Search by group name
result = groupDAO.countByNameLikeAndNotMember(context, query, excludeParentGroup);
} else if (!uuid.equals(excludeParentGroup.getID())) {
// Search by group id
Group group = find(context, uuid);
// Verify it is NOT a member of the given excludeParentGroup before adding
if (group != null && !isMember(excludeParentGroup, group)) {
result = 1;
}
}
return result;
}
@Override
public void delete(Context context, Group group) throws SQLException {
if (group.isPermanent()) {

View File

@@ -33,11 +33,68 @@ public interface EPersonDAO extends DSpaceObjectDAO<EPerson>, DSpaceObjectLegacy
public EPerson findByNetid(Context context, String netid) throws SQLException;
/**
* Search all EPersons by the given MetadataField objects, sorting by the given sort fields.
* <P>
* NOTE: As long as a query is specified, the EPerson's email address is included in the search alongside any given
* metadata fields.
*
* @param context DSpace context
* @param query the text to search EPersons for
* @param queryFields the metadata fields to search within (email is also included automatically)
* @param sortFields the metadata field(s) to sort the results by
* @param offset the position of the first result to return
* @param limit how many results return
* @return List of matching EPerson objects
* @throws SQLException if an error occurs
*/
public List<EPerson> search(Context context, String query, List<MetadataField> queryFields,
List<MetadataField> sortFields, int offset, int limit) throws SQLException;
/**
* Count number of EPersons who match a search on the given metadata fields. This returns the count of total
* results for the same query using the 'search()', and therefore can be used to provide pagination.
*
* @param context DSpace context
* @param query the text to search EPersons for
* @param queryFields the metadata fields to search within (email is also included automatically)
* @return total number of EPersons who match the query
* @throws SQLException if an error occurs
*/
public int searchResultCount(Context context, String query, List<MetadataField> queryFields) throws SQLException;
/**
* Search all EPersons via their firstname, lastname, email (fuzzy match), limited to those EPersons which are NOT
* a member of the given group. This may be used to search across EPersons which are valid to add as members to the
* given group.
*
* @param context The DSpace context
* @param query the text to search EPersons for
* @param queryFields the metadata fields to search within (email is also included automatically)
* @param excludeGroup Group to exclude results from. Members of this group will never be returned.
* @param offset the position of the first result to return
* @param limit how many results return
* @return EPersons matching the query (which are not members of the given group)
* @throws SQLException if database error
*/
List<EPerson> searchNotMember(Context context, String query, List<MetadataField> queryFields, Group excludeGroup,
List<MetadataField> sortFields, int offset, int limit) throws SQLException;
/**
* Count number of EPersons that match a given search (fuzzy match) across firstname, lastname and email. This
* search is limited to those EPersons which are NOT a member of the given group. This may be used
* (with searchNotMember()) to perform a paginated search across EPersons which are valid to add to the given group.
*
* @param context The DSpace context
* @param query querystring to fuzzy match against.
* @param queryFields the metadata fields to search within (email is also included automatically)
* @param excludeGroup Group to exclude results from. Members of this group will never be returned.
* @return Groups matching the query (which are not members of the given parent)
* @throws SQLException if database error
*/
int searchNotMemberCount(Context context, String query, List<MetadataField> queryFields, Group excludeGroup)
throws SQLException;
/**
* Find all EPersons who are a member of one or more of the listed groups in a paginated fashion. This returns
* EPersons ordered by UUID.

View File

@@ -135,6 +135,38 @@ public interface GroupDAO extends DSpaceObjectDAO<Group>, DSpaceObjectLegacySupp
*/
int countByNameLike(Context context, String groupName) throws SQLException;
/**
* Search all groups via their name (fuzzy match), limited to those groups which are NOT a member of the given
* parent group. This may be used to search across groups which are valid to add to the given parent group.
* <P>
* NOTE: The parent group itself is also excluded from the search.
*
* @param context The DSpace context
* @param groupName Group name to fuzzy match against.
* @param excludeParent Parent Group to exclude results from. Groups under this parent will never be returned.
* @param offset Offset to use for pagination (-1 to disable)
* @param limit The maximum number of results to return (-1 to disable)
* @return Groups matching the query (which are not members of the given parent)
* @throws SQLException if database error
*/
List<Group> findByNameLikeAndNotMember(Context context, String groupName, Group excludeParent,
int offset, int limit) throws SQLException;
/**
* Count number of groups that match a given name (fuzzy match), limited to those groups which are NOT a member of
* the given parent group. This may be used (with findByNameLikeAndNotMember()) to search across groups which are
* valid to add to the given parent group.
* <P>
* NOTE: The parent group itself is also excluded from the count.
*
* @param context The DSpace context
* @param groupName Group name to fuzzy match against.
* @param excludeParent Parent Group to exclude results from. Groups under this parent will never be returned.
* @return Groups matching the query (which are not members of the given parent)
* @throws SQLException if database error
*/
int countByNameLikeAndNotMember(Context context, String groupName, Group excludeParent) throws SQLException;
/**
* Find a group by its name and the membership of the given EPerson
*

View File

@@ -70,17 +70,9 @@ public class EPersonDAOImpl extends AbstractHibernateDSODAO<EPerson> implements
String queryString = "SELECT " + EPerson.class.getSimpleName()
.toLowerCase() + " FROM EPerson as " + EPerson.class
.getSimpleName().toLowerCase() + " ";
if (query != null) {
query = "%" + query.toLowerCase() + "%";
}
Query hibernateQuery = getSearchQuery(context, queryString, query, queryFields, sortFields, null);
if (0 <= offset) {
hibernateQuery.setFirstResult(offset);
}
if (0 <= limit) {
hibernateQuery.setMaxResults(limit);
}
Query hibernateQuery = getSearchQuery(context, queryString, query, queryFields, null,
sortFields, null, limit, offset);
return list(hibernateQuery);
}
@@ -92,6 +84,28 @@ public class EPersonDAOImpl extends AbstractHibernateDSODAO<EPerson> implements
return count(hibernateQuery);
}
@Override
public List<EPerson> searchNotMember(Context context, String query, List<MetadataField> queryFields,
Group excludeGroup, List<MetadataField> sortFields,
int offset, int limit) throws SQLException {
String queryString = "SELECT " + EPerson.class.getSimpleName()
.toLowerCase() + " FROM EPerson as " + EPerson.class
.getSimpleName().toLowerCase() + " ";
Query hibernateQuery = getSearchQuery(context, queryString, query, queryFields, excludeGroup,
sortFields, null, limit, offset);
return list(hibernateQuery);
}
public int searchNotMemberCount(Context context, String query, List<MetadataField> queryFields,
Group excludeGroup) throws SQLException {
String queryString = "SELECT count(*) FROM EPerson as " + EPerson.class.getSimpleName().toLowerCase();
Query hibernateQuery = getSearchQuery(context, queryString, query, queryFields, excludeGroup,
Collections.EMPTY_LIST, null, -1, -1);
return count(hibernateQuery);
}
@Override
public List<EPerson> findAll(Context context, MetadataField metadataSortField, String sortField, int pageSize,
int offset) throws SQLException {
@@ -105,8 +119,8 @@ public class EPersonDAOImpl extends AbstractHibernateDSODAO<EPerson> implements
sortFields = Collections.singletonList(metadataSortField);
}
Query query = getSearchQuery(context, queryString, null, ListUtils.EMPTY_LIST, sortFields, sortField, pageSize,
offset);
Query query = getSearchQuery(context, queryString, null, ListUtils.EMPTY_LIST, null,
sortFields, sortField, pageSize, offset);
return list(query);
}
@@ -178,43 +192,88 @@ public class EPersonDAOImpl extends AbstractHibernateDSODAO<EPerson> implements
protected Query getSearchQuery(Context context, String queryString, String queryParam,
List<MetadataField> queryFields, List<MetadataField> sortFields, String sortField)
throws SQLException {
return getSearchQuery(context, queryString, queryParam, queryFields, sortFields, sortField, -1, -1);
return getSearchQuery(context, queryString, queryParam, queryFields, null, sortFields, sortField, -1, -1);
}
/**
* Build a search query across EPersons based on the given metadata fields and sorted based on the given metadata
* field(s) or database column.
* <P>
* NOTE: the EPerson's email address is included in the search alongside any given metadata fields.
*
* @param context DSpace Context
* @param queryString String which defines the beginning "SELECT" for the SQL query
* @param queryParam Actual text being searched for
* @param queryFields List of metadata fields to search within
* @param excludeGroup Optional Group which should be excluded from search. Any EPersons who are members
* of this group will not be included in the results.
* @param sortFields Optional List of metadata fields to sort by (should not be specified if sortField is used)
* @param sortField Optional database column to sort on (should not be specified if sortFields is used)
* @param pageSize how many results return
* @param offset the position of the first result to return
* @return built Query object
* @throws SQLException if error occurs
*/
protected Query getSearchQuery(Context context, String queryString, String queryParam,
List<MetadataField> queryFields, List<MetadataField> sortFields, String sortField,
int pageSize, int offset) throws SQLException {
List<MetadataField> queryFields, Group excludeGroup,
List<MetadataField> sortFields, String sortField,
int pageSize, int offset) throws SQLException {
// Initialize SQL statement using the passed in "queryString"
StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append(queryString);
Set<MetadataField> metadataFieldsToJoin = new LinkedHashSet<>();
metadataFieldsToJoin.addAll(queryFields);
metadataFieldsToJoin.addAll(sortFields);
// Append necessary join information for MetadataFields we will search within
if (!CollectionUtils.isEmpty(metadataFieldsToJoin)) {
addMetadataLeftJoin(queryBuilder, EPerson.class.getSimpleName().toLowerCase(), metadataFieldsToJoin);
}
if (queryParam != null) {
// Always append a search on EPerson "email" based on query
if (StringUtils.isNotBlank(queryParam)) {
addMetadataValueWhereQuery(queryBuilder, queryFields, "like",
EPerson.class.getSimpleName().toLowerCase() + ".email like :queryParam");
}
// If excludeGroup is specified, exclude members of that group from results
// This uses a subquery to find the excluded group & verify that it is not in the EPerson list of "groups"
if (excludeGroup != null) {
// If query params exist, then we already have a WHERE clause (see above) and just need to append an AND
if (StringUtils.isNotBlank(queryParam)) {
queryBuilder.append(" AND ");
} else {
// no WHERE clause yet, so this is the start of the WHERE
queryBuilder.append(" WHERE ");
}
queryBuilder.append("(FROM Group g where g.id = :group_id) NOT IN elements (")
.append(EPerson.class.getSimpleName().toLowerCase()).append(".groups)");
}
// Add sort/order by info to query, if specified
if (!CollectionUtils.isEmpty(sortFields) || StringUtils.isNotBlank(sortField)) {
addMetadataSortQuery(queryBuilder, sortFields, Collections.singletonList(sortField));
}
// Create the final SQL SELECT statement (based on included params above)
Query query = createQuery(context, queryBuilder.toString());
// Set pagesize & offset for pagination
if (pageSize > 0) {
query.setMaxResults(pageSize);
}
if (offset > 0) {
query.setFirstResult(offset);
}
// Set all parameters to the SQL SELECT statement (based on included params above)
if (StringUtils.isNotBlank(queryParam)) {
query.setParameter("queryParam", "%" + queryParam.toLowerCase() + "%");
}
for (MetadataField metadataField : metadataFieldsToJoin) {
query.setParameter(metadataField.toString(), metadataField.getID());
}
if (excludeGroup != null) {
query.setParameter("group_id", excludeGroup.getID());
}
query.setHint("org.hibernate.cacheable", Boolean.TRUE);
return query;
}

View File

@@ -164,6 +164,41 @@ public class GroupDAOImpl extends AbstractHibernateDSODAO<Group> implements Grou
return count(query);
}
@Override
public List<Group> findByNameLikeAndNotMember(Context context, String groupName, Group excludeParent,
int offset, int limit) throws SQLException {
Query query = createQuery(context,
"FROM Group " +
"WHERE lower(name) LIKE lower(:group_name) " +
"AND id != :parent_id " +
"AND (from Group g where g.id = :parent_id) not in elements (parentGroups)");
query.setParameter("parent_id", excludeParent.getID());
query.setParameter("group_name", "%" + StringUtils.trimToEmpty(groupName) + "%");
if (0 <= offset) {
query.setFirstResult(offset);
}
if (0 <= limit) {
query.setMaxResults(limit);
}
query.setHint("org.hibernate.cacheable", Boolean.TRUE);
return list(query);
}
@Override
public int countByNameLikeAndNotMember(Context context, String groupName, Group excludeParent) throws SQLException {
Query query = createQuery(context,
"SELECT count(*) FROM Group " +
"WHERE lower(name) LIKE lower(:group_name) " +
"AND id != :parent_id " +
"AND (from Group g where g.id = :parent_id) not in elements (parentGroups)");
query.setParameter("parent_id", excludeParent.getID());
query.setParameter("group_name", "%" + StringUtils.trimToEmpty(groupName) + "%");
return count(query);
}
@Override
public void delete(Context context, Group group) throws SQLException {
Query query = getHibernateSession(context)
@@ -213,6 +248,7 @@ public class GroupDAOImpl extends AbstractHibernateDSODAO<Group> implements Grou
return list(query);
}
@Override
public int countByParent(Context context, Group parent) throws SQLException {
Query query = createQuery(context, "SELECT count(g) FROM Group g JOIN g.parentGroups pg " +
"WHERE pg.id = :parent_id");

View File

@@ -98,9 +98,9 @@ public interface EPersonService extends DSpaceObjectService<EPerson>, DSpaceObje
*
* @param context The relevant DSpace Context.
* @param query The search string
* @param offset Inclusive offset
* @param offset Inclusive offset (the position of the first result to return)
* @param limit Maximum number of matches returned
* @return array of EPerson objects
* @return List of matching EPerson objects
* @throws SQLException An exception that provides information on a database access error or other errors.
*/
public List<EPerson> search(Context context, String query, int offset, int limit)
@@ -118,6 +118,34 @@ public interface EPersonService extends DSpaceObjectService<EPerson>, DSpaceObje
public int searchResultCount(Context context, String query)
throws SQLException;
/**
* Find the EPersons that match the search query which are NOT currently members of the given Group. The search
* query is run against firstname, lastname or email.
*
* @param context DSpace context
* @param query The search string
* @param excludeGroup Group to exclude results from. Members of this group will never be returned.
* @param offset Inclusive offset (the position of the first result to return)
* @param limit Maximum number of matches returned
* @return List of matching EPerson objects
* @throws SQLException if error
*/
List<EPerson> searchNonMembers(Context context, String query, Group excludeGroup,
int offset, int limit) throws SQLException;
/**
* Returns the total number of EPersons that match the search query which are NOT currently members of the given
* Group. The search query is run against firstname, lastname or email. Can be used with searchNonMembers() to
* support pagination
*
* @param context DSpace context
* @param query The search string
* @param excludeGroup Group to exclude results from. Members of this group will never be returned.
* @return List of matching EPerson objects
* @throws SQLException if error
*/
int searchNonMembersCount(Context context, String query, Group excludeGroup) throws SQLException;
/**
* Find all the {@code EPerson}s in a specific order by field.
* The sortable fields are:

View File

@@ -261,37 +261,67 @@ public interface GroupService extends DSpaceObjectService<Group>, DSpaceObjectLe
public List<Group> findAll(Context context, int sortField) throws SQLException;
/**
* Find the groups that match the search query across eperson_group_id or name
* Find the Groups that match the query across both Group name and Group ID. This is an unpaginated search,
* which means it will load all matching groups into memory at once. This may provide POOR PERFORMANCE when a large
* number of groups are matched.
*
* @param context DSpace context
* @param groupIdentifier The group name or group ID
* @return array of Group objects
* @param context DSpace context
* @param query The search string used to search across group name or group ID
* @return List of matching Group objects
* @throws SQLException if error
*/
public List<Group> search(Context context, String groupIdentifier) throws SQLException;
List<Group> search(Context context, String query) throws SQLException;
/**
* Find the groups that match the search query across eperson_group_id or name
* Find the Groups that match the query across both Group name and Group ID. This method supports pagination,
* which provides better performance than the above non-paginated search() method.
*
* @param context DSpace context
* @param groupIdentifier The group name or group ID
* @param offset Inclusive offset
* @param limit Maximum number of matches returned
* @return array of Group objects
* @param context DSpace context
* @param query The search string used to search across group name or group ID
* @param offset Inclusive offset (the position of the first result to return)
* @param limit Maximum number of matches returned
* @return List of matching Group objects
* @throws SQLException if error
*/
public List<Group> search(Context context, String groupIdentifier, int offset, int limit) throws SQLException;
List<Group> search(Context context, String query, int offset, int limit) throws SQLException;
/**
* Returns the total number of groups returned by a specific query, without the overhead
* of creating the Group objects to store the results.
* Returns the total number of Groups returned by a specific query. Search is performed based on Group name
* and Group ID. May be used with search() above to support pagination of matching Groups.
*
* @param context DSpace context
* @param query The search string
* @param query The search string used to search across group name or group ID
* @return the number of groups matching the query
* @throws SQLException if error
*/
public int searchResultCount(Context context, String query) throws SQLException;
int searchResultCount(Context context, String query) throws SQLException;
/**
* Find the groups that match the search query which are NOT currently members (subgroups)
* of the given parentGroup
*
* @param context DSpace context
* @param query The search string used to search across group name or group ID
* @param excludeParentGroup Parent group to exclude results from
* @param offset Inclusive offset (the position of the first result to return)
* @param limit Maximum number of matches returned
* @return List of matching Group objects
* @throws SQLException if error
*/
List<Group> searchNonMembers(Context context, String query, Group excludeParentGroup,
int offset, int limit) throws SQLException;
/**
* Returns the total number of groups that match the search query which are NOT currently members (subgroups)
* of the given parentGroup. Can be used with searchNonMembers() to support pagination.
*
* @param context DSpace context
* @param query The search string used to search across group name or group ID
* @param excludeParentGroup Parent group to exclude results from
* @return the number of Groups matching the query
* @throws SQLException if error
*/
int searchNonMembersCount(Context context, String query, Group excludeParentGroup) throws SQLException;
/**
* Return true if group has no direct or indirect members