Search JSON Array
Search array items by text matching in specified fields
Source Code
import fs from "fs";
import path from "path";
const [inputPath, query, fieldsArg, mode = "contains", outputPath] =
process.argv.slice(2);
if (!inputPath || !query || !fieldsArg || !outputPath) {
console.error("Usage: inputPath query fields [mode] outputPath");
process.exit(1);
}
/**
* Get nested field value using dot notation
*/
function getField(obj, fieldPath) {
const parts = fieldPath.split(".");
let value = obj;
for (const part of parts) {
if (value == null) return undefined;
value = value[part];
}
return value;
}
/**
* Calculate simple fuzzy match score (Levenshtein-like)
*/
function fuzzyScore(str, query) {
const s = str.toLowerCase();
const q = query.toLowerCase();
// Check for substring match first
if (s.includes(q)) return 1;
// Check if all query characters appear in order
let sIdx = 0;
let matchCount = 0;
for (const char of q) {
const found = s.indexOf(char, sIdx);
if (found >= 0) {
matchCount++;
sIdx = found + 1;
}
}
// Return ratio of matched characters
return matchCount / q.length;
}
/**
* Check if item matches the search query
*/
function matches(item, fields, query, mode) {
const queryLower = query.toLowerCase();
for (const field of fields) {
const value = getField(item, field);
if (value == null) continue;
const strValue = String(value);
const strLower = strValue.toLowerCase();
switch (mode) {
case "exact":
if (strLower === queryLower) return true;
break;
case "contains":
if (strLower.includes(queryLower)) return true;
break;
case "fuzzy":
if (fuzzyScore(strValue, query) >= 0.7) return true;
break;
}
}
return false;
}
try {
console.log(`Reading ${inputPath}...`);
const raw = fs.readFileSync(inputPath, "utf-8");
const data = JSON.parse(raw);
const items = Array.isArray(data)
? data
: data.items || data.results || data.messages || [];
if (!Array.isArray(items)) {
console.error("Input must be a JSON array or object with array property");
process.exit(1);
}
const fields = fieldsArg.split(",").map((f) => f.trim());
console.log(
`Searching ${items.length} items for "${query}" in fields: ${fields.join(", ")} (${mode} mode)...`
);
const results = items.filter((item) => matches(item, fields, query, mode));
// Ensure output directory exists
const dir = path.dirname(outputPath);
if (dir && dir !== ".") {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(outputPath, JSON.stringify(results, null, 2));
console.log(`\nā Found ${results.length} matches out of ${items.length} items`);
console.log(` Query: "${query}"`);
console.log(` Fields: ${fields.join(", ")}`);
console.log(` Mode: ${mode}`);
console.log(` Written to: ${outputPath}`);
console.log(
JSON.stringify({
success: true,
outputPath,
query,
fields,
mode,
inputCount: items.length,
matchCount: results.length,
})
);
} catch (error) {
console.error("Error:", error.message);
process.exit(1);
}