[UPDATE] Enhance README and demo scripts with comprehensive query examples and improved schema mappings

This commit is contained in:
Eliyan
2025-10-15 10:01:00 +02:00
parent e863a0a5ea
commit 77c2c4ab9b
3 changed files with 635 additions and 66 deletions

299
demo.js
View File

@@ -1,46 +1,287 @@
#!/usr/bin/env node
// Demo script showing different ODMDB query types with real data
// Demo script that actually uses the PoC functionality to demonstrate real query generation
import fs from "node:fs";
import OpenAI from "openai";
console.log("🚀 ODMDB NL to Query Demo");
console.log("=".repeat(50));
// Import PoC components (we'll need to extract them to make them reusable)
const MODEL = process.env.OPENAI_MODEL || "gpt-5";
const ODMDB_BASE_PATH = "../smatchitObjectOdmdb";
const SCHEMA_PATH = `${ODMDB_BASE_PATH}/schema`;
// Sample queries to demonstrate
const queries = [
console.log("🚀 ODMDB NL to Query Demo - Live PoC Testing");
console.log("=".repeat(60));
// Check prerequisites
if (!process.env.OPENAI_API_KEY) {
console.log("❌ Missing OPENAI_API_KEY environment variable");
console.log(" Set it with: export OPENAI_API_KEY=sk-your-api-key");
process.exit(1);
}
// Load schema (same function as in poc.js)
function loadJsonSafe(path) {
try {
if (fs.existsSync(path)) {
return JSON.parse(fs.readFileSync(path, "utf-8"));
}
} catch (e) {
console.warn(`Warning: Could not load ${path}:`, e.message);
}
return null;
}
// Load actual ODMDB schemas
const SCHEMAS = {
seekers: loadJsonSafe(`${SCHEMA_PATH}/seekers.json`),
main: loadJsonSafe("./main.json"), // Fallback consolidated schema
};
// Simplified SchemaMapper for demo
class DemoSchemaMapper {
constructor(schemas) {
this.seekersSchema = schemas.seekers;
console.log(
`📋 Loaded seekers schema with ${
Object.keys(this.seekersSchema?.properties || {}).length
} properties`
);
}
getRecruiterReadableFields() {
if (!this.seekersSchema?.apxaccessrights?.recruiters?.R) {
return ["alias", "email", "seekstatus", "seekworkingyear"];
}
return this.seekersSchema.apxaccessrights.recruiters.R;
}
getAllSeekersFields() {
if (!this.seekersSchema?.properties) return [];
return Object.keys(this.seekersSchema.properties);
}
}
const schemaMapper = new DemoSchemaMapper(SCHEMAS);
// Sample queries to demonstrate with actual PoC execution
const demoQueries = [
{
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"],
nl: "get seekers with their contact info and personality types",
description: "Multiple field types (contact + MBTI)",
},
];
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("<EFBFBD> Demo Queries - Testing Live PoC:");
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");
// JSON Schema for query generation (same as poc.js)
function buildResponseJsonSchema() {
const recruiterReadableFields = schemaMapper.getRecruiterReadableFields();
return {
type: "object",
additionalProperties: false,
properties: {
object: { type: "string", enum: ["seekers"] },
condition: { type: "array", items: { type: "string" }, minItems: 1 },
fields: {
type: "array",
items: { type: "string", enum: recruiterReadableFields },
minItems: 1,
},
},
required: ["object", "condition", "fields"],
};
}
console.log("\n📊 Current ODMDB Status:");
// System prompt (simplified version from poc.js)
function systemPrompt() {
const availableFields = schemaMapper.getAllSeekersFields();
const recruiterReadableFields = schemaMapper.getRecruiterReadableFields();
return [
"You convert a natural language request into an ODMDB search payload.",
"Return ONLY a compact JSON object that matches the provided JSON Schema.",
"",
"ODMDB DSL:",
"- idx.<indexName>(value) - for indexed fields",
"- prop.<field>(operator:value) - for direct property queries",
"",
"Available seekers fields:",
availableFields.slice(0, 15).join(", ") +
(availableFields.length > 15 ? "..." : ""),
"",
"Recruiter-readable fields (use these for field selection):",
recruiterReadableFields.join(", "),
"",
"Field mappings:",
"- 'email', 'contact info' → email",
"- 'experience', 'years of experience' → seekworkingyear",
"- 'status', 'availability' → seekstatus",
"- 'salary', 'pay' → salaryexpectation",
"- 'personality', 'MBTI' → mbti",
"",
"Status value mappings:",
"- 'urgent', 'urgently', 'ASAP' → startasap",
"- 'no rush', 'taking time' → norush",
"- 'not looking' → notlooking",
"",
"Rules: Object must be 'seekers'. Use idx.seekstatus_alias for status queries.",
].join("\n");
}
// OpenAI client and query function
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
async function generateQuery(nlText) {
try {
const resp = await client.responses.create({
model: MODEL,
input: [
{ role: "system", content: systemPrompt() },
{
role: "user",
content: `Natural language request: "${nlText}"\nReturn ONLY the JSON object.`,
},
],
text: {
format: {
name: "OdmdbQuery",
type: "json_schema",
schema: buildResponseJsonSchema(),
strict: true,
},
},
});
const jsonText = resp.output_text || resp.output?.[0]?.content?.[0]?.text;
return JSON.parse(jsonText);
} catch (error) {
console.error(`❌ Query generation failed: ${error.message}`);
return null;
}
}
// Simple query execution (simplified from poc.js)
function loadSeekersData() {
const seekersItemsPath = `${ODMDB_BASE_PATH}/objects/seekers/itm`;
try {
const files = fs
.readdirSync(seekersItemsPath)
.filter((file) => file.endsWith(".json") && file !== "backup")
.slice(0, 10); // Just 10 files for demo speed
const seekers = [];
for (const file of files) {
try {
const filePath = `${seekersItemsPath}/${file}`;
const data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
seekers.push(data);
} catch (error) {
// Skip invalid files
}
}
return seekers;
} catch (error) {
return [];
}
}
async function executeQuery(query) {
const allSeekers = loadSeekersData();
if (allSeekers.length === 0) return { data: [] };
let filteredSeekers = allSeekers;
// Simple filtering
for (const condition of query.condition) {
if (condition.includes("idx.seekstatus_alias(startasap)")) {
filteredSeekers = filteredSeekers.filter(
(seeker) => seeker.seekstatus === "startasap"
);
}
if (condition.includes("prop.salaryexpectation(exists:true)")) {
filteredSeekers = filteredSeekers.filter(
(seeker) => seeker.salaryexpectation
);
}
if (condition.includes("prop.email(exists:true)")) {
filteredSeekers = filteredSeekers.filter((seeker) => seeker.email);
}
if (condition.includes("prop.mbti(exists:true)")) {
filteredSeekers = filteredSeekers.filter((seeker) => seeker.mbti);
}
}
// Select only requested fields
const results = filteredSeekers.map((seeker) => {
const filtered = {};
for (const field of query.fields) {
if (seeker.hasOwnProperty(field)) {
filtered[field] = seeker[field];
}
}
return filtered;
});
return { data: results };
}
// Main demo execution
async function runDemo() {
const executeQueries = process.env.EXECUTE_DEMO === "true";
for (let i = 0; i < demoQueries.length; i++) {
const query = demoQueries[i];
console.log(`\n${i + 1}. "${query.nl}"`);
console.log(` Purpose: ${query.description}`);
console.log(" 🤖 Generating query...");
const generatedQuery = await generateQuery(query.nl);
if (generatedQuery) {
console.log(" ✅ Generated ODMDB Query:");
console.log(
` ${JSON.stringify(generatedQuery, null, 6).replace(/\n/g, "\n ")}`
);
if (executeQueries) {
console.log(" 🔍 Executing query...");
const results = await executeQuery(generatedQuery);
console.log(` 📊 Found ${results.data.length} results`);
if (results.data.length > 0) {
console.log(" 📋 Sample result:");
console.log(
` ${JSON.stringify(results.data[0], null, 6).replace(
/\n/g,
"\n "
)}`
);
}
}
} else {
console.log(" ❌ Failed to generate query");
}
if (i < demoQueries.length - 1) {
console.log(" " + "-".repeat(50));
}
}
if (!executeQueries) {
console.log(`\n💡 To execute queries and see results, run:`);
console.log(` EXECUTE_DEMO=true node demo.js`);
}
}
console.log("\n📊 ODMDB Status Check:");
// Check if ODMDB data is accessible
const seekersPath = "../smatchitObjectOdmdb/objects/seekers/itm";
@@ -98,4 +339,12 @@ try {
console.log(`❌ Error loading schema: ${error.message}`);
}
console.log("\n✅ Demo complete!");
console.log("\n🚀 Running Live PoC Demo...");
runDemo()
.then(() => {
console.log("\n✅ Demo complete!");
})
.catch((error) => {
console.error("\n❌ Demo failed:", error.message);
process.exit(1);
});