Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions docs/UBahn_API.postman_collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,58 @@
},
"response": []
},
{
"name": "{{HOST}}/search/skills",
"event": [
{
"listen": "test",
"script": {
"id": "d56349bd-a797-47cf-b6bf-2ecfbd342815",
"exec": [
""
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"type": "text",
"value": "Bearer {{token}}"
},
{
"key": "Content-Type",
"name": "Content-Type",
"type": "text",
"value": "application/json"
}
],
"url": {
"raw": "{{HOST}}/search/userAchievements?organizationId=36ed815b-3da1-49f1-a043-aaed0a4e81ad&keyword=Topcoder",
"host": [
"{{HOST}}"
],
"path": [
"search",
"userAchievements"
],
"query": [
{
"key": "organizationId",
"value": "36ed815b-3da1-49f1-a043-aaed0a4e81ad"
},
{
"key": "keyword",
"value": "Topcoder"
}
]
}
},
"response": []
},
{
"name": "{{HOST}}/search/users",
"event": [
Expand Down
107 changes: 88 additions & 19 deletions src/common/es-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -676,33 +676,53 @@ function hasNonAlphaNumeric (text) {
* @param keyword the search keyword
* @returns array of skillIds
*/
async function searchSkills (keyword) {
async function searchSkills (keyword, skillProviderIds) {
const queryDoc = DOCUMENTS.skill
keyword = escapeRegex(keyword)
const query = hasNonAlphaNumeric(keyword) ? `\\*${keyword}\\*` : `*${keyword}*`

const keywordSearchClause = {
query_string: {
default_field: 'name',
minimum_should_match: '100%',
query
}
}

const searchClause = {
query: {}
}

if (skillProviderIds == null) {
searchClause.query = keywordSearchClause
searchClause._source = 'id'
} else {
searchClause.query = {
bool: {
filter: [{
terms: {
[`${RESOURCE_FILTER.skill.skillProviderId.queryField}.keyword`]: skillProviderIds
}
}],
must: [keywordSearchClause]
}
}
}

const esQuery = {
index: queryDoc.index,
type: queryDoc.type,
body: {
query: {
query_string: {
default_field: 'name',
minimum_should_match: '100%',
query
}
},
_source: 'id'
}
body: searchClause
}

logger.debug(`ES query for searching skills: ${JSON.stringify(esQuery, null, 2)}`)
const results = await esClient.search(esQuery)
return results.hits.hits.map(hit => hit._source.id)

return results.hits.hits.map(hit => hit._source)
}

async function setUserSearchClausesToEsQuery (boolClause, keyword) {
const skillIds = await searchSkills(keyword)
const skillIds = (await searchSkills(keyword)).map(skill => skill.id)
boolClause.should.push({
query_string: {
fields: ['firstName', 'lastName', 'handle'],
Expand Down Expand Up @@ -866,6 +886,27 @@ function buildEsQueryToGetAttributeValues (attributeId, attributeValue, size) {
return esQuery
}

function buildEsQueryToGetSkillProviderIds (organizationId) {
const queryDoc = DOCUMENTS.organization

const esQuery = {
index: queryDoc.index,
type: queryDoc.type,
body: {
size: 1000,
query: {
term: {
'id.keyword': {
value: organizationId
}
}
}
}
}

return esQuery
}

async function resolveUserFilterFromDb (filter, { handle }, organizationId) {
const DBHelper = require('../models/index').DBHelper

Expand Down Expand Up @@ -1391,6 +1432,33 @@ async function searchUsers (authUser, filter, params) {
}
}

/**
* Search for skills matching the given keyword and are part of the given organization
* @param {Object} param0 the organizationId and keyword
*/

async function searchSkillsInOrganization ({ organizationId, keyword }) {
const esQueryToGetSkillProviders = buildEsQueryToGetSkillProviderIds(organizationId)
logger.debug(`ES query to get skill provider ids: ${JSON.stringify(esQueryToGetSkillProviders, null, 2)}`)

const esResultOfQueryToGetSkillProviders = await esClient.search(esQueryToGetSkillProviders)
logger.debug(`ES result: ${JSON.stringify(esResultOfQueryToGetSkillProviders, null, 2)}`)

const skillProviderIds = _.flatten(esResultOfQueryToGetSkillProviders.hits.hits.map(hit => hit._source.skillProviders == null ? [] : hit._source.skillProviders.map(sp => sp.id)))
logger.debug(`Organization ${organizationId} yielded skillProviderIds: ${JSON.stringify(skillProviderIds, null, 2)}`)

const skills = await searchSkills(keyword, skillProviderIds)

return {
result: skills.map(skill => ({
name: skill.name,
skillId: skill.id,
skillProviderId: skill.skillProviderId
// skillProviderName: 'TODO'
}))
}
}

/**
* Searches for matching values for the given attribute value, under the given attribute id
* @param {Object} param0 The attribute id and the attribute value properties
Expand Down Expand Up @@ -1427,19 +1495,19 @@ async function searchAchievementValues ({ organizationId, keyword }) {
const esResult = await esClient.search(esQuery)
logger.debug(`ES response ${JSON.stringify(esResult, null, 2)}`)
const result = esResult.aggregations.achievements.buckets.map(a => {
let achievementName = a.key
const achievementName = a.key
let achievementId = null
for (let achievement of a.ids.hits.hits[0]._source.achievements) {
if (achievement.name == achievementName) {

for (const achievement of a.ids.hits.hits[0]._source.achievements) {
if (achievement.name === achievementName) {
achievementId = achievement.id
break;
break
}
}
return {
id: achievementId,
name: achievementName
};
}
})

return {
Expand All @@ -1451,6 +1519,7 @@ module.exports = {
searchElasticSearch,
getFromElasticSearch,
searchUsers,
searchSkillsInOrganization,
searchAttributeValues,
searchAchievementValues
}
9 changes: 9 additions & 0 deletions src/modules/search/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ async function searchUsers (req, res) {
res.send(result.result)
}

/**
* Search for skills in organization
*/
async function searchSkills (req, res) {
const result = await esHelper.searchSkillsInOrganization(req.query)
res.send(result.result)
}

/**
* Search for attribute values
*/
Expand All @@ -31,6 +39,7 @@ async function searchAchievementValues (req, res) {

module.exports = {
searchUsers,
searchSkills,
searchAttributeValues,
searchAchievementValues
}
8 changes: 8 additions & 0 deletions src/modules/search/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ module.exports = {
scopes: ['read:user', 'all:user']
}
},
'/search/skills': {
get: {
method: Controller.searchSkills
auth: 'jwt',
access: consts.AdminUser,
scopes: ['create:userAttribute', 'all:userAttribute']
}
},
'/search/userAttributes': {
get: {
method: Controller.searchAttributeValues,
Expand Down