[WIP] mappings
This commit is contained in:
209
schema-mappings/base-mapping.js
Normal file
209
schema-mappings/base-mapping.js
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
// Base mapping structure for all ODMDB schemas
|
||||||
|
export const createSchemaMapping = (schemaData, objectName) => {
|
||||||
|
if (!schemaData || !schemaData.properties) {
|
||||||
|
return {
|
||||||
|
objectName,
|
||||||
|
available: false,
|
||||||
|
error: "Schema not found or invalid",
|
||||||
|
properties: {},
|
||||||
|
synonyms: {},
|
||||||
|
indexes: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const properties = schemaData.properties;
|
||||||
|
const synonyms = {};
|
||||||
|
const fieldMappings = {};
|
||||||
|
|
||||||
|
// Generate comprehensive synonyms for each field
|
||||||
|
Object.entries(properties).forEach(([fieldName, fieldDef]) => {
|
||||||
|
const fieldSynonyms = generateFieldSynonyms(
|
||||||
|
fieldName,
|
||||||
|
fieldDef,
|
||||||
|
objectName
|
||||||
|
);
|
||||||
|
fieldMappings[fieldName] = {
|
||||||
|
field: fieldName,
|
||||||
|
title: fieldDef.title?.toLowerCase(),
|
||||||
|
description: fieldDef.description?.toLowerCase(),
|
||||||
|
type: fieldDef.type,
|
||||||
|
synonyms: fieldSynonyms,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Index by synonyms
|
||||||
|
fieldSynonyms.forEach((synonym) => {
|
||||||
|
synonyms[synonym.toLowerCase()] = fieldName;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Index by title
|
||||||
|
if (fieldDef.title) {
|
||||||
|
synonyms[fieldDef.title.toLowerCase()] = fieldName;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Extract indexes if available
|
||||||
|
const indexes = schemaData.apxidx
|
||||||
|
? schemaData.apxidx.map((idx) => ({
|
||||||
|
name: idx.name,
|
||||||
|
type: idx.type,
|
||||||
|
keyval: idx.keyval,
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
|
||||||
|
// Extract access rights if available
|
||||||
|
const accessRights = schemaData.apxaccessrights || {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
objectName,
|
||||||
|
available: true,
|
||||||
|
propertyCount: Object.keys(properties).length,
|
||||||
|
properties: fieldMappings,
|
||||||
|
synonyms,
|
||||||
|
indexes,
|
||||||
|
accessRights,
|
||||||
|
rawSchema: schemaData,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate field-specific synonyms based on field name and context
|
||||||
|
const generateFieldSynonyms = (fieldName, fieldDef, objectName) => {
|
||||||
|
const synonyms = [];
|
||||||
|
|
||||||
|
// Add the field name itself
|
||||||
|
synonyms.push(fieldName);
|
||||||
|
|
||||||
|
// Add title if available
|
||||||
|
if (fieldDef.title) {
|
||||||
|
synonyms.push(fieldDef.title.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common patterns across all objects
|
||||||
|
const commonPatterns = {
|
||||||
|
// Identity & References
|
||||||
|
alias: ["id", "identifier", "username", "user id"],
|
||||||
|
owner: ["owner", "belongs to", "owned by"],
|
||||||
|
|
||||||
|
// Dates & Timestamps
|
||||||
|
dt_create: [
|
||||||
|
"created",
|
||||||
|
"creation date",
|
||||||
|
"new",
|
||||||
|
"recent",
|
||||||
|
"since",
|
||||||
|
"registration date",
|
||||||
|
],
|
||||||
|
dt_update: ["updated", "last update", "modified", "last modified"],
|
||||||
|
dt_publish: ["published", "publication date", "went live"],
|
||||||
|
dt_close: ["closed", "closing date", "ended"],
|
||||||
|
|
||||||
|
// Contact Information
|
||||||
|
email: ["contact", "mail", "contact email", "e-mail"],
|
||||||
|
phone: ["telephone", "phone number", "contact number"],
|
||||||
|
|
||||||
|
// Status & State
|
||||||
|
state: ["status", "condition", "current state"],
|
||||||
|
status: ["state", "condition", "current status"],
|
||||||
|
|
||||||
|
// Location
|
||||||
|
location: ["where", "place", "address", "position"],
|
||||||
|
|
||||||
|
// Descriptions
|
||||||
|
description: ["desc", "details", "info", "about"],
|
||||||
|
shortdescription: ["short desc", "summary", "brief", "overview"],
|
||||||
|
|
||||||
|
// Common business fields
|
||||||
|
siret: ["company id", "business id", "organization id"],
|
||||||
|
sirets: ["companies", "businesses", "organizations"],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Object-specific patterns
|
||||||
|
const objectSpecificPatterns = {
|
||||||
|
seekers: {
|
||||||
|
seekstatus: [
|
||||||
|
"status",
|
||||||
|
"availability",
|
||||||
|
"looking",
|
||||||
|
"job search status",
|
||||||
|
"urgency",
|
||||||
|
],
|
||||||
|
seekworkingyear: [
|
||||||
|
"experience",
|
||||||
|
"years of experience",
|
||||||
|
"work experience",
|
||||||
|
"career length",
|
||||||
|
],
|
||||||
|
seekjobtitleexperience: [
|
||||||
|
"job titles",
|
||||||
|
"positions",
|
||||||
|
"roles",
|
||||||
|
"work history",
|
||||||
|
],
|
||||||
|
salaryexpectation: [
|
||||||
|
"salary",
|
||||||
|
"pay",
|
||||||
|
"compensation",
|
||||||
|
"wage",
|
||||||
|
"expected salary",
|
||||||
|
],
|
||||||
|
seeklocation: [
|
||||||
|
"location",
|
||||||
|
"where",
|
||||||
|
"work location",
|
||||||
|
"preferred location",
|
||||||
|
],
|
||||||
|
skills: ["competencies", "abilities", "technical skills"],
|
||||||
|
mbti: ["personality", "personality type", "MBTI", "profile"],
|
||||||
|
},
|
||||||
|
jobads: {
|
||||||
|
jobadid: ["job id", "ad id", "posting id"],
|
||||||
|
jobtitle: ["job title", "position", "role", "job name"],
|
||||||
|
salary: ["pay", "compensation", "wage", "remuneration"],
|
||||||
|
joblocation: ["job location", "work location", "where"],
|
||||||
|
description: ["job description", "details", "requirements"],
|
||||||
|
state: ["status", "publication status", "availability"],
|
||||||
|
},
|
||||||
|
recruiters: {
|
||||||
|
sirets: ["companies", "businesses", "clients", "employers"],
|
||||||
|
tipsadvice: ["tips", "advice", "articles", "guidance"],
|
||||||
|
},
|
||||||
|
persons: {
|
||||||
|
firstname: ["first name", "given name", "name"],
|
||||||
|
lastname: ["last name", "family name", "surname"],
|
||||||
|
dt_birth: ["birth date", "birthday", "date of birth", "age"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Apply common patterns
|
||||||
|
if (commonPatterns[fieldName]) {
|
||||||
|
synonyms.push(...commonPatterns[fieldName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply object-specific patterns
|
||||||
|
if (
|
||||||
|
objectSpecificPatterns[objectName] &&
|
||||||
|
objectSpecificPatterns[objectName][fieldName]
|
||||||
|
) {
|
||||||
|
synonyms.push(...objectSpecificPatterns[objectName][fieldName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate semantic synonyms based on field name patterns
|
||||||
|
if (fieldName.includes("salary")) {
|
||||||
|
synonyms.push("pay", "compensation", "wage", "remuneration");
|
||||||
|
}
|
||||||
|
if (fieldName.includes("location")) {
|
||||||
|
synonyms.push("where", "place", "address", "position");
|
||||||
|
}
|
||||||
|
if (fieldName.includes("experience")) {
|
||||||
|
synonyms.push("background", "history", "expertise");
|
||||||
|
}
|
||||||
|
if (fieldName.includes("skill")) {
|
||||||
|
synonyms.push("competencies", "abilities", "talents");
|
||||||
|
}
|
||||||
|
if (fieldName.includes("date") || fieldName.startsWith("dt_")) {
|
||||||
|
synonyms.push("when", "time", "timestamp");
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...new Set(synonyms)]; // Remove duplicates
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createSchemaMapping;
|
151
schema-mappings/jobads-mapping.js
Normal file
151
schema-mappings/jobads-mapping.js
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
// JobAds schema mapping - job posting natural language support
|
||||||
|
import { createSchemaMapping } from "./base-mapping.js";
|
||||||
|
import fs from "node:fs";
|
||||||
|
|
||||||
|
const SCHEMA_PATH = "../smatchitObjectOdmdb/schema/jobads.json";
|
||||||
|
|
||||||
|
let jobadsSchema = null;
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(SCHEMA_PATH)) {
|
||||||
|
jobadsSchema = JSON.parse(fs.readFileSync(SCHEMA_PATH, "utf-8"));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Warning: Could not load jobads schema: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const jobadsMapping = createSchemaMapping(jobadsSchema, "jobads");
|
||||||
|
|
||||||
|
// Additional jobads-specific enhancements
|
||||||
|
if (jobadsMapping.available) {
|
||||||
|
const jobadsEnhancements = {
|
||||||
|
// Job Identification
|
||||||
|
"job id": "jobadid",
|
||||||
|
"posting id": "jobadid",
|
||||||
|
"ad id": "jobadid",
|
||||||
|
"advertisement id": "jobadid",
|
||||||
|
"job posting": "jobadid",
|
||||||
|
|
||||||
|
// Job Details
|
||||||
|
"job title": "jobtitle",
|
||||||
|
position: "jobtitle",
|
||||||
|
role: "jobtitle",
|
||||||
|
"job name": "jobtitle",
|
||||||
|
"position title": "jobtitle",
|
||||||
|
"job role": "jobtitle",
|
||||||
|
|
||||||
|
// Job Status & State
|
||||||
|
"job status": "state",
|
||||||
|
"posting status": "state",
|
||||||
|
"publication status": "state",
|
||||||
|
availability: "state",
|
||||||
|
"job state": "state",
|
||||||
|
active: "state",
|
||||||
|
published: "state",
|
||||||
|
draft: "state",
|
||||||
|
archived: "state",
|
||||||
|
|
||||||
|
// Company & Organization
|
||||||
|
company: "siret",
|
||||||
|
employer: "siret",
|
||||||
|
organization: "siret",
|
||||||
|
business: "siret",
|
||||||
|
firm: "siret",
|
||||||
|
|
||||||
|
// Job Compensation
|
||||||
|
salary: "salary",
|
||||||
|
pay: "salary",
|
||||||
|
compensation: "salary",
|
||||||
|
wage: "salary",
|
||||||
|
remuneration: "salary",
|
||||||
|
payment: "salary",
|
||||||
|
|
||||||
|
// Job Location
|
||||||
|
"job location": "joblocation",
|
||||||
|
"work location": "joblocation",
|
||||||
|
workplace: "joblocation",
|
||||||
|
"office location": "joblocation",
|
||||||
|
where: "joblocation",
|
||||||
|
place: "joblocation",
|
||||||
|
|
||||||
|
// Job Description & Requirements
|
||||||
|
"job description": "description",
|
||||||
|
"job details": "description",
|
||||||
|
requirements: "description",
|
||||||
|
responsibilities: "description",
|
||||||
|
duties: "description",
|
||||||
|
"job requirements": "description",
|
||||||
|
"role description": "description",
|
||||||
|
|
||||||
|
// Job Type & Contract
|
||||||
|
"employment type": "jobtype",
|
||||||
|
"contract type": "jobtype",
|
||||||
|
"job type": "jobtype",
|
||||||
|
"work type": "jobtype",
|
||||||
|
"position type": "jobtype",
|
||||||
|
|
||||||
|
// Remote Work
|
||||||
|
"remote work": "remote",
|
||||||
|
"work from home": "remote",
|
||||||
|
telecommute: "remote",
|
||||||
|
"remote job": "remote",
|
||||||
|
"home office": "remote",
|
||||||
|
|
||||||
|
// Dates & Timeline
|
||||||
|
"published date": "dt_publish",
|
||||||
|
"posting date": "dt_publish",
|
||||||
|
"publication date": "dt_publish",
|
||||||
|
"went live": "dt_publish",
|
||||||
|
"closing date": "dt_close",
|
||||||
|
deadline: "dt_close",
|
||||||
|
"application deadline": "dt_close",
|
||||||
|
expires: "dt_close",
|
||||||
|
|
||||||
|
// Skills & Qualifications
|
||||||
|
"required skills": "skills",
|
||||||
|
qualifications: "skills",
|
||||||
|
competencies: "skills",
|
||||||
|
"abilities required": "skills",
|
||||||
|
"expertise needed": "skills",
|
||||||
|
|
||||||
|
// Experience Requirements
|
||||||
|
"experience required": "experiencerequired",
|
||||||
|
"years of experience": "experiencerequired",
|
||||||
|
"work experience": "experiencerequired",
|
||||||
|
"professional experience": "experiencerequired",
|
||||||
|
|
||||||
|
// Education Requirements
|
||||||
|
"education required": "education",
|
||||||
|
"degree required": "education",
|
||||||
|
"qualification needed": "education",
|
||||||
|
"educational background": "education",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Merge enhancements into synonyms
|
||||||
|
Object.entries(jobadsEnhancements).forEach(([synonym, fieldName]) => {
|
||||||
|
jobadsMapping.synonyms[synonym.toLowerCase()] = fieldName;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add state value mappings
|
||||||
|
jobadsMapping.statusValues = {
|
||||||
|
state: {
|
||||||
|
active: "publish",
|
||||||
|
published: "publish",
|
||||||
|
live: "publish",
|
||||||
|
online: "publish",
|
||||||
|
available: "publish",
|
||||||
|
draft: "draft",
|
||||||
|
unpublished: "draft",
|
||||||
|
"in progress": "draft",
|
||||||
|
ready: "ready",
|
||||||
|
pending: "ready",
|
||||||
|
waiting: "ready",
|
||||||
|
closed: "archive",
|
||||||
|
archived: "archive",
|
||||||
|
expired: "archive",
|
||||||
|
inactive: "archive",
|
||||||
|
ended: "archive",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default jobadsMapping;
|
412
schema-mappings/mapping-manager.js
Normal file
412
schema-mappings/mapping-manager.js
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
// Comprehensive ODMDB Schema Mapping Manager
|
||||||
|
// Handles all objects, detects data availability, and provides intelligent query routing
|
||||||
|
|
||||||
|
import fs from "node:fs";
|
||||||
|
import { seekersMapping } from "./seekers-mapping.js";
|
||||||
|
import { jobadsMapping } from "./jobads-mapping.js";
|
||||||
|
import { recruitersMapping } from "./recruiters-mapping.js";
|
||||||
|
import { personsMapping } from "./persons-mapping.js";
|
||||||
|
import { createSchemaMapping } from "./base-mapping.js";
|
||||||
|
|
||||||
|
const SCHEMA_BASE_PATH = "../smatchitObjectOdmdb/schema";
|
||||||
|
const OBJECTS_BASE_PATH = "../smatchitObjectOdmdb/objects";
|
||||||
|
|
||||||
|
class ODMDBMappingManager {
|
||||||
|
constructor() {
|
||||||
|
this.mappings = new Map();
|
||||||
|
this.dataAvailability = new Map();
|
||||||
|
this.loadAllMappings();
|
||||||
|
this.checkDataAvailability();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAllMappings() {
|
||||||
|
// Load primary mappings (with custom enhancements)
|
||||||
|
this.mappings.set("seekers", seekersMapping);
|
||||||
|
this.mappings.set("jobads", jobadsMapping);
|
||||||
|
this.mappings.set("recruiters", recruitersMapping);
|
||||||
|
this.mappings.set("persons", personsMapping);
|
||||||
|
|
||||||
|
// Load remaining schemas dynamically
|
||||||
|
const remainingSchemas = [
|
||||||
|
"jobsteps",
|
||||||
|
"jobtitles",
|
||||||
|
"quizz",
|
||||||
|
"screens",
|
||||||
|
"sirets",
|
||||||
|
"trainingprovider",
|
||||||
|
"trainings",
|
||||||
|
];
|
||||||
|
|
||||||
|
remainingSchemas.forEach((schemaName) => {
|
||||||
|
const schemaPath = `${SCHEMA_BASE_PATH}/${schemaName}.json`;
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(schemaPath)) {
|
||||||
|
const schemaData = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
|
||||||
|
const mapping = createSchemaMapping(schemaData, schemaName);
|
||||||
|
this.mappings.set(schemaName, mapping);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
`Warning: Could not load ${schemaName} schema: ${error.message}`
|
||||||
|
);
|
||||||
|
this.mappings.set(schemaName, {
|
||||||
|
objectName: schemaName,
|
||||||
|
available: false,
|
||||||
|
error: error.message,
|
||||||
|
properties: {},
|
||||||
|
synonyms: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`🗺️ Loaded ${this.mappings.size} object mappings`);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDataAvailability() {
|
||||||
|
// Check which objects have actual data files
|
||||||
|
this.mappings.forEach((mapping, objectName) => {
|
||||||
|
const objectPath = `${OBJECTS_BASE_PATH}/${objectName}`;
|
||||||
|
const itemsPath = `${objectPath}/itm`;
|
||||||
|
|
||||||
|
let availability = {
|
||||||
|
schemaAvailable: mapping.available,
|
||||||
|
dataAvailable: false,
|
||||||
|
dataPath: itemsPath,
|
||||||
|
fileCount: 0,
|
||||||
|
sampleFiles: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(itemsPath)) {
|
||||||
|
const files = fs
|
||||||
|
.readdirSync(itemsPath)
|
||||||
|
.filter((f) => f.endsWith(".json") && f !== "backup")
|
||||||
|
.filter((f) => !fs.statSync(`${itemsPath}/${f}`).isDirectory());
|
||||||
|
|
||||||
|
availability.dataAvailable = files.length > 0;
|
||||||
|
availability.fileCount = files.length;
|
||||||
|
availability.sampleFiles = files.slice(0, 3); // First 3 files as samples
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
`Warning: Could not check data for ${objectName}: ${error.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataAvailability.set(objectName, availability);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Log availability summary
|
||||||
|
const availableObjects = Array.from(this.dataAvailability.entries())
|
||||||
|
.filter(([_, availability]) => availability.dataAvailable)
|
||||||
|
.map(
|
||||||
|
([objectName, availability]) =>
|
||||||
|
`${objectName}(${availability.fileCount})`
|
||||||
|
)
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
console.log(`📊 Data available for: ${availableObjects}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intelligent object detection from natural language
|
||||||
|
detectObjectFromQuery(nlQuery) {
|
||||||
|
const query = nlQuery.toLowerCase();
|
||||||
|
const detectedObjects = [];
|
||||||
|
|
||||||
|
// Direct object name mentions
|
||||||
|
this.mappings.forEach((mapping, objectName) => {
|
||||||
|
if (
|
||||||
|
query.includes(objectName) ||
|
||||||
|
query.includes(objectName.slice(0, -1))
|
||||||
|
) {
|
||||||
|
// singular form
|
||||||
|
detectedObjects.push({
|
||||||
|
object: objectName,
|
||||||
|
confidence: 0.9,
|
||||||
|
reason: `Direct mention of '${objectName}'`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Semantic object detection
|
||||||
|
const objectIndicators = {
|
||||||
|
seekers: [
|
||||||
|
"seekers",
|
||||||
|
"seeker",
|
||||||
|
"job seekers",
|
||||||
|
"candidates",
|
||||||
|
"applicants",
|
||||||
|
"people looking for jobs",
|
||||||
|
"job hunters",
|
||||||
|
"looking for work",
|
||||||
|
"experience",
|
||||||
|
"skills",
|
||||||
|
"salary expectation",
|
||||||
|
"availability",
|
||||||
|
],
|
||||||
|
jobads: [
|
||||||
|
"jobs",
|
||||||
|
"job postings",
|
||||||
|
"job ads",
|
||||||
|
"positions",
|
||||||
|
"openings",
|
||||||
|
"vacancies",
|
||||||
|
"employment opportunities",
|
||||||
|
"job offers",
|
||||||
|
"job description",
|
||||||
|
"job requirements",
|
||||||
|
"salary range",
|
||||||
|
],
|
||||||
|
recruiters: [
|
||||||
|
"recruiters",
|
||||||
|
"recruiter",
|
||||||
|
"hiring managers",
|
||||||
|
"hr",
|
||||||
|
"employers",
|
||||||
|
"hiring",
|
||||||
|
"recruitment",
|
||||||
|
"talent acquisition",
|
||||||
|
"headhunters",
|
||||||
|
],
|
||||||
|
persons: [
|
||||||
|
"people",
|
||||||
|
"users",
|
||||||
|
"profiles",
|
||||||
|
"personal information",
|
||||||
|
"contact details",
|
||||||
|
"names",
|
||||||
|
"demographics",
|
||||||
|
"biography",
|
||||||
|
],
|
||||||
|
sirets: [
|
||||||
|
"companies",
|
||||||
|
"businesses",
|
||||||
|
"organizations",
|
||||||
|
"employers",
|
||||||
|
"firms",
|
||||||
|
"corporations",
|
||||||
|
"enterprises",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.entries(objectIndicators).forEach(([objectName, indicators]) => {
|
||||||
|
const matches = indicators.filter((indicator) =>
|
||||||
|
query.includes(indicator)
|
||||||
|
);
|
||||||
|
if (matches.length > 0) {
|
||||||
|
const confidence = Math.min(0.8, matches.length * 0.3);
|
||||||
|
detectedObjects.push({
|
||||||
|
object: objectName,
|
||||||
|
confidence,
|
||||||
|
reason: `Semantic match: ${matches.join(", ")}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort by confidence and remove duplicates
|
||||||
|
const uniqueObjects = detectedObjects.reduce((acc, current) => {
|
||||||
|
const existing = acc.find((item) => item.object === current.object);
|
||||||
|
if (!existing || current.confidence > existing.confidence) {
|
||||||
|
acc = acc.filter((item) => item.object !== current.object);
|
||||||
|
acc.push(current);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return uniqueObjects.sort((a, b) => b.confidence - a.confidence);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get data availability statistics
|
||||||
|
getDataAvailabilityStats() {
|
||||||
|
const availableObjects = [];
|
||||||
|
const objectStats = {};
|
||||||
|
|
||||||
|
for (const [objectType, mapping] of Object.entries(this.mappings)) {
|
||||||
|
if (mapping.available) {
|
||||||
|
availableObjects.push(objectType);
|
||||||
|
objectStats[objectType] = mapping.dataStats.fileCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const summary = availableObjects
|
||||||
|
.map((obj) => `${obj}(${objectStats[obj]})`)
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
return {
|
||||||
|
availableObjects,
|
||||||
|
objectStats,
|
||||||
|
summary,
|
||||||
|
totalObjects: availableObjects.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a query is feasible given available data
|
||||||
|
validateQueryFeasibility(nlQuery, suggestedObject = null) {
|
||||||
|
const detectedObjects = suggestedObject
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
object: suggestedObject,
|
||||||
|
confidence: 1.0,
|
||||||
|
reason: "Explicitly specified",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: this.detectObjectFromQuery(nlQuery);
|
||||||
|
|
||||||
|
if (detectedObjects.length === 0) {
|
||||||
|
return {
|
||||||
|
feasible: false,
|
||||||
|
reason: "Cannot determine which object type this query refers to",
|
||||||
|
suggestion:
|
||||||
|
"Please specify if you're looking for seekers, jobs, recruiters, or companies",
|
||||||
|
availableObjects: Array.from(this.dataAvailability.keys()).filter(
|
||||||
|
(obj) => this.dataAvailability.get(obj).dataAvailable
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const primaryObject = detectedObjects[0];
|
||||||
|
const availability = this.dataAvailability.get(primaryObject.object);
|
||||||
|
|
||||||
|
if (!availability) {
|
||||||
|
return {
|
||||||
|
feasible: false,
|
||||||
|
reason: `Unknown object type: ${primaryObject.object}`,
|
||||||
|
suggestion: `Available objects: ${Array.from(this.mappings.keys()).join(
|
||||||
|
", "
|
||||||
|
)}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!availability.schemaAvailable) {
|
||||||
|
return {
|
||||||
|
feasible: false,
|
||||||
|
reason: `Schema not available for ${primaryObject.object}`,
|
||||||
|
suggestion: `Cannot process queries for ${primaryObject.object} - schema missing`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!availability.dataAvailable) {
|
||||||
|
return {
|
||||||
|
feasible: false,
|
||||||
|
reason: `No data available for ${primaryObject.object}`,
|
||||||
|
suggestion: `${
|
||||||
|
primaryObject.object
|
||||||
|
} schema exists but no data files found. Available data: ${Array.from(
|
||||||
|
this.dataAvailability.entries()
|
||||||
|
)
|
||||||
|
.filter(([_, avail]) => avail.dataAvailable)
|
||||||
|
.map(([name, avail]) => `${name}(${avail.fileCount})`)
|
||||||
|
.join(", ")}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if requested fields exist
|
||||||
|
const mapping = this.mappings.get(primaryObject.object);
|
||||||
|
const queryWords = nlQuery.toLowerCase().split(/\s+/);
|
||||||
|
const unmappedWords = [];
|
||||||
|
|
||||||
|
queryWords.forEach((word) => {
|
||||||
|
if (
|
||||||
|
word.length > 2 && // Skip short words
|
||||||
|
!mapping.synonyms[word] &&
|
||||||
|
!Object.keys(mapping.properties).includes(word) &&
|
||||||
|
![
|
||||||
|
"show",
|
||||||
|
"get",
|
||||||
|
"find",
|
||||||
|
"with",
|
||||||
|
"their",
|
||||||
|
"and",
|
||||||
|
"the",
|
||||||
|
"me",
|
||||||
|
"all",
|
||||||
|
].includes(word)
|
||||||
|
) {
|
||||||
|
unmappedWords.push(word);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
feasible: true,
|
||||||
|
primaryObject,
|
||||||
|
detectedObjects,
|
||||||
|
dataStats: {
|
||||||
|
fileCount: availability.fileCount,
|
||||||
|
sampleFiles: availability.sampleFiles,
|
||||||
|
},
|
||||||
|
fieldWarnings:
|
||||||
|
unmappedWords.length > 0
|
||||||
|
? `Some terms might not map to fields: ${unmappedWords.join(", ")}`
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get mapping for a specific object
|
||||||
|
getMapping(objectName) {
|
||||||
|
return this.mappings.get(objectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all available objects with data
|
||||||
|
getAvailableObjects() {
|
||||||
|
return Array.from(this.dataAvailability.entries())
|
||||||
|
.filter(
|
||||||
|
([_, availability]) =>
|
||||||
|
availability.dataAvailable && availability.schemaAvailable
|
||||||
|
)
|
||||||
|
.map(([objectName, availability]) => ({
|
||||||
|
object: objectName,
|
||||||
|
fileCount: availability.fileCount,
|
||||||
|
propertyCount: this.mappings.get(objectName)?.propertyCount || 0,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get comprehensive field suggestions for an object
|
||||||
|
getFieldSuggestions(objectName, queryTerms = []) {
|
||||||
|
const mapping = this.getMapping(objectName);
|
||||||
|
if (!mapping || !mapping.available) return [];
|
||||||
|
|
||||||
|
const suggestions = [];
|
||||||
|
|
||||||
|
// Find fields that match query terms
|
||||||
|
queryTerms.forEach((term) => {
|
||||||
|
const field = mapping.synonyms[term.toLowerCase()];
|
||||||
|
if (field) {
|
||||||
|
const fieldInfo = mapping.properties[field];
|
||||||
|
suggestions.push({
|
||||||
|
field,
|
||||||
|
matchedTerm: term,
|
||||||
|
title: fieldInfo.title,
|
||||||
|
type: fieldInfo.type,
|
||||||
|
synonyms: fieldInfo.synonyms.slice(0, 3), // Top 3 synonyms
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return suggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate intelligent error messages with suggestions
|
||||||
|
generateErrorMessage(nlQuery, error) {
|
||||||
|
const feasibility = this.validateQueryFeasibility(nlQuery);
|
||||||
|
|
||||||
|
if (!feasibility.feasible) {
|
||||||
|
return {
|
||||||
|
error: feasibility.reason,
|
||||||
|
suggestion: feasibility.suggestion,
|
||||||
|
availableObjects:
|
||||||
|
feasibility.availableObjects || this.getAvailableObjects(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
error: error.message || "Unknown error",
|
||||||
|
suggestion: "Query seems valid but processing failed",
|
||||||
|
queryAnalysis: feasibility,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export class and singleton instance
|
||||||
|
export { ODMDBMappingManager };
|
||||||
|
export const odmdbMappingManager = new ODMDBMappingManager();
|
||||||
|
export default ODMDBMappingManager;
|
104
schema-mappings/persons-mapping.js
Normal file
104
schema-mappings/persons-mapping.js
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
// Persons schema mapping - person profile natural language support
|
||||||
|
import { createSchemaMapping } from "./base-mapping.js";
|
||||||
|
import fs from "node:fs";
|
||||||
|
|
||||||
|
const SCHEMA_PATH = "../smatchitObjectOdmdb/schema/persons.json";
|
||||||
|
|
||||||
|
let personsSchema = null;
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(SCHEMA_PATH)) {
|
||||||
|
personsSchema = JSON.parse(fs.readFileSync(SCHEMA_PATH, "utf-8"));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Warning: Could not load persons schema: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const personsMapping = createSchemaMapping(personsSchema, "persons");
|
||||||
|
|
||||||
|
// Additional persons-specific enhancements
|
||||||
|
if (personsMapping.available) {
|
||||||
|
const personsEnhancements = {
|
||||||
|
// Personal Information
|
||||||
|
"first name": "firstname",
|
||||||
|
"given name": "firstname",
|
||||||
|
name: "firstname",
|
||||||
|
"last name": "lastname",
|
||||||
|
"family name": "lastname",
|
||||||
|
surname: "lastname",
|
||||||
|
"full name": "fullname",
|
||||||
|
"display name": "fullname",
|
||||||
|
|
||||||
|
// Demographics
|
||||||
|
"birth date": "dt_birth",
|
||||||
|
birthday: "dt_birth",
|
||||||
|
"date of birth": "dt_birth",
|
||||||
|
age: "dt_birth",
|
||||||
|
born: "dt_birth",
|
||||||
|
gender: "pronom",
|
||||||
|
pronouns: "pronom",
|
||||||
|
|
||||||
|
// Contact & Communication
|
||||||
|
"personal email": "emailcom",
|
||||||
|
"communication email": "emailcom",
|
||||||
|
"contact email": "emailcom",
|
||||||
|
"email address": "emailcom",
|
||||||
|
|
||||||
|
// Profile Information
|
||||||
|
biography: "biography",
|
||||||
|
bio: "biography",
|
||||||
|
about: "biography",
|
||||||
|
description: "biography",
|
||||||
|
"personal story": "biography",
|
||||||
|
background: "biography",
|
||||||
|
|
||||||
|
hobbies: "hobbies",
|
||||||
|
interests: "hobbies",
|
||||||
|
activities: "hobbies",
|
||||||
|
pastimes: "hobbies",
|
||||||
|
leisure: "hobbies",
|
||||||
|
|
||||||
|
// Visual Profile
|
||||||
|
"profile picture": "imgavatar",
|
||||||
|
avatar: "imgavatar",
|
||||||
|
photo: "imgavatar",
|
||||||
|
image: "imgavatar",
|
||||||
|
picture: "imgavatar",
|
||||||
|
|
||||||
|
// Access & Privacy
|
||||||
|
"profile access": "profilaccess",
|
||||||
|
"privacy settings": "profilaccess",
|
||||||
|
visibility: "profilaccess",
|
||||||
|
"profile visibility": "profilaccess",
|
||||||
|
|
||||||
|
// Activity & Status
|
||||||
|
"last login": "last_login",
|
||||||
|
"last active": "last_login",
|
||||||
|
"last seen": "last_login",
|
||||||
|
"login time": "last_login",
|
||||||
|
|
||||||
|
// Account Information
|
||||||
|
"account created": "dt_create",
|
||||||
|
registration: "dt_create",
|
||||||
|
joined: "dt_create",
|
||||||
|
"sign up": "dt_create",
|
||||||
|
"profile updated": "dt_update",
|
||||||
|
"last modified": "dt_update",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Merge enhancements into synonyms
|
||||||
|
Object.entries(personsEnhancements).forEach(([synonym, fieldName]) => {
|
||||||
|
personsMapping.synonyms[synonym.toLowerCase()] = fieldName;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add persons-specific context
|
||||||
|
personsMapping.context = {
|
||||||
|
description: "Personal profile information for users in the system",
|
||||||
|
primaryData: ["identity", "contact", "demographics", "profile"],
|
||||||
|
relationships: {
|
||||||
|
seekers: "Person who is seeking employment",
|
||||||
|
recruiters: "Person who is recruiting for companies",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default personsMapping;
|
118
schema-mappings/recruiters-mapping.js
Normal file
118
schema-mappings/recruiters-mapping.js
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
// Recruiters schema mapping - recruiter natural language support
|
||||||
|
import { createSchemaMapping } from "./base-mapping.js";
|
||||||
|
import fs from "node:fs";
|
||||||
|
|
||||||
|
const SCHEMA_PATH = "../smatchitObjectOdmdb/schema/recruiters.json";
|
||||||
|
|
||||||
|
let recruitersSchema = null;
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(SCHEMA_PATH)) {
|
||||||
|
recruitersSchema = JSON.parse(fs.readFileSync(SCHEMA_PATH, "utf-8"));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Warning: Could not load recruiters schema: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const recruitersMapping = createSchemaMapping(
|
||||||
|
recruitersSchema,
|
||||||
|
"recruiters"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Additional recruiters-specific enhancements
|
||||||
|
if (recruitersMapping.available) {
|
||||||
|
const recruitersEnhancements = {
|
||||||
|
// Identity & Contact
|
||||||
|
"recruiter id": "alias",
|
||||||
|
"recruiter name": "alias",
|
||||||
|
"hr id": "alias",
|
||||||
|
"hiring manager": "alias",
|
||||||
|
|
||||||
|
// Contact Information
|
||||||
|
"contact email": "email",
|
||||||
|
"recruiter email": "email",
|
||||||
|
"hiring email": "email",
|
||||||
|
"hr email": "email",
|
||||||
|
"contact phone": "phone",
|
||||||
|
"recruiter phone": "phone",
|
||||||
|
telephone: "phone",
|
||||||
|
"phone number": "phone",
|
||||||
|
|
||||||
|
// Company Associations
|
||||||
|
companies: "sirets",
|
||||||
|
employers: "sirets",
|
||||||
|
clients: "sirets",
|
||||||
|
businesses: "sirets",
|
||||||
|
organizations: "sirets",
|
||||||
|
"company list": "sirets",
|
||||||
|
"employer list": "sirets",
|
||||||
|
|
||||||
|
// Professional Information
|
||||||
|
tips: "tipsadvice",
|
||||||
|
advice: "tipsadvice",
|
||||||
|
articles: "tipsadvice",
|
||||||
|
guidance: "tipsadvice",
|
||||||
|
recommendations: "tipsadvice",
|
||||||
|
"help articles": "tipsadvice",
|
||||||
|
|
||||||
|
// Activity & Dates
|
||||||
|
joined: "dt_create",
|
||||||
|
registration: "dt_create",
|
||||||
|
"sign up": "dt_create",
|
||||||
|
"account created": "dt_create",
|
||||||
|
"last updated": "dt_update",
|
||||||
|
"profile updated": "dt_update",
|
||||||
|
modified: "dt_update",
|
||||||
|
|
||||||
|
// Status & Activity
|
||||||
|
"active recruiter": "status",
|
||||||
|
"recruiter status": "status",
|
||||||
|
"hiring status": "status",
|
||||||
|
availability: "status",
|
||||||
|
|
||||||
|
// Job Management
|
||||||
|
"job postings": "jobads",
|
||||||
|
"job ads": "jobads",
|
||||||
|
postings: "jobads",
|
||||||
|
advertisements: "jobads",
|
||||||
|
vacancies: "jobads",
|
||||||
|
openings: "jobads",
|
||||||
|
|
||||||
|
// Recruitment Activity
|
||||||
|
candidates: "candidates",
|
||||||
|
applicants: "applicants",
|
||||||
|
seekers: "seekers",
|
||||||
|
prospects: "prospects",
|
||||||
|
"talent pool": "candidates",
|
||||||
|
|
||||||
|
// Performance & Metrics
|
||||||
|
placements: "placements",
|
||||||
|
hires: "hires",
|
||||||
|
"successful hires": "placements",
|
||||||
|
"recruitment success": "placements",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Merge enhancements into synonyms
|
||||||
|
Object.entries(recruitersEnhancements).forEach(([synonym, fieldName]) => {
|
||||||
|
recruitersMapping.synonyms[synonym.toLowerCase()] = fieldName;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add recruiter-specific context
|
||||||
|
recruitersMapping.context = {
|
||||||
|
description:
|
||||||
|
"Recruiters create and manage job postings, recruit for companies (sirets), and manage the hiring process",
|
||||||
|
primaryActions: [
|
||||||
|
"create jobads",
|
||||||
|
"manage candidates",
|
||||||
|
"process applications",
|
||||||
|
"schedule interviews",
|
||||||
|
],
|
||||||
|
relationships: {
|
||||||
|
sirets: "Companies the recruiter works for",
|
||||||
|
jobads: "Job postings created by this recruiter",
|
||||||
|
candidates: "People being recruited",
|
||||||
|
seekers: "Job seekers in recruitment process",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default recruitersMapping;
|
134
schema-mappings/seekers-mapping.js
Normal file
134
schema-mappings/seekers-mapping.js
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
// Seekers schema mapping - comprehensive natural language support
|
||||||
|
import { createSchemaMapping } from "./base-mapping.js";
|
||||||
|
import fs from "node:fs";
|
||||||
|
|
||||||
|
const SCHEMA_PATH = "../smatchitObjectOdmdb/schema/seekers.json";
|
||||||
|
|
||||||
|
let seekersSchema = null;
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(SCHEMA_PATH)) {
|
||||||
|
seekersSchema = JSON.parse(fs.readFileSync(SCHEMA_PATH, "utf-8"));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Warning: Could not load seekers schema: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const seekersMapping = createSchemaMapping(seekersSchema, "seekers");
|
||||||
|
|
||||||
|
// Additional seeker-specific enhancements
|
||||||
|
if (seekersMapping.available) {
|
||||||
|
// Add seeker-specific synonym enhancements
|
||||||
|
const seekerEnhancements = {
|
||||||
|
// Work & Career
|
||||||
|
"work experience": "seekworkingyear",
|
||||||
|
"career experience": "seekworkingyear",
|
||||||
|
"professional experience": "seekworkingyear",
|
||||||
|
"years working": "seekworkingyear",
|
||||||
|
"job experience": "seekjobtitleexperience",
|
||||||
|
"previous jobs": "seekjobtitleexperience",
|
||||||
|
"work history": "seekjobtitleexperience",
|
||||||
|
"positions held": "seekjobtitleexperience",
|
||||||
|
|
||||||
|
// Job Search Status
|
||||||
|
urgency: "seekstatus",
|
||||||
|
"how fast": "seekstatus",
|
||||||
|
"job urgency": "seekstatus",
|
||||||
|
"looking urgently": "seekstatus",
|
||||||
|
"need job quickly": "seekstatus",
|
||||||
|
|
||||||
|
// Compensation
|
||||||
|
"expected salary": "salaryexpectation",
|
||||||
|
"salary expectation": "salaryexpectation",
|
||||||
|
"desired salary": "salaryexpectation",
|
||||||
|
"target salary": "salaryexpectation",
|
||||||
|
"pay expectation": "salaryexpectation",
|
||||||
|
"wage expectation": "salaryexpectation",
|
||||||
|
|
||||||
|
// Location preferences
|
||||||
|
"work location": "seeklocation",
|
||||||
|
"job location": "seeklocation",
|
||||||
|
"where to work": "seeklocation",
|
||||||
|
"preferred location": "seeklocation",
|
||||||
|
"work geography": "seeklocation",
|
||||||
|
|
||||||
|
// Skills & Abilities
|
||||||
|
"technical skills": "skills",
|
||||||
|
"professional skills": "skills",
|
||||||
|
competencies: "skills",
|
||||||
|
abilities: "skills",
|
||||||
|
expertise: "skills",
|
||||||
|
languages: "languageskills",
|
||||||
|
"language abilities": "languageskills",
|
||||||
|
"linguistic skills": "languageskills",
|
||||||
|
|
||||||
|
// Personality & Profile
|
||||||
|
"personality type": "mbti",
|
||||||
|
"personality profile": "mbti",
|
||||||
|
"MBTI type": "mbti",
|
||||||
|
"psychological profile": "mbti",
|
||||||
|
|
||||||
|
// Job Preferences
|
||||||
|
"job type": "seekjobtype",
|
||||||
|
"employment type": "seekjobtype",
|
||||||
|
"contract type": "seekjobtype",
|
||||||
|
"work type": "seekjobtype",
|
||||||
|
|
||||||
|
// Availability & Schedule
|
||||||
|
"working hours": "preferedworkinghours",
|
||||||
|
"work schedule": "preferedworkinghours",
|
||||||
|
"preferred hours": "preferedworkinghours",
|
||||||
|
"schedule preference": "preferedworkinghours",
|
||||||
|
"not available": "notavailabletowork",
|
||||||
|
unavailable: "notavailabletowork",
|
||||||
|
"blocked times": "notavailabletowork",
|
||||||
|
|
||||||
|
// Job Search Activity
|
||||||
|
"job applications": "jobadapply",
|
||||||
|
"applied jobs": "jobadapply",
|
||||||
|
"applications sent": "jobadapply",
|
||||||
|
"bookmarked jobs": "jobadsaved",
|
||||||
|
"saved jobs": "jobadsaved",
|
||||||
|
"favorite jobs": "jobadsaved",
|
||||||
|
"job invitations": "jobadinvitedtoapply",
|
||||||
|
"invited to apply": "jobadinvitedtoapply",
|
||||||
|
|
||||||
|
// Education & Training
|
||||||
|
education: "educations",
|
||||||
|
degree: "educations",
|
||||||
|
qualifications: "educations",
|
||||||
|
diploma: "educations",
|
||||||
|
studies: "educations",
|
||||||
|
|
||||||
|
// Communication preferences
|
||||||
|
notifications: "notificationformatches",
|
||||||
|
alerts: "notificationformatches",
|
||||||
|
"email preferences": "emailactivityreportweekly",
|
||||||
|
newsletter: "emailnewsletter",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Merge enhancements into synonyms
|
||||||
|
Object.entries(seekerEnhancements).forEach(([synonym, fieldName]) => {
|
||||||
|
seekersMapping.synonyms[synonym.toLowerCase()] = fieldName;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add status value mappings
|
||||||
|
seekersMapping.statusValues = {
|
||||||
|
seekstatus: {
|
||||||
|
urgent: "startasap",
|
||||||
|
urgently: "startasap",
|
||||||
|
asap: "startasap",
|
||||||
|
quickly: "startasap",
|
||||||
|
immediately: "startasap",
|
||||||
|
fast: "startasap",
|
||||||
|
"no rush": "norush",
|
||||||
|
"taking time": "norush",
|
||||||
|
leisurely: "norush",
|
||||||
|
"not urgent": "norush",
|
||||||
|
"not looking": "notlooking",
|
||||||
|
"not active": "notlooking",
|
||||||
|
inactive: "notlooking",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default seekersMapping;
|
Reference in New Issue
Block a user