[WIP] making a working demo for all possible seekers questions
This commit is contained in:
54
README.md
54
README.md
@@ -8,11 +8,13 @@ This is a **Proof of Concept (PoC)** that demonstrates the conversion of natural
|
||||
|
||||
## Features
|
||||
|
||||
- Converts natural language requests into ODMDB DSL queries
|
||||
- Handles temporal queries ("new seekers since last week")
|
||||
- Maps human-readable field names to schema fields
|
||||
- Validates output using Zod schema validation
|
||||
- Uses OpenAI's structured output for reliable JSON generation
|
||||
- **Natural Language Processing**: Converts human questions into structured ODMDB queries
|
||||
- **Real ODMDB Integration**: Works with actual ODMDB data from `../smatchitObjectOdmdb/`
|
||||
- **Schema-Based Mapping**: Uses actual seekers.json schema for accurate field mapping (62 properties)
|
||||
- **Local Data Execution**: Processes queries against local seeker files in `objects/seekers/itm/`
|
||||
- **OpenAI Structured Output**: Ensures reliable JSON query generation
|
||||
- **Query Validation**: Validates generated queries against real ODMDB schema rules
|
||||
- **jq Integration**: Powerful result processing, filtering, and CSV export capabilities
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -21,13 +23,24 @@ This is a **Proof of Concept (PoC)** that demonstrates the conversion of natural
|
||||
|
||||
## Installation
|
||||
|
||||
1. Install dependencies:
|
||||
1. Make sure you have the ODMDB data structure available:
|
||||
|
||||
```
|
||||
../smatchitObjectOdmdb/
|
||||
├── schema/
|
||||
│ └── seekers.json # Seeker schema (62 properties)
|
||||
└── objects/
|
||||
└── seekers/
|
||||
└── itm/ # Individual seeker JSON files
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Set your OpenAI API key:
|
||||
3. Set your OpenAI API key:
|
||||
```bash
|
||||
export OPENAI_API_KEY=sk-your-api-key-here
|
||||
```
|
||||
@@ -48,10 +61,6 @@ npm start
|
||||
EXECUTE_QUERY=true npm start
|
||||
```
|
||||
|
||||
**With Custom ODMDB Server:**
|
||||
|
||||
```bash
|
||||
EXECUTE_QUERY=true ODMDB_BASE_URL=http://localhost:8080 npm start
|
||||
```
|
||||
|
||||
This will process the hardcoded natural language query and output the generated ODMDB query in JSON format. When `EXECUTE_QUERY=true`, it will also execute the query against the ODMDB server.
|
||||
@@ -67,17 +76,28 @@ const NL_QUERY = "your natural language query here";
|
||||
|
||||
### Example Queries
|
||||
|
||||
**Status-based queries:**
|
||||
|
||||
- `"show me seekers with status startasap and their email and experience"`
|
||||
- `"find seekers looking for jobs urgently with their skills"`
|
||||
|
||||
**Date-based queries:**
|
||||
|
||||
- `"give me new seekers since last week with email and experience"`
|
||||
- `"find recent seekers with job titles and salary expectations"`
|
||||
- `"show me seekers from yesterday with their skills"`
|
||||
|
||||
### Testing jq Processing
|
||||
**Field-specific queries:**
|
||||
|
||||
- `"find seekers with job titles and salary expectations"`
|
||||
- `"show me seeker locations and availability"`
|
||||
|
||||
**Supported filter types:**
|
||||
|
||||
- **Status filtering**: `seekstatus` (startasap, norush, notlooking)
|
||||
- **Date filtering**: `dt_create` with date ranges
|
||||
- **Index optimization**: Uses ODMDB indexes for efficient queries
|
||||
|
||||
To test the jq processing capabilities with mock data:
|
||||
|
||||
```bash
|
||||
node test-jq.js
|
||||
```
|
||||
|
||||
This demonstrates various jq operations including:
|
||||
|
||||
|
101
demo.js
Normal file
101
demo.js
Normal file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Demo script showing different ODMDB query types with real data
|
||||
import fs from "node:fs";
|
||||
|
||||
console.log("🚀 ODMDB NL to Query Demo");
|
||||
console.log("=".repeat(50));
|
||||
|
||||
// Sample queries to demonstrate
|
||||
const queries = [
|
||||
{
|
||||
nl: "show me seekers with status startasap and their email and experience",
|
||||
description: "Status-based filtering with field selection",
|
||||
expectedCondition: "idx.seekstatus_alias(startasap)",
|
||||
expectedFields: ["email", "seekworkingyear"],
|
||||
},
|
||||
{
|
||||
nl: "find seekers looking for jobs urgently with salary expectations",
|
||||
description: "Status synonym mapping + salary field",
|
||||
expectedCondition: "idx.seekstatus_alias(startasap)",
|
||||
expectedFields: ["salaryexpectation", "salaryunit"],
|
||||
},
|
||||
{
|
||||
nl: "give me seekers from last month with their locations",
|
||||
description: "Date-based filtering + location fields",
|
||||
expectedCondition: "prop.dt_create(>=:2025-09-14)",
|
||||
expectedFields: ["seeklocation"],
|
||||
},
|
||||
];
|
||||
|
||||
console.log("📋 Demo Queries:");
|
||||
queries.forEach((query, i) => {
|
||||
console.log(`\n${i + 1}. "${query.nl}"`);
|
||||
console.log(` Purpose: ${query.description}`);
|
||||
console.log(` Expected DSL: ${query.expectedCondition}`);
|
||||
console.log(` Expected Fields: ${query.expectedFields.join(", ")}`);
|
||||
});
|
||||
|
||||
console.log("\n💡 To test these queries:");
|
||||
console.log("1. Edit the NL_QUERY constant in poc.js");
|
||||
console.log("2. Run: EXECUTE_QUERY=true npm start");
|
||||
|
||||
console.log("\n📊 Current ODMDB Status:");
|
||||
|
||||
// Check if ODMDB data is accessible
|
||||
const seekersPath = "../smatchitObjectOdmdb/objects/seekers/itm";
|
||||
try {
|
||||
if (fs.existsSync(seekersPath)) {
|
||||
const files = fs
|
||||
.readdirSync(seekersPath)
|
||||
.filter((f) => f.endsWith(".json") && f !== "backup");
|
||||
console.log(`✅ Found ${files.length} seeker files in ${seekersPath}`);
|
||||
|
||||
// Sample a few files to show data types
|
||||
const sampleFile = files[0];
|
||||
const sampleData = JSON.parse(
|
||||
fs.readFileSync(`${seekersPath}/${sampleFile}`, "utf-8")
|
||||
);
|
||||
console.log(`📄 Sample seeker data (${sampleFile}):`);
|
||||
console.log(` - alias: ${sampleData.alias}`);
|
||||
console.log(` - email: ${sampleData.email}`);
|
||||
console.log(` - seekstatus: ${sampleData.seekstatus}`);
|
||||
console.log(` - seekworkingyear: ${sampleData.seekworkingyear}`);
|
||||
console.log(` - dt_create: ${sampleData.dt_create}`);
|
||||
} else {
|
||||
console.log(`❌ ODMDB data not found at ${seekersPath}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`❌ Error accessing ODMDB data: ${error.message}`);
|
||||
}
|
||||
|
||||
const schemaPath = "../smatchitObjectOdmdb/schema/seekers.json";
|
||||
try {
|
||||
if (fs.existsSync(schemaPath)) {
|
||||
const schema = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
|
||||
const fieldCount = Object.keys(schema.properties || {}).length;
|
||||
console.log(`✅ Loaded seekers schema with ${fieldCount} properties`);
|
||||
|
||||
// Show access rights info
|
||||
if (schema.apxaccessrights?.recruiters?.R) {
|
||||
console.log(
|
||||
`📋 Recruiter-readable fields: ${schema.apxaccessrights.recruiters.R.slice(
|
||||
0,
|
||||
5
|
||||
).join(", ")}... (${schema.apxaccessrights.recruiters.R.length} total)`
|
||||
);
|
||||
}
|
||||
|
||||
// Show available indexes
|
||||
if (schema.apxidx) {
|
||||
const indexes = schema.apxidx.map((idx) => idx.name);
|
||||
console.log(`🔍 Available indexes: ${indexes.join(", ")}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`❌ Schema not found at ${schemaPath}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`❌ Error loading schema: ${error.message}`);
|
||||
}
|
||||
|
||||
console.log("\n✅ Demo complete!");
|
8
poc.js
8
poc.js
@@ -23,7 +23,7 @@ const EXECUTE_QUERY = process.env.EXECUTE_QUERY === "true"; // Set to "true" to
|
||||
|
||||
// Hardcoded NL query for the PoC (no multi-turn)
|
||||
const NL_QUERY =
|
||||
"give me new seekers since last week with email and experience";
|
||||
"show me seekers with status startasap and their email and experience";
|
||||
|
||||
// ---- Load schemas (safe) ----
|
||||
function loadJsonSafe(path) {
|
||||
@@ -603,7 +603,7 @@ async function processResults(results, jqFilter = ".") {
|
||||
console.log("\n📋 Results Summary:");
|
||||
const summary = await processResults(
|
||||
results,
|
||||
`.[0:3] | map({alias, email, seekstatus})`
|
||||
`.[0:3] | map({email, seekworkingyear})`
|
||||
);
|
||||
console.log(JSON.stringify(summary, null, 2));
|
||||
|
||||
@@ -617,8 +617,8 @@ async function processResults(results, jqFilter = ".") {
|
||||
const csvData = await processResults(
|
||||
results,
|
||||
`
|
||||
map([.alias // "N/A", .email // "N/A", .seekstatus // "N/A"]) |
|
||||
["alias","email","status"] as $header |
|
||||
map([.email // "N/A", .seekworkingyear // "N/A"]) |
|
||||
["email","experience"] as $header |
|
||||
[$header] + .[0:5] |
|
||||
.[] | @csv
|
||||
`
|
||||
|
Reference in New Issue
Block a user