{"id":13514284,"url":"https://github.com/cube2222/jql","last_synced_at":"2025-04-13T07:48:35.294Z","repository":{"id":46103910,"uuid":"230017173","full_name":"cube2222/jql","owner":"cube2222","description":"Easy JSON Query Processor with a Lispy syntax in Go","archived":false,"fork":false,"pushed_at":"2023-09-08T12:23:26.000Z","size":127,"stargazers_count":899,"open_issues_count":3,"forks_count":19,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-04-13T07:48:27.278Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cube2222.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-12-25T00:19:31.000Z","updated_at":"2025-02-20T11:34:19.000Z","dependencies_parsed_at":"2024-01-12T20:03:51.751Z","dependency_job_id":null,"html_url":"https://github.com/cube2222/jql","commit_stats":{"total_commits":69,"total_committers":2,"mean_commits":34.5,"dds":0.01449275362318836,"last_synced_commit":"2bca3b0a498624428d55d77ab9a0089b4f8acf62"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cube2222%2Fjql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cube2222%2Fjql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cube2222%2Fjql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cube2222%2Fjql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cube2222","download_url":"https://codeload.github.com/cube2222/jql/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248681494,"owners_count":21144700,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T05:00:51.697Z","updated_at":"2025-04-13T07:48:35.260Z","avatar_url":"https://github.com/cube2222.png","language":"Go","readme":"# jql\nHey there!\n\nYou're probably here cause you're fed up with other json query processors being too complicated to use for anything surpassing simple single field selection.\n\nWell, at least that's what led me here. And that's why I've written **jql**, a json query processor with an even more cryptic - lispy - syntax (Maybe not that cryptic after all? I quite like it :) )\n\n**jql** aims to be friendly, helpful, pat you on the back when you're faced with a monstrosity of a JSON blob. Help you mold it into something useful, step by step.\n\nIf you want to see **benchmarks**, they're at [the end of the README.](#Benchmarks)\n\nIf you want to see a cheatsheet with function types, it's [right before the benchmarks](#Type-Cheatsheet).\n\nOk, let's check it out now, but first things first, you have to install it:\n```\ngo get github.com/cube2222/jql\n```\n\nOk. Done.\n\nIf you don't have the Go toolchain installed, just download one of the release binaries.\n\nIf you want an interactive window to get live output for queries, use:\n```\necho '' | fzf --print-query --preview-window wrap --preview 'cat test.json | jql {q}'\n```\n\nLet's check out a few _simple_ examples. (remember? That's explicitly **not** why we're here. But it aids understanding of the more complex examples, so stay with me just a little bit longer!)\n\nWe'll be working with this piece of json:\n```json\n{ \n  \"count\": 3,\n  \"countries\": [\n    {\n      \"name\": \"Poland\",\n      \"population\": 38000000,\n      \"european\": true,\n      \"eu_since\": \"2004\"\n    },\n    {\n      \"name\": \"United States\",\n      \"population\": 327000000,\n      \"european\": false\n    },\n    {\n      \"name\": \"Germany\",\n      \"population\": 83000000,\n      \"european\": true,\n      \"eu_since\": \"1993\"\n    }\n  ]\n}\n```\nTo start with, let's get the countries array only.\n```\n\u003e cat test.json | jql '(elem \"countries\")'\n[\n  {\n    \"eu_since\": \"2004\",\n    \"european\": true,\n    \"name\": \"Poland\",\n    \"population\": 38000000\n  },\n  {\n    \"european\": false,\n    \"name\": \"United States\",\n    \"population\": 327000000\n  },\n  {\n    \"eu_since\": \"1993\",\n    \"european\": true,\n    \"name\": \"Germany\",\n    \"population\": 83000000\n  }\n]\n```\n\nWhoa, whoa, whoa! What's up with the parentheses?!\n\nRemember what I said before? Lispy syntax. In short, whenever you see (add 1 2), it's basically the same as add(1, 2). I like that syntax for big hierarchical expressions, if you really really _really_ don't like it, then you can probably stop right here, though you'll be losing out!\n\nI've warned you.\n\nWhat if we wanted only the first country from that list?\n```\n\u003e cat test.json | jql '(elem \"countries\" (elem 0))'\n{\n  \"eu_since\": \"2004\",\n  \"european\": true,\n  \"name\": \"Poland\",\n  \"population\": 38000000\n}\n```\nLet's break down what happened here. First we took the \"countries\" field. elem takes an additional argument, it says \"how to transform the element\" and is also an expression. Here we say we want to take the first element of the countries array. The default function is _id_, which stands for identity.\n\n#### array\n\nWe can also pass an array of positions to elem, to get more than one country:\n```\n\u003e cat test.json | jql '(elem \"countries\" (elem (array 0 2)))'\n[\n  {\n    \"eu_since\": \"2004\",\n    \"european\": true,\n    \"name\": \"Poland\",\n    \"population\": 38000000\n  },\n  {\n    \"eu_since\": \"1993\",\n    \"european\": true,\n    \"name\": \"Germany\",\n    \"population\": 83000000\n  }\n]\n```\n\nelem can work with single strings, single integers, arrays of those, and objects with them as values (but we won't cover those now to keep things simple).\n\n#### keys\n\nWhat if we want to get all the country names? A new friend - _keys_ - is key in this situation. 🗝\n```\ncat test.json | jql '(elem \"countries\" (elem (keys) (elem \"name\")))'\n[\n  \"Poland\",\n  \"United States\",\n  \"Germany\"\n]\n```\n\nIt returns an array of all the keys of the given collection. Fields and Indices for Objects and Arrays respectively.\n\nTo illustrate, here's _keys_ used on an object:\n```\n\u003e cat test.json | jql '(elem \"countries\" (elem 0 (keys)))'\n[\n  \"name\",\n  \"population\",\n  \"european\",\n  \"eu_since\"\n]\n```\n\n---\n### Attention\n\nNow we have to understand a very important mechanism underlying **jql**. All functions operate in the context of the JSON we're operating on.\n\nSome functions, like elem, will cut down the context for expressions it evaluates. The first argument - which should evaluate to the positions we need - gets evaluated in the context of the entire array, that's why _keys_ returns all the indices. The second one on the other hand, operates in the context of a single element.\n\nIn theory we're really just creating and composing a big function - pipeline, so to say - which gets applied to our JSON blob.\n\nThis may sound complicated, but I find it becomes intuitive quite quickly.\n\n---\n\nYou can see that elem is the most used function, and in fact it's what you'll usually be using when munging data, so there's a shortcut. If you put a value in function name position, it implicitly converts it to an elem.\n\nThis way we can rewrite the previous query to be much shorter, and better match the shape of the data.\n```\n\u003e cat test.json | jql '(\"countries\" ((keys) (\"name\")))'\n[\n  \"Poland\",\n  \"United States\",\n  \"Germany\"\n]\n```\n\n---\n### Little showcase\nRemember when I said you can use integers, strings, arrays and objects as positions?\n```\n\u003e cat test.json | jql '(\"countries\" ((array (array 0 (array 0 (array 0 (array 0 2)))) 1 (object \"key1\" 1 \"key2\" (array 0 (object \"key1\" 1 \"key2\" (array 0 2))))) (\"population\")))'\n[\n  [\n    38000000,\n    [\n      38000000,\n      [\n        38000000,\n        [\n          38000000,\n          83000000\n        ]\n      ]\n    ]\n  ],\n  327000000,\n  {\n    \"key1\": 327000000,\n    \"key2\": [\n      38000000,\n      {\n        \"key1\": 327000000,\n        \"key2\": [\n          38000000,\n          83000000\n        ]\n      }\n    ]\n  }\n]\n```\n🔥🔥🔥\n\nDon't do this.\n\n---\n\n#### range\n\nWe can also select a range of elements, using the... you guessed it - _range_ function.\n```\n\u003e cat test.json | jql '(\"countries\" ((range 1 3) (\"name\")))'\n[\n  \"United States\",\n  \"Germany\"\n]\n```\n\nYou can use the _array_ function in value position too obviously. If you want a list of name-population tuples you can just\n```\n\u003e cat test.json | jql '(\"countries\" ((keys) (array (\"name\") (\"population\"))))'\n[\n  [\n    \"Poland\",\n    38000000\n  ],\n  [\n    \"United States\",\n    327000000\n  ],\n  [\n    \"Germany\",\n    83000000\n  ]\n]\n```\n\nHere you can see that _array_ passes the whole context given to it to each of its arguments. (Reminder: We're using \"name\" and \"population\" as elem shortcuts here.)\n\nMost functions work like this. Only elem is the \"context-cutdowner\", so to say.\n\n#### object\n\nYou can also use _object_ to create objects, with arguments alternating keys and values.\n```\n\u003e cat test.json | jql '(object\n                            \"names\" (\"countries\" ((keys) (\"name\")))\n                            \"populations\" (\"countries\" ((array 0 0 1) (\"population\"))))'\n{\n  \"names\": [\n    \"Poland\",\n    \"United States\",\n    \"Germany\"\n  ],\n  \"populations\": [\n    38000000,\n    38000000,\n    327000000\n  ]\n}\n```\n\n#### zip\n\nOccasionally you might want to combine multiple arrays, you can use _zip_ to do that. For example, to create a list of key-value entries from an object, you can do the following:\n```\n\u003e cat test.json | jql '(\"countries\" \n                           (0 (zip \n                                 (keys) \n                                 ((keys)))))'\n[\n  [\n     \"eu_since\",\n     \"2004\"\n  ],\n  [\n     \"european\",\n     true\n  ],\n  [\n     \"name\",\n     \"Poland\"\n  ],\n  [\n     \"population\",\n     38000000\n  ]\n]\n```\n\nNow we're done with the **core** functionality of jql. The stuff so far will probably suffice for most use cases and even very complex data structures.\n\nHowever, here come more functions:\n\n### String manipulation 🎻\n\n#### join\nIf you ever need to _join_ an array of expressions into a string, _join_'s the mate you're looking for! _join_ will also stringify anything it meets.\nWithout separator:\n```\n\u003e cat test.json | jql '(\"countries\" ((keys) (join (array (\"name\") (\"population\") (\"european\")))))'\n[\n  \"Poland3.8e+07true\",\n  \"United States3.27e+08false\",\n  \"Germany8.3e+07true\"\n]\n```\n\nWith separator:\n```\n\u003e cat test.json | jql '(\"countries\" ((keys) (join (array (\"name\") (\"population\") (\"european\")) \", \")))'\n[\n  \"Poland, 3.8e+07, true\",\n  \"United States, 3.27e+08, false\",\n  \"Germany, 8.3e+07, true\"\n]\n```\n\n#### sprintf\nWhenever I learn a new language, I feel much more comfortable when I know there's a sprintf function. (and how to use it)\n\nAnyways, here you go, the syntax is the same as that of the go standard library [fmt.Sprintf](https://golang.org/pkg/fmt/) function:\n```\n\u003e cat test.json | jql '(\"countries\" ((keys) (sprintf \"%s population: %.0f\" (\"name\") (\"population\"))))'\n[\n  \"Poland population: 38000000\",\n  \"United States population: 327000000\",\n  \"Germany population: 83000000\"\n]\n```\nHope you're feeling comfortable 🛋 now :)\n\n### error\nThere's a little helper function - _error_ - for those times when you're debugging your queries.\n\nIt's an expression which errors on evaluation and gives you a stack trace. It can also evaluate and print any expression you want in it's context.\n\n```\n\u003e cat test.json | jql '(\"countries\" ((keys) (sprintf \"%s population: %.0f\" (\"name\") (error \"test message\"))))'\n2019/12/26 00:17:04 error getting expression value for object: couldn't get transformed value for field countries with value [map[eu_since:2004 european:true name:Poland population:3.8e+07] map[european:false name:United States population:3.27e+08] map[eu_since:1993 european:true name:Germany population:8.3e+07]]: couldn't get element using position at array index 0: couldn't get transformed value for index 0 with value map[eu_since:2004 european:true name:Poland population:3.8e+07]: couldn't evaluate sprintf argument with index 1: Message: test message\ngoroutine 1 [running]:\nruntime/debug.Stack(0xc0000723f0, 0x1114e00, 0xc0000744e0)\n        /usr/local/Cellar/go/1.13.4/libexec/src/runtime/debug/stack.go:24 +0x9d\ngithub.com/cube2222/jql/jql.Error.Get(0x1164680, 0xc0000723f0, 0x1114e00, 0xc0000744e0, 0x110c5c0, 0xc000072440, 0x0, 0x0)\n        /Users/jakub/Projects/Go/src/github.com/cube2222/jql/jql/functions.go:715 +0x102\ngithub.com/cube2222/jql/jql.Sprintf.Get(0x1164680, 0xc0000723b0, 0xc000074460, 0x2, 0x2, 0x1114e00, 0xc0000744e0, 0x100b136, 0xc000072490, 0x10, ...)\n        /Users/jakub/Projects/Go/src/github.com/cube2222/jql/jql/functions.go:299 +0x16a\ngithub.com/cube2222/jql/jql.GetElement(0x110bb80, 0x12479e0, 0x1109ec0, 0xc00008a1e0, 0x1164a20, 0xc000074480, 0x122c560, 0x128b6d0, 0x0, 0x100ba08)\n        /Users/jakub/Projects/Go/src/github.com/cube2222/jql/jql/functions.go:101 +0xa96\ngithub.com/cube2222/jql/jql.GetElement(0x1109ec0, 0xc00008a220, 0x1109ec0, 0xc00008a1e0, 0x1164a20, 0xc000074480, 0x0, 0x15fffff, 0xc0000afba0, 0x194)\n        /Users/jakub/Projects/Go/src/github.com/cube2222/jql/jql/functions.go:71 +0x46d\ngithub.com/cube2222/jql/jql.Element.Get(0x1164980, 0x12470c8, 0x1164a20, 0xc000074480, 0x1109ec0, 0xc00008a1e0, 0xc0000c6018, 0x1ee, 0xc0000c6000, 0x103301c)\n        /Users/jakub/Projects/Go/src/github.com/cube2222/jql/jql/functions.go:135 +0x12f\ngithub.com/cube2222/jql/jql.GetElement(0x110c5c0, 0xc0000722b0, 0x1114e00, 0xc0000744b0, 0x11648c0, 0xc00008a160, 0x0, 0x10, 0xc0000724b0, 0xa)\n        /Users/jakub/Projects/Go/src/github.com/cube2222/jql/jql/functions.go:118 +0x862\ngithub.com/cube2222/jql/jql.Element.Get(0x1164680, 0xc0000723a0, 0x11648c0, 0xc00008a160, 0x1114e00, 0xc0000744b0, 0x10, 0x1114860, 0x1, 0xc0000724b0)\n        /Users/jakub/Projects/Go/src/github.com/cube2222/jql/jql/functions.go:135 +0x12f\nmain.main()\n        /Users/jakub/Projects/Go/src/github.com/cube2222/jql/main.go:39 +0x2b4\n```\n\n### Logic\nYey, back to the basics!\n\nYou've got _eq_, _lt_ and _gt_, all working as you'd probably expect:\n```\n\u003e cat test.json | jql '(eq \"test\" \"test\")'\ntrue\n\u003e cat test.json | jql '(eq \"test\" \"test2\")'\nfalse\n\u003e cat test.json | jql '(lt \"a\" \"b\")'\ntrue\n\u003e cat test.json | jql '(lt \"b\" \"a\")'\nfalse\n\u003e cat test.json | jql '(gt 5 4)'\ntrue\n```\nIn case you're wondering, _eq_ does a [reflect.DeepEqual](https://golang.org/pkg/reflect/#DeepEqual) on both arguments.\n\nYou've also got _and_, _or_, _not_, to cover your back when tackling those primal and primitive (some would say **fundamental**) problems you may encounter:\n```\n\u003e cat test.json | jql '(and true true true)'\ntrue\n\u003e cat test.json | jql '(and true true false)'\nfalse\n\u003e cat test.json | jql '(and true true null)'\nfalse\n\u003e cat test.json | jql '(or true true false)'\ntrue\n\u003e cat test.json | jql '(or)'\nfalse\n\u003e cat test.json | jql '(and)'\ntrue\n\u003e cat test.json | jql '(not true)'\nfalse\n\u003e cat test.json | jql '(not false)'\ntrue\n\u003e cat test.json | jql '(not null)'\ntrue\n\u003e cat test.json | jql '(not (array false))'\nfalse\n```\n_and_ and _or_ are both lazy.\n\n#### Truthiness\n\nThis brings us to the topic of truthiness. What does _and_ consider to be \"true\"? Well, it's quite simple actually.\n\n* null **is not** truthy.\n* false **is not** truthy.\n* anything else **is** truthy.\n\n#### ifte\n\nifte sounds kinda fluffy, but unfortunately it only stands for If Then Else.\n```\n\u003e cat test.json | jql '(ifte true \"true\" \"false\")'\n\"true\"\n```\n\nIt's lazy too. If it weren't, this would error:\n```\nl\u003e cat test.json | jql '(ifte true \"true\" (error \":(\"))'\n\"true\"\n```\n\nFluffy and lazy. Like a cat. Who doesn't like cats? Who doesn't like ifte? 🐈\n\n#### filter 🍰\nSometimes you want just part of the cake, the part with no \\\u003cinsert disliked fruit here\\\u003e.\n\nI've got no data set on cakes though, so let's get back to our beloved countries:\n```\n\u003e cat test.json | jql '(\"countries\" (filter (gt (\"population\") 50000000)))'\n[\n  {\n    \"european\": false,\n    \"name\": \"United States\",\n    \"population\": 327000000\n  },\n  {\n    \"eu_since\": \"1993\",\n    \"european\": true,\n    \"name\": \"Germany\",\n    \"population\": 83000000\n  }\n]\n```\n\nAlso, because _null_ is not truthy, and _elem_ returns _false_ if it encounters missing fields or indices, you can use _filter_ to get rid of wrong-schema data:\n```\n\u003e cat test.json | jql '(\"countries\" (filter (\"eu_since\")))'\n[\n  {\n    \"eu_since\": \"2004\",\n    \"european\": true,\n    \"name\": \"Poland\",\n    \"population\": 38000000\n  },\n  {\n    \"eu_since\": \"1993\",\n    \"european\": true,\n    \"name\": \"Germany\",\n    \"population\": 83000000\n  }\n]\n```\n\n### pipe\n_pipe_ is a fairly useless function because you can just use a bash pipe. But if for some reason you want to save cpu cycles:\n```\n\u003e cat test.json | jql '(pipe\n                           (\"countries\")\n                           ((range 2))\n                           ((keys) (\"name\")))'\n[\n  \"Poland\",\n  \"United States\"\n]\n```\nis equal to\n```\n\u003e cat test.json | jql '(\"countries\")' | jql '((range 2))' | jql '((keys) (\"name\"))'\n[\n  \"Poland\",\n  \"United States\"\n]\n```\n\n### recover\nFinally, the one whose name shall not be spoken out loud. 👹 Needing him means you either encountered a bug in **jql**, or that your dataset is seriously botched.\n\nHe'll help you catch errors and panics, nullyfing the expression, leaving only void behind.\n```\n\u003e cat test.json | jql '(\"countries\" ((keys) (rec*** (ifte (\"european\") (id) (error \"not european\")))))'\n[\n  {\n    \"eu_since\": \"2004\",\n    \"european\": true,\n    \"name\": \"Poland\",\n    \"population\": 38000000\n  },\n  null,\n  {\n    \"eu_since\": \"1993\",\n    \"european\": true,\n    \"name\": \"Germany\",\n    \"population\": 83000000\n  }\n]\n```\nCombine him with _filter_ and even the void will be gone!\n\nIn practice you obviously have to spell out his name, otherwise it won't work, but that's on you!\n\n# Summary\nHope you enjoyed this **incredible** journey!\n\nMoreover, I hope it's not the end of it! Hordes of JSON blobs still await and I hope **jql** will become your weapon of choice for dealing with them from now on! ⚔️\n\nIssues, ⭐️stars⭐️, comments, messages, reviews, benchmarks, you name it! - all are very appreciated! 😉\n\n# Type Cheatsheet\n```\nJSON: Any value\nExpression[T]: (JSON -\u003e T)\nExpression can be seen as a Continuation\nelem:\n    With one arg: (Expression[Position]) -\u003e (Expression[JSON]) = (elem position (id))\n    With two args: (Expression[Position] x Expression[T]) -\u003e (Expression[T])\nkeys: () -\u003e (Expression[JSON])\nid: () -\u003e (Expression[JSON])\narray: (Expression[T]...) -\u003e (Expression[Array[T]]) (T's can vary)\nobject: ((Expression x Expression[T])...) -\u003e (Expression[T])\npipe: (Expression...) -\u003e (Expression)\nsprintf: (Expression[String] x Expression[T]...) -\u003e (Expression[String])\njoin: (Expression[T]...) -\u003e (Expression[String])\nfilter: (Expression[Bool]) -\u003e (Expression[JSON])\neq,lt,gt: (Expression x Expression) -\u003e (Expression[Bool])\nrange: \n    With one arg: (Expression[Int]) -\u003e (Expression[Array[Int]])\n    With two args: (Expression[Int] x Expression[Int]) -\u003e (Expression[Array[Int]])\nzip: (Expression[Array[A]], Expression[Array[B]], ...) -\u003e (Expression[Array[Array[A | B | ...]]])\nand,or: (Expression[Bool]...) -\u003e (Expression[Bool])\nnot: (Expression[Bool]) -\u003e (Expression[Bool])\nifte: (Expression[Bool] x Expression[A] x Expression[B]) -\u003e (Expression[A|B])\nerror: (Expression[JSON]) -\u003e (!)\nrecover: (Expression[JSON]) -\u003e (Expression[JSON])\n```\n\n# Benchmarks\n\nThis is a fairly synthetic benchmark (it's the first that came to my mind), so I won't say **jql** is faster than jq. Especially since jq has various much more advanced functionalities.\n\nWhat I will say though, is that **jql** is definitely not slow and you can freely cut your way through gigabytes of data quickly, as in the following benchmarks it was 2-3 times faster than jq.\n\nThe benchbig.json file contains 2^20 repetitions of the json we've been using in the examples so far, and the benchmedium.json file contains 20.\n\n| Command | Mean [s] | Min [s] | Max [s] | Relative |\n|:---|---:|---:|---:|---:|\n| `cat benchbig.json \\| jql '(\"countries\" ((keys) (\"name\")))' \u003e\u003e out.json` | 5.923 ± 0.034 | 5.890 | 6.003 | 797.90 ± 52.78 |\n| `cat benchbig.json \\| jq '.countries[].name' \u003e\u003e out.json` | 14.960 ± 0.047 | 14.906 | 15.047 | 2015.24 ± 132.94 |\n| `cat benchmedium.json \\| jql '(\"countries\" ((keys) (\"name\")))' \u003e\u003e out.json` | 0.007 ± 0.000 | 0.007 | 0.010 | 1.00 |\n| `cat benchmedium.json \\| jq '.countries[].name' \u003e\u003e out.json` | 0.024 ± 0.001 | 0.022 | 0.032 | 3.23 ± 0.26 |\n\nThe benchmarks were run using [hyperfine](https://github.com/sharkdp/hyperfine) on a preheated (very loud) Macbook Pro 13 mid-2019 i7 2.8GHz 16GB 256GB.\n\nYou can generate the benchmark data with benchmarks/gen.sh.\n\n# Special Thanks\nA big thank you to the following people for reviewing the current or previous versions of this document:\n- Jan Chomiak ( @JasiekChomiak )\n- Hugo Dutka ( @hugodutka )\n- Katarzyna Gajos\n- Andrzej Głuszak ( @agluszak )\n- Wojciech Kuźmiński ( @woojtek )\n","funding_links":[],"categories":["Go","Other"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcube2222%2Fjql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcube2222%2Fjql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcube2222%2Fjql/lists"}