{"id":48573473,"url":"https://github.com/polydojo/pogodb","last_synced_at":"2026-04-08T15:35:02.433Z","repository":{"id":57453900,"uuid":"308083524","full_name":"polydojo/pogodb","owner":"polydojo","description":"Simple NoSQL wrapper for Postgres' JSONB type.","archived":false,"fork":false,"pushed_at":"2020-11-10T20:51:54.000Z","size":33,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-27T18:53:07.781Z","etag":null,"topics":["nosql-data-storage","nosql-database","nosql-wrapper","postgres","sql"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/polydojo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-28T16:53:42.000Z","updated_at":"2024-01-25T15:55:37.000Z","dependencies_parsed_at":"2022-08-29T06:51:41.912Z","dependency_job_id":null,"html_url":"https://github.com/polydojo/pogodb","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/polydojo/pogodb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polydojo%2Fpogodb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polydojo%2Fpogodb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polydojo%2Fpogodb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polydojo%2Fpogodb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/polydojo","download_url":"https://codeload.github.com/polydojo/pogodb/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polydojo%2Fpogodb/sbom","scorecard":{"id":740386,"data":{"date":"2025-08-11","repo":{"name":"github.com/polydojo/pogodb","commit":"3e4bcd96b0b8e7f00ed38f43e7700b760c9fded4"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/8 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-22T17:15:29.588Z","repository_id":57453900,"created_at":"2025-08-22T17:15:29.588Z","updated_at":"2025-08-22T17:15:29.588Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31562691,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["nosql-data-storage","nosql-database","nosql-wrapper","postgres","sql"],"created_at":"2026-04-08T15:35:01.959Z","updated_at":"2026-04-08T15:35:02.425Z","avatar_url":"https://github.com/polydojo.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"PogoDB\n=======\n\nSimple NoSQL wrapper around Postgres' JSONB type.\n\nInstallation\n--------------\nPogoDB is installable via pip, following a *two-step* process:\n1. `pip install pogodb`\n2. `pip install psycopg2` *OR* `pip install psycopg2-binary`\n\nSince the `psycopg2`/`psycopg2-binary` split, instead of forcing a dependency on either one, the choice is left to you. PogoDB should work with either. *Tip:*  If `pip install psycopg2` fails, try `pip install psycopg2-binary`.\n\nQuickstart\n--------------\nTo connect from a Python Shell, use `pogodb.shellConnect(.)`.\n```py\n\u003e\u003e\u003e import pogodb\n\u003e\u003e\u003e db = pogodb.shellConnect(\"postgres://..dsn..\")\nConnection opened. Call `.close()` to close.\n\u003e\u003e\u003e db.insertOne({\"_id\": \"foo\", \"value\": \"foobar\"})\n\u003e\u003e\u003e db.findOne(\"foo\")\n{'_id': 'foo', 'value': 'foobar'}\n\u003e\u003e\u003e db.close()\nConnection committed \u0026 closed. Call `.reopen()` to resume.\n\u003e\u003e\u003e\n```\n*Note:* `pogodb.shellConnect(.)` is meant only for *quick and dirty* shell connections. You need to explicitly call `db.close()` to commit the transaction and close the connection.\n\nConnecting Properly\n-------------------------\n\n#### Context Manager:\n\nUsing `with pogodb.connect(.) as db` is a better way to connect. On exiting the `with` block, the transaction is auto-committed and the connection is auto-closed.\n```py\nimport pogodb\nwith pogodb.connect(\"postgres://..dsn..\") as db:\n    db.insertOne({\"_id\": \"bar\", \"value\": \"foobar\"})\n    # etc. ...\n```\n\n#### Connection Decorator:\n\nFor frequently connecting to the same database, consider setting up a connection decorator as follows:\n```py\nimport pogodb\ndbConnect = pogodb.makeConnector(\"postgres://..dsn..\")\n\n@dbConnect\ndef yourLogic (db):\n    db.insertOne({\"_id\": \"baz\", \"value\": \"quax\"})\n    # etc. ...\n```\nThe decorator supplies the `db` parameter to the decorated function. The parameter is supplied by name, so it must be called `db`, not `myDb` or something else. That is, `@dbConnect` automatically passes `db` to `yourLogic`, on each call.\n\n#### Parameter `skipSetup`:\nBoth `pogodb.connect(.)` and `pogodb.makeConnector(.)` accept `skipSetup` as a parameter, which defaults to `False`. By default, PogoDB runs some setup-code upon each connection.\n\n_After_ your first interaction with the the database through  PogoDB, to _avoid_ unnecessary setup, pass `skipSetup=True`.\n\n#### Parameter `verbose`:\nEach connection method accepts `verbose` as a parameter, defaulting to `False`. If `True`, details regarding connecting to Postgres and executing SQL are printed using `print(.)`.\n\nInserting Data\n------------------\n```py\n# Insert a single document:\ndb.insertOne({\n   \"_id\":\"a\", \"author\":\"Alice\", \"text\":\"AA\", \"rank\":0,\n})\n# Insert multiple documents:\ndb.insertMany([\n  {\"_id\":\"b\", \"author\":\"Becci\", \"text\":\"BB\", \"rank\":1},\n  {\"_id\":\"c\", \"author\":\"Cathy\", \"text\":\"CC\", \"rank\":2},\n  {\"_id\":\"d\", \"author\":\"Alice\", \"text\":\"DD\", \"rank\":1},\n]);\n```\n\n#### Document Model:\nEach document must:\n- be a JSON-serializable `dict` or dict-like object, *and*\n- have a *unique string* value corresponding to the `\"_id\"` key.\n\nRetrieving Data\n-------------------\nIn continuation with the above code snippet ...\n```py\n# Find by _id:\ntaskA = db.findOne(\"a\");\nprint(taskA.author, \"-\", taskA.text)\n# Output: Alice - AA\n\n# Find by sub-document:\ntaskB = db.findOne({\"author\": \"Becci\"});\nprint(taskB.author, \"-\", taskB.text)\n# Output: Becci - BB\n\n# Find multiple:\naliceTasks = db.find({\"author\": \"Alice\"})\nassert aliceTasks[0] == taskA and len(aliceTasks) == 2\ntaskD = aliceTasks[1];\nprint(taskD.author, \"-\", taskD.text)\n# Output: Alice - DD\n```\nNote: If no matching document is found, `.findOne(.)` returns `None` while `.find(.)` returns an empty list.\n\nUpdating Data\n------------------\nIn continuation with the above code snippet ...\n```py\n# Replace document:\ntaskA.text = \"New AA\"           # \u003c-- Update in-memory\ntaskA.x = {\"y\": 10, \"z\": 20}\ndb.replaceOne(taskA);           # \u003c-- Propagate to db\nprint([db.findOne(taskA._id).text, taskA.x])\n# Output: ['New AA', {'y': 10, 'z': 20}]\n\n# Increment within document:\ndb.incr({\"_id\": \"a\"}, \"x.y\", 1) # Incr x.y by 1\nprint(db.findOne(\"a\").x)\n# Output: {'y': 11, 'z': 20}\n\n# Decrement:\ndb.decr({\"_id\": \"a\"}, \"x.z\", 1) # Decr x.z by 1\nprint(db.findOne(\"a\").x)\n# Output: {'y': 11, 'z': 19}\n```\n\nDeleting Data\n-----------------\nIn continuation with the above code snippet ...\n```py\n# Delete by _id:\ndb.deleteOne(\"a\");\nprint(db.findOne(\"a\"))\n# Output: None\n```\nAs of writing, you can only delete one document at a time, by `_id`.\n\nQuick Plug\n--------------\nPogoDB built and maintained by the folks at [Polydojo, Inc.](https://www.polydojo.com/), led by Sumukh Barve. If your team is looking for a simple project management tool, please check out our latest product: [BoardBell.com](https://www.boardbell.com/).\n\nType Identifiers\n-------------------\n\nPogoDB doesn't include buckets, collections or other such concepts for **logically grouping** different types of objects. But you can use a key for differentiating objects of various types.\n\n#### Convention:\nKeeping things simple, we recommend using the `\"type\"` key for indicating the type of a document/object.\n\n#### Example:\nIn a blogging app, you'll have to deal with users, posts, comments and other types of object. \n\n```py\n# Insert users:\ndb.insertMany([\n    {\"_id\": \"00\", \"type\":\"user\", \"name\": \"Alice\"},\n    {\"_id\": \"01\", \"type\":\"user\", \"name\": \"Becci\"},\n    {\"_id\": \"02\", \"type\":\"user\", \"name\": \"Cathy\"},\n]);\n\n# Insert posts:\ndb.insertMany([\n    {\"_id\": \"03\", \"type\":\"post\", \"authorId\": \"00\",\n        \"title\": \"Title X .. \", \"body\": \"Body X ..\"},\n    {\"_id\": \"04\", \"type\":\"post\", \"authorId\": \"01\",\n        \"title\": \"Title Y .. \", \"body\": \"Body Y ..\"},\n    {\"_id\": \"05\", \"type\":\"post\", \"authorId\": \"02\",\n        \"title\": \"Title Z .. \", \"body\": \"Body Z ..\"},\n    {\"_id\": \"06\", \"type\":\"post\", \"authorId\": \"00\",\n        \"title\": \"Title A .. \", \"body\": \"Body A ..\"},\n]);\n\n# Insert comments:\ndb.insertMany([\n    {\"_id\": \"07\", \"type\":\"comment\", \"authorId\": \"02\",\n        \"postId\": \"03\", \"text\": \"Comment P ..\"},\n    {\"_id\": \"08\", \"type\":\"comment\", \"authorId\": \"01\",\n        \"postId\": \"04\", \"text\": \"Comment Q ..\"},\n    {\"_id\": \"09\", \"type\":\"comment\", \"authorId\": \"00\",\n        \"postId\": \"05\", \"text\": \"Comment R ..\"},\n]);\n\n# Get all users:\ndb.find({\"type\": \"user\"});\n\n# Get all posts:\ndb.find({\"type\": \"post\"});\n\n# Get posts by a specific author:\ndef getPostByAuthor (userId):\n    return db.find({\"type\":\"post\", \"authorId\":userId})\n\n# Get comments on a specific post:\ndef getCommentsByPost (postId):\n    return db.find({\"type\":\"comment\", \"postId\":postId})\n\n# Get comments by a specific user:\ndef getCommentsByUser (userId):\n    return db.find({\"type\":\"user\", \"authorId\":userId})\n```\n\nAs you can see, using the `\"type\"` key allows us to limit our query to a specific type. In continuation with the above code snippet, consider ...\n```py\ndef typed_getPostById (postId):\n    return db.findOne({\"_id\": postId, \"type\": \"post\"});\n\ndef untyped_getPostById (postId):\n    return db.findOne({\"_id\": postId})\n\nprint(typed_getPostById(\"00\"))   # Correct result.\n# Output: None\n\nprint(untyped_getPostById(\"00\")) # Weird result.\n# Output: {'_id': '00', 'name': 'Alice', 'type': 'user'} \n```\n\nIn the above example, `\"00\"` corresponds to Alice's `\"user\"` object. It's not a `\"post\"`. Yet `untyped_getPostById(.)` (incorrectly) returns it because it is type-blind.\n\nSQL Familiarity\n-------------------\n\nFrom this point, the documentation assumes basic familiarity with SQL and Postgres' JSONB type. If you aren't familiar with these, you may safely skip most of the documentation below. However, please note that such familiarity would be required for running advanced, fine-grained queries.\n\nUnder The Hood\n---------------------\nUnder the hood, PogoDB creates a single table named `pogotbl` with a single `JSONB` column named `doc` (for document).\n\nWhen you call `db.find(.)`, PogoDB uses Postgres' `@\u003e` to find and fetch the relevant documents. For example, calling `db.find({\"type\": \"post\"})` will result in the following underlying SQL query:\n```sql\nSELECT doc FROM pogotbl WHERE doc @\u003e '{\"type\": \"post\"}';\n```\nThe above SQL will produce a list of records of type`psycopg2.extras.RealDictCursor`, each with just one column: `\"doc\"`. That is, the list of records is of the form:\n```json\n[   {\"doc\": {\"_id\": \"1..\", \"type\": \"post\", \"etc\": \"...\"}},\n    {\"doc\": {\"_id\": \"2..\", \"type\": \"post\", \"etc\": \"...\"}},\n    \"etc. ...\"\n]\n```\nAfter executing the SQL, `db.find(.)` plucks the `\"doc\"` column from each record and returns the resultant list, which is (as expected,) of the form:\n```json\n[   {\"_id\": \"1..\", \"type\": \"post\", \"etc\": \"...\"},\n    {\"_id\": \"2..\", \"type\": \"post\", \"etc\": \"...\"},\n    \"etc. ...\"\n]\n```\nAdditionally, `db.find(.)` ensures that each returned document is a dot-accessible dictionary, thanks to [Dotsi](https://github.com/polydojo/dotsi). That is, you can use dot-notation (like `post._id`) in addition to square-bracket notation (like `post[\"_id\"]`).\n\nCustom `WHERE` Clause\n------------------------------------\n\nLet's say you've stored the following exam-results using PogoDB:\n```json\n[   {\"_id\":\"1\", \"studentId\":\"X\", \"subjectId\":\"M\", \"score\": 70},\n    {\"_id\":\"2\", \"studentId\":\"Y\", \"subjectId\":\"M\", \"score\": 75},\n    {\"_id\":\"3\", \"studentId\":\"Z\", \"subjectId\":\"M\", \"score\": 80},\n    {\"_id\":\"4\", \"studentId\":\"X\", \"subjectId\":\"N\", \"score\": 85},\n    {\"_id\":\"5\", \"studentId\":\"Y\", \"subjectId\":\"N\", \"score\": 90},\n    {\"_id\":\"6\", \"studentId\":\"Z\", \"subjectId\":\"N\", \"score\": 95},\n]\n```\n\nTo find *all* results for *Subject M*, we'd write `db.find({\"subjectId\": \"M\"})`, which'd result in the underlying SQL query:\n```sql\nSELECT doc FROM pogotbl WHERE doc @\u003e '{\"subjectId\": \"M\"}';\n```\n\nBut how about retrieving ***only those*** results for *Subject M*, where the *score* is `75` or higher? In raw SQL, we could've written:\n```sql\nSELECT doc FROM pogotbl\n  WHERE doc @\u003e '{\"subjectId\": \"M\"}'\n    AND (doc-\u003e\u003e'score')::int \u003e= 75;\n```\n\nWith regard to the two SQL queries above, note that the `WHERE` clause additionally includes `AND (doc-\u003e\u003e'score')::int \u003e= 75`. You can pass this extra bit to `db.find(.)` using the `whereEtc` parameter:\n```py\ndb.find({\"subjectId\": \"M\"},\n    whereEtc=\"AND (doc-\u003e\u003e'score')::int \u003e= 75\"\n)\n```\n\nIn fact, `db.find(.)` is very flexible. Its full signature is documented below.\n\nFull `db.find(.)` Signature\n----------------------------------\n\n`db.find(.)` accepts 4 parameters:\n1. `subdoc` (required): The sub-document to match against.\n2. `whereEtc` (optional): Anything that should go ***after*** PogoDB's  default SQL `WHERE` clause.\n3. `argsEtc` (optional): Tuple (or list) for placeholder-substitution against `whereEtc`.\n4. `limit` (optional): The maximum number of results desired. (Either use this param or add the SQL `LIMIT` clause in `whereEtc`; don't do both.)\n\n**Note:** `db.findOne(.)` has the same signature as `db.find(.)`, except of course, that it doesn't have a `limit` parameter (and neither does it expect to see the `LIMIT` clause in `whereEtc`).\n\nClauses `ORDER BY`, `LIMIT`  etc.\n-----------------------------------------\nEverything in `whereEtc` is placed directly in the executed SQL. (Of course, placeholder-substitution is performed carefully. More on this later.) Thus, by using `whereEtc`, not only can you specify additional matching conditions (like `AND (doc-\u003e\u003e'score')::int \u003e= 75`), but you can also include other SQL clauses such as `ORDER BY`, `LIMIT` etc.\n\n### Sorting:\nContinuing the above exam-results example, to find results for *Subject M* sorted by *Student IDs* (lowest to highest):\n```py\ndb.find({\"subjectId\": \"M\"},\n    whereEtc=\"ORDER BY doc-\u003e\u003e'studentId' ASC\"\n)\n```\nThe underlying SQL executed by PogoDB will be:\n```sql\nSELECT doc FROM pogotbl WHERE doc @\u003e '{\"subjectId\": \"M\"}'\n    ORDER BY doc-\u003e\u003e'studentId' ASC;\n```\n\n### Limiting:\nTo find the *top 2* results for *Subject M*, we can use:\n```py\ndb.find({\"subjectId\": \"M\"},\n    whereEtc=\"ORDER BY (doc-\u003e\u003e'score')::int DESC\",\n    limit=2\n)\n```\nOr equivalently:\n```py\ndb.find({\"subjectId\": \"M\"},\n    whereEtc=\"ORDER BY (doc-\u003e\u003e'score')::int DESC LIMIT 2\"\n)\n```\nIn either case, the underlying SQL executed by PogoDB will be:\n```sql\nSELECT doc FROM pogotbl WHERE doc @\u003e '{\"subjectId\": \"M\"}'\n    ORDER BY (doc-\u003e\u003e'score')::int DESC\n    LIMIT 2;\n```\n\n### Placeholders:\nLet's write a function for finding the top N (`n`) results for a given subject (`subjectId`), at or above a given threshold (`minScore`).\n```py\nimport pogodb;\ndbConnect = pogodb.makeConnector(\"postgres://..dsn..\");\n\n@dbConnect\ndef getTopN (n, subjectId, minScore, db):\n  return db.find({\"subjectId\": subjectId},\n    whereEtc=\"AND (doc-\u003e\u003e'score')::int \u003e= %s ORDER BY (doc-\u003e\u003e'score')::int DESC\",\n    argsEtc=[minScore],\n    limit=n\n  );\n```\nAlternatively:\n```py\n@dbConnect\ndef getTopN (n, subjectId, minScore, db):\n  return db.find({\"subjectId\": subjectId},\n    whereEtc=\"AND (doc-\u003e\u003e'score')::int \u003e= %s ORDER BY (doc-\u003e\u003e'score')::int DESC LIMIT %s\",\n    argsEtc=[minScore, n],\n  );\n```\nNote: Placeholder substitution is deferred to [Psycopg's `cursor.execute(.)` method](https://www.psycopg.org/docs/cursor.html#cursor.execute), which should prevent [SQL-injection](https://owasp.org/www-community/attacks/SQL_Injection).\n\n**WARNING:** Do **NOT** use string concatenation (i.e. `+`, `str.join(.)`, etc.) or string interpolation (i.e. `%`, `str.format(.)`,  etc.) along with `whereEtc`. Pass `argsEtc` instead.\n\n\nExecuting Raw SQL\n------------------------\nIf you'd like to execute raw SQL, we recommend using [Psycopg](https://www.psycopg.org/) directly. We recommend *against* using `db._execute(.)`.\n\nTypically, `db._execute(.)` should only be relevant to PogoDB's maintainers. It accepts three parameters:\n1. `stmt` (required): The SQL statement to be executed.\n2. `args` (optional): Tuple (or list) for `%s` placeholder substitution.\n3. `fetch` (optional): Either `None` (optional), `\"one\"` or `\"all\"`.\n\nParameters `stmt` and `args` are passed directly to Psycopg's `cursor.execute(.)` method. Based on `fetch`, none, one or all records are fetched.\n\nA close cousin to `db._execute(.)` is `db._findSql(.)`, which is useful for executing `SELECT` queries. It only accepts `stmt` (required) and `args` (optional), as described above. It fetches all matching results, plucks the `doc` column, ensures dot-accessibility of dictionary objects, and returns the result.\n\nLicensing\n------------\nCopyright (c) 2020 Polydojo, Inc.\n\n**Software Licensing:**  \nThe software is released \"AS IS\" under the **Apache License 2.0**, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Kindly see [LICENSE.txt](https://github.com/polydojo/pogodb/blob/master/LICENSE.txt) for more details.\n\n**No Trademark Rights:**  \nThe above software licensing terms **do not** grant any right in the trademarks, service marks, brand names or logos of Polydojo, Inc.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolydojo%2Fpogodb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpolydojo%2Fpogodb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolydojo%2Fpogodb/lists"}