You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UserIndex.java 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2020 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.user.index;
  21. import java.util.ArrayList;
  22. import java.util.List;
  23. import java.util.Optional;
  24. import org.apache.commons.lang.StringUtils;
  25. import org.elasticsearch.action.search.SearchRequestBuilder;
  26. import org.elasticsearch.index.query.BoolQueryBuilder;
  27. import org.elasticsearch.index.query.Operator;
  28. import org.elasticsearch.index.query.QueryBuilder;
  29. import org.elasticsearch.index.query.QueryBuilders;
  30. import org.elasticsearch.search.SearchHit;
  31. import org.elasticsearch.search.sort.SortOrder;
  32. import org.sonar.api.ce.ComputeEngineSide;
  33. import org.sonar.api.server.ServerSide;
  34. import org.sonar.api.utils.System2;
  35. import org.sonar.server.es.EsClient;
  36. import org.sonar.server.es.SearchOptions;
  37. import org.sonar.server.es.SearchResult;
  38. import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
  39. import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
  40. import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
  41. import static org.elasticsearch.index.query.QueryBuilders.termQuery;
  42. import static org.sonar.server.es.newindex.DefaultIndexSettingsElement.SORTABLE_ANALYZER;
  43. import static org.sonar.server.es.newindex.DefaultIndexSettingsElement.USER_SEARCH_GRAMS_ANALYZER;
  44. import static org.sonar.server.user.index.UserIndexDefinition.FIELD_ACTIVE;
  45. import static org.sonar.server.user.index.UserIndexDefinition.FIELD_EMAIL;
  46. import static org.sonar.server.user.index.UserIndexDefinition.FIELD_LOGIN;
  47. import static org.sonar.server.user.index.UserIndexDefinition.FIELD_NAME;
  48. import static org.sonar.server.user.index.UserIndexDefinition.FIELD_ORGANIZATION_UUIDS;
  49. import static org.sonar.server.user.index.UserIndexDefinition.FIELD_SCM_ACCOUNTS;
  50. @ServerSide
  51. @ComputeEngineSide
  52. public class UserIndex {
  53. private final EsClient esClient;
  54. private final System2 system2;
  55. public UserIndex(EsClient esClient, System2 system2) {
  56. this.esClient = esClient;
  57. this.system2 = system2;
  58. }
  59. /**
  60. * Returns the active users (at most 3) who are associated to the given SCM account. This method can be used
  61. * to detect user conflicts.
  62. */
  63. public List<UserDoc> getAtMostThreeActiveUsersForScmAccount(String scmAccount) {
  64. List<UserDoc> result = new ArrayList<>();
  65. if (!StringUtils.isEmpty(scmAccount)) {
  66. SearchRequestBuilder request = esClient.prepareSearch(UserIndexDefinition.TYPE_USER)
  67. .setQuery(boolQuery().must(matchAllQuery()).filter(
  68. boolQuery()
  69. .must(termQuery(FIELD_ACTIVE, true))
  70. .should(termQuery(FIELD_LOGIN, scmAccount))
  71. .should(matchQuery(SORTABLE_ANALYZER.subField(FIELD_EMAIL), scmAccount))
  72. .should(matchQuery(SORTABLE_ANALYZER.subField(FIELD_SCM_ACCOUNTS), scmAccount))))
  73. .setSize(3);
  74. for (SearchHit hit : request.get().getHits().getHits()) {
  75. result.add(new UserDoc(hit.getSourceAsMap()));
  76. }
  77. }
  78. return result;
  79. }
  80. public SearchResult<UserDoc> search(UserQuery userQuery, SearchOptions options) {
  81. SearchRequestBuilder request = esClient.prepareSearch(UserIndexDefinition.TYPE_USER)
  82. .setSize(options.getLimit())
  83. .setFrom(options.getOffset())
  84. .addSort(FIELD_NAME, SortOrder.ASC);
  85. BoolQueryBuilder filter = boolQuery().must(termQuery(FIELD_ACTIVE, true));
  86. userQuery.getOrganizationUuid()
  87. .ifPresent(o -> filter.must(termQuery(FIELD_ORGANIZATION_UUIDS, o)));
  88. userQuery.getExcludedOrganizationUuid()
  89. .ifPresent(o -> filter.mustNot(termQuery(FIELD_ORGANIZATION_UUIDS, o)));
  90. QueryBuilder esQuery = matchAllQuery();
  91. Optional<String> textQuery = userQuery.getTextQuery();
  92. if (textQuery.isPresent()) {
  93. esQuery = QueryBuilders.multiMatchQuery(textQuery.get(),
  94. FIELD_LOGIN,
  95. USER_SEARCH_GRAMS_ANALYZER.subField(FIELD_LOGIN),
  96. FIELD_NAME,
  97. USER_SEARCH_GRAMS_ANALYZER.subField(FIELD_NAME),
  98. FIELD_EMAIL,
  99. USER_SEARCH_GRAMS_ANALYZER.subField(FIELD_EMAIL))
  100. .operator(Operator.AND);
  101. }
  102. request.setQuery(boolQuery().must(esQuery).filter(filter));
  103. return new SearchResult<>(request.get(), UserDoc::new, system2.getDefaultTimeZone());
  104. }
  105. }