[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
|
## Features
|
||||||
|
|
||||||
- Converts natural language requests into ODMDB DSL queries
|
- **Natural Language Processing**: Converts human questions into structured ODMDB queries
|
||||||
- Handles temporal queries ("new seekers since last week")
|
- **Real ODMDB Integration**: Works with actual ODMDB data from `../smatchitObjectOdmdb/`
|
||||||
- Maps human-readable field names to schema fields
|
- **Schema-Based Mapping**: Uses actual seekers.json schema for accurate field mapping (62 properties)
|
||||||
- Validates output using Zod schema validation
|
- **Local Data Execution**: Processes queries against local seeker files in `objects/seekers/itm/`
|
||||||
- Uses OpenAI's structured output for reliable JSON generation
|
- **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
|
## Prerequisites
|
||||||
|
|
||||||
@@ -21,13 +23,24 @@ This is a **Proof of Concept (PoC)** that demonstrates the conversion of natural
|
|||||||
|
|
||||||
## Installation
|
## 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
|
```bash
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Set your OpenAI API key:
|
3. Set your OpenAI API key:
|
||||||
```bash
|
```bash
|
||||||
export OPENAI_API_KEY=sk-your-api-key-here
|
export OPENAI_API_KEY=sk-your-api-key-here
|
||||||
```
|
```
|
||||||
@@ -48,10 +61,6 @@ npm start
|
|||||||
EXECUTE_QUERY=true 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.
|
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
|
### 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"`
|
- `"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"`
|
- `"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:
|
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)
|
// Hardcoded NL query for the PoC (no multi-turn)
|
||||||
const NL_QUERY =
|
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) ----
|
// ---- Load schemas (safe) ----
|
||||||
function loadJsonSafe(path) {
|
function loadJsonSafe(path) {
|
||||||
@@ -603,7 +603,7 @@ async function processResults(results, jqFilter = ".") {
|
|||||||
console.log("\n📋 Results Summary:");
|
console.log("\n📋 Results Summary:");
|
||||||
const summary = await processResults(
|
const summary = await processResults(
|
||||||
results,
|
results,
|
||||||
`.[0:3] | map({alias, email, seekstatus})`
|
`.[0:3] | map({email, seekworkingyear})`
|
||||||
);
|
);
|
||||||
console.log(JSON.stringify(summary, null, 2));
|
console.log(JSON.stringify(summary, null, 2));
|
||||||
|
|
||||||
@@ -617,8 +617,8 @@ async function processResults(results, jqFilter = ".") {
|
|||||||
const csvData = await processResults(
|
const csvData = await processResults(
|
||||||
results,
|
results,
|
||||||
`
|
`
|
||||||
map([.alias // "N/A", .email // "N/A", .seekstatus // "N/A"]) |
|
map([.email // "N/A", .seekworkingyear // "N/A"]) |
|
||||||
["alias","email","status"] as $header |
|
["email","experience"] as $header |
|
||||||
[$header] + .[0:5] |
|
[$header] + .[0:5] |
|
||||||
.[] | @csv
|
.[] | @csv
|
||||||
`
|
`
|
||||||
|
|||||||
Reference in New Issue
Block a user