p0lip / nimma Goto Github PK
View Code? Open in Web Editor NEWScalable JSON Path engine.
License: Apache License 2.0
Scalable JSON Path engine.
License: Apache License 2.0
It looks like the spec draft that you've based your implementation on is old. Check out the details at https://github.com/ietf-wg-jsonpath/draft-ietf-jsonpath-base. There have been quite a few changes (with good reason) since you last looked.
Mainly:
in
and =~
are no longer valid operators.Also, I would recommend hiding JsonPath-Plus features behind a default-off configuration option to align with the spec.
There's also a comparison website which lists a bunch of paths and various implementations' results. I've added an issue to add this lib. I'll be working on it (I am not a JS dev), but if you want to add it yourself, feel free. It'd be great to have another implementation there.
And if you want to join in the coversation for developing the spec, you'd be welcome!
Hi Jakub!
In the README there is a statement:
Supports the majority of JSONPath-plus additions
Is there a list of supported features?
I've tried to use things like @root
, @parent
— it doesn't work for me... does Nimma support them?
So in the readme it states:
[...] whereas Nimma, for the most time, doesn't really care whether you supply it with 10s or 100s of paths. [...]
Unfortunately multiple paths mean multiple accesses when using Nimma. This is a huge deal when many input paths are given or slow getters are used.
This should definitely be mentioned in the Readme. It is probably the biggest and most fundamental difference to object-scan.
import Nimma from 'nimma';
import objectScan from 'object-scan';
class AccessLogger {
constructor(obj) {
const logs = {};
const prepare = (o) => {
const entries = Object.entries(o);
const result = {};
for (let i = 0; i < entries.length; i += 1) {
const [k, v] = entries[i];
const value = prepare(v);
result[k] = value;
Object.defineProperty(result, k, {
get() {
if (!(k in logs)) {
logs[k] = 0;
}
logs[k] += 1;
return value;
}
});
}
return result;
};
this.logs = logs;
this.obj = prepare(obj);
}
reset() {
const logs = { ...this.logs };
Object.keys(this.logs).forEach((k) => {
delete this.logs[k];
});
return logs;
}
}
const accessLogger = new AccessLogger({
a: {
b: {
c: {},
d: {},
e: {}
}
}
});
const { obj } = accessLogger;
const keysNimma = [
'$.a.b.c',
'$..*',
'$.a.b.d',
'$.a.b.e',
'$.a.*.e',
'$..e',
'$.*.*',
'$..b.c',
'$.a.b'
];
const keysObjectScan = keysNimma
.map((k) => (k.startsWith('$..') ? `**${k.slice(2)}` : k.slice(2)))
const testNimma = () => {
const callbacks = [];
new Nimma(keysNimma).query(obj, keysNimma.reduce((p, k) => ({
...p,
[k]({ value, path }) {
callbacks.push([path.join('.'), k, value]);
}
}), {}));
return { callbacks, access: accessLogger.reset() };
};
const testObjectScan = () => {
const callbacks = objectScan(keysObjectScan, {
joined: true,
filterFn: ({ matchedBy, key, value, context }) => {
matchedBy.forEach((m) => {
context.push([key, m, value]);
});
}
})(obj, []);
return { callbacks, access: accessLogger.reset() };
};
console.log(testNimma());
/*
{
callbacks: [
[ 'a.b.c', '$.a.b.c', {} ],
[ 'a.b.d', '$.a.b.d', {} ],
[ 'a.b.e', '$.a.b.e', {} ],
[ 'a.b', '$.a.b', [Object] ],
[ 'a', '$..*', [Object] ],
[ 'a.b', '$..*', [Object] ],
[ 'a.b', '$.*.*', [Object] ],
[ 'a.b.c', '$..*', {} ],
[ 'a.b.c', '$..b.c', {} ],
[ 'a.b.d', '$..*', {} ],
[ 'a.b.e', '$..*', {} ],
[ 'a.b.e', '$.a.*.e', {} ],
[ 'a.b.e', '$..e', {} ]
],
access: { a: 10, b: 9, c: 3, d: 3, e: 3 }
}
*/
console.log(testObjectScan());
/*
{
callbacks: [
[ 'a.b.e', 'a.b.e', {} ],
[ 'a.b.e', 'a.*.e', {} ],
[ 'a.b.e', '**.*', {} ],
[ 'a.b.e', '**.e', {} ],
[ 'a.b.d', 'a.b.d', {} ],
[ 'a.b.d', '**.*', {} ],
[ 'a.b.c', 'a.b.c', {} ],
[ 'a.b.c', '**.*', {} ],
[ 'a.b.c', '**.b.c', {} ],
[ 'a.b', 'a.b', [Object] ],
[ 'a.b', '**.*', [Object] ],
[ 'a.b', '*.*', [Object] ],
[ 'a', '**.*', [Object] ]
],
access: { a: 2, b: 2, e: 2, d: 2, c: 2 }
}
*/
Was just browsing for competitors to object-scan and this popped up!
Awesome project, I have to look into it in more detail and will definitely add it to the functionality comparison and performance benchmarking suite of object-scan! I'm really looking forward to testing how it compares performance wise!
I have to look at the source code as well. I'm assuming you have a precompilation phase where you build a search tree? Or how does this work internally? What are the next things on the roadmap for this project?
PS: If it wasn't clear from my comment, I'm the author of object-scan
Edit: Read up on how this works internally and can't wait to try it out! It's actually quite different and a cool way of handling the multi path traversal case. However does it require always a full traversal of the entire input?
I am using nimma in the context of spectral with the fallback option resorting to JSONPath Plus. I have a JSONPath expression that works well in JSONPath Plus. However, this expression is unusable in spectral because nimma raises an error instead of forwarding to the fallback implementation:
Here's the path I try to use:
$..properties[?([email protected] && (@parent.required || []).indexOf(@property) == -1)]
And here's the error I get in spectral:
❯ spectral --version
6.4.1
❯ spectral lint openapi-spec.yaml -r .spectral.yaml -v
Found 72 rules (61 enabled)
Linting /xxx/openapi-spec.yaml
Error running Spectral!
Error #1: (scope.sandbox.parentValue.required || []).indexOf is not a function
at indexOf(@property) … …js/core/index.js:66 _rollupPluginBabelH…
at eval …js/core/index.js:66 _rollupPluginBabelH…
at _traverseBody …time/traverse.js:13 cb(scope);
at _traverse …time/traverse.js:41 _traverseBody(key, …
at _traverseBody …time/traverse.js:17 _traverse(value, sc…
Hi Jakub,
I use filter expressions to search for an entry in an array of objects. Is it possible for you to include the @jsep-plugin/arrow? For example, the Array.find() method can also be used as a filter expression in the array.
Here is a small example:
JSONPath: $[?(@.phoneNumbers && @.phoneNumbers.find(phoneNumber => phoneNumber.type.match(/iPh.*/)))]
[{ "firstName": "John", "lastName": "doe", "age": 26, "address": { "streetAddress": "naist street", "city": "Nara", "postalCode": "630-0192" }, "phoneNumbers": [ { "type": "iPhone", "number": "0123-4567-8888" }, { "type": "home", "number": "0123-4567-8910" } ] }]
Best regards,
Rainer
$..: not working in query method
THis work in JSON path $..street:Address but not in query method
Tried to query the following JSON document with the JSONPath $..type^[*]^
,
{
"properties": {
"test#1": {
"type": "string"
}
}
}
I encounted the error: TypeError: Cannot read property 'toString' of undefined. I noticed the error does not occur if property name does not include #.
For demonstration purposes, I made my query (i.e. [*]) trivial here. In my real use-case, I was using a similar JSONPath query with a non-trivial filter [?(...)] in the Spectral context.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.