{"id":13798842,"url":"https://github.com/jsayol/FireSQL","last_synced_at":"2025-05-13T06:31:39.333Z","repository":{"id":51759307,"uuid":"157123541","full_name":"jsayol/FireSQL","owner":"jsayol","description":"Query Firestore using SQL syntax","archived":false,"fork":false,"pushed_at":"2021-05-10T06:18:51.000Z","size":1341,"stargazers_count":351,"open_issues_count":21,"forks_count":32,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-05-06T23:35:54.153Z","etag":null,"topics":["firebase","firestore","nodejs","sql","typescript-library"],"latest_commit_sha":null,"homepage":"https://devlibrary.withgoogle.com/products/firebase/repos/jsayol-FireSQL","language":"TypeScript","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/jsayol.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}},"created_at":"2018-11-11T21:37:01.000Z","updated_at":"2025-03-23T19:04:33.000Z","dependencies_parsed_at":"2022-08-23T05:51:19.107Z","dependency_job_id":null,"html_url":"https://github.com/jsayol/FireSQL","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsayol%2FFireSQL","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsayol%2FFireSQL/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsayol%2FFireSQL/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsayol%2FFireSQL/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jsayol","download_url":"https://codeload.github.com/jsayol/FireSQL/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253547858,"owners_count":21925657,"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":["firebase","firestore","nodejs","sql","typescript-library"],"created_at":"2024-08-04T00:00:55.116Z","updated_at":"2025-05-13T06:31:38.811Z","avatar_url":"https://github.com/jsayol.png","language":"TypeScript","readme":"# FireSQL - Query Firestore using SQL syntax\n\n## What is FireSQL?\n\nFireSQL is a library built on top of the official Firebase SDK that allows you to query Cloud Firestore using SQL syntax. It's smart enough to issue the minimum amount of queries necessary to the Firestore servers in order to get the data that you request.\n\nOn top of that, it offers some of the handy utilities that you're used to when using SQL, so that it can provide a better querying experience beyond what's offered by the native querying methods.\n\n## Installation\n\nJust add `firesql` and `firebase` to your project:\n\n```sh\nnpm install firesql firebase\n# or\nyarn add firesql firebase\n```\n\nIf you want to receive realtime updates when querying, then you will also need to install `rxjs` and `rxfire`:\n\n```sh\nnpm install firesql firebase rxjs rxfire\n# or\nyarn add firesql firebase rxjs rxfire\n```\n\n## Usage\n\n```js\n// You can either query the collections at the root of the database...\nconst dbRef = firebase.firestore();\n\n// ... or the subcollections of some document\nconst docRef = firebase.firestore().doc('someDoc');\n\n// And then just pass that reference to FireSQL\nconst fireSQL = new FireSQL(dbRef);\n\n// Use `.query()` to get a one-time result\nfireSQL.query('SELECT * FROM myCollection').then(documents =\u003e {\n  documents.forEach(doc =\u003e {\n    /* Do something with the document */\n  });\n});\n\n// Use `.rxQuery()` to get an observable for realtime results.\n// Don't forget to import \"firesql/rx\" first (see example below).\nfireSQL.rxQuery('SELECT * FROM myCollection').subscribe(documents =\u003e {\n  /* Got an update with the documents! */\n});\n\n```\n\n## Examples\n\n### One-time result (Promise)\n\n```js\nimport { FireSQL } from 'firesql';\nimport firebase from 'firebase/app';\nimport 'firebase/firestore';\n\nfirebase.initializeApp({ /* ... */ });\n\nconst fireSQL = new FireSQL(firebase.firestore());\n\nconst citiesPromise = fireSQL.query(`\n  SELECT name AS city, country, population AS people\n  FROM cities\n  WHERE country = 'USA' AND population \u003e 700000\n  ORDER BY country, population DESC\n  LIMIT 10\n`);\n\ncitiesPromise.then(cities =\u003e {\n  for (const city of cities) {\n    console.log(\n      `${city.city} in ${city.country} has ${city.people} people`\n    );\n  }\n});\n```\n\n### Realtime updates (Observable)\n\n```js\nimport { FireSQL } from 'firesql';\nimport firebase from 'firebase/app';\nimport 'firesql/rx'; // \u003c-- Important! Don't forget\nimport 'firebase/firestore';\n\nfirebase.initializeApp({ /* ... */ });\n\nconst fireSQL = new FireSQL(firebase.firestore());\n\nconst cities$ = fireSQL.rxQuery(`\n  SELECT city, category, AVG(price) AS avgPrice\n  FROM restaurants\n  WHERE category IN (\"Mexican\", \"Indian\", \"Brunch\")\n  GROUP BY city, category\n`);\n\ncities$.subscribe(results =\u003e {\n  /* REALTIME AGGREGATED DATA! */\n});\n```\n\n## Limitations\n\n- Only `SELECT` queries for now. Support for `INSERT`, `UPDATE`, and `DELETE` might come in the future.\n- No support for `JOIN`s.\n- `LIMIT` doesn't accept an `OFFSET`, only a single number.\n- No support for aggregate function `COUNT`.\n- If using `GROUP BY`, it cannot be combined with `ORDER BY` nor `LIMIT`.\n- No support for negating conditions with `NOT`.\n- Limited `LIKE`. Allows for searches in the form of `WHERE field LIKE 'value%'`, to look for fields that begin with the given value; and `WHERE field LIKE 'value'`, which is functionally equivalent to `WHERE field = 'value'`.\n\n## Nested objects\nYou can access nested objects by using backticks around the field path. For example, if you have a collection \"*products*\" with documents like this:\n```js\n{\n  productName: \"Firebase Hot Sauce\",\n  details: {\n    available: true,\n    stock: 42\n  }\n}\n```\nYou could do the following queries:\n```sql\nSELECT *\nFROM products\nWHERE `details.stock` \u003e 10\n```\n```sql\nSELECT productName, `details.stock` AS productStock\nFROM products\nWHERE `details.available` = true\n```\n\n## Getting the document IDs\nYou can use the special field `__name__` to refer to the document ID (its key inside a collection). For convenience, you might want to alias it:\n```sql\nSELECT __name__ AS docId, country, population\nFROM cities\n```\n\nIf you always want to include the document ID, you can specify that as a global option to the FireSQL class:\n```js\nconst fireSQL = new FireSQL(ref, { includeId: true}); // To include it as \"__name__\"\nconst fireSQL = new FireSQL(ref, { includeId: 'fieldName'}); // To include it as \"fieldName\"\n```\n\nYou can also specify that option when querying. This will always take preference over the global option:\n```js\nfireSQL.query(sql, { includeId: 'id'}); // To include it as \"id\"\nfireSQL.query(sql, { includeId: false}); // To not include it\n```\n\nWhen querying it's also possible to use the document as a search field by using `__name__` directly. For example, you could search for all the documents whose IDs start with `Hello`:\n```sql\nSELECT *\nFROM cities\nWHERE __name__ LIKE 'Hello%'\n```\n\n\u003e **Note**: You will need to specify the `includeId` option if you want to obtain the document IDs when doing a `SELECT *` query.\n\n## Collection group queries\nYou can easily do collection group queries with FireSQL!\n\nThis query will get all documents from any collection or subcollection named \"landmarks\":\n```sql\nSELECT *\nFROM GROUP landmarks\n```\n\nYou can [read more about collection group queries](https://firebase.google.com/docs/firestore/query-data/queries#collection-group-query) in the official Firestore documentation.\n\n## Array membership queries\nIt's as simple as using the `CONTAINS` condition:\n```sql\nSELECT *\nFROM posts\nWHERE tags CONTAINS 'interesting'\n```\n\nYou can [read more about array membership queries](https://firebase.google.com/docs/firestore/query-data/queries#array_membership) in the official Firestore documentation.\n\n## How does FireSQL work?\n\nFireSQL transforms your SQL query into one or more queries to Firestore. Once all the necessary data has been retrieved, it does some internal processing in order to give you exactly what you asked for.\n\nFor example, take the following SQL:\n```sql\nSELECT *\nFROM cities\nWHERE country = 'USA' AND population \u003e 50000\n```\nThis would get transformed into this single Firestore query:\n```js\ndb.collection('cities')\n  .where('country', '==', 'USA')\n  .where('population', '\u003e', 50000);\n```\nThat's pretty straightforward. But what about this one?\n```sql\nSELECT *\nFROM cities\nWHERE country = 'USA' OR population \u003e 50000\n```\nThere's no direct way to perform an `OR` query on Firestore so FireSQL splits that into 2 separate queries:\n```js\ndb.collection('cities').where('country', '==', 'USA');\ndb.collection('cities').where('population', '\u003e', 50000);\n```\nThe results are then merged and any possible duplicates are eliminated.\n\nThe same principle applies to any other query. Sometimes your SQL will result in a single Firestore query and some other times it might result in several.\n\nFor example, take a seemingly simple SQL statement like the following:\n```sql\nSELECT *\nFROM cities\nWHERE country != 'Japan' AND region IN ('north', 'east', 'west') AND (capital = true OR population \u003e 100000)\n```\n\nThis will need to launch a total of 12 concurrent queries to Firestore!\n```js\nconst cities = db.collection('cities');\ncities.where('country', '\u003c', 'Japan').where('region', '==', 'north').where('capital', '==', true);\ncities.where('country', '\u003c', 'Japan').where('region', '==', 'north').where('population', '\u003e', 100000);\ncities.where('country', '\u003c', 'Japan').where('region', '==', 'east').where('capital', '==', true);\ncities.where('country', '\u003c', 'Japan').where('region', '==', 'east').where('population', '\u003e', 100000);\ncities.where('country', '\u003c', 'Japan').where('region', '==', 'west').where('capital', '==', true);\ncities.where('country', '\u003c', 'Japan').where('region', '==', 'west').where('population', '\u003e', 100000);\ncities.where('country', '\u003e', 'Japan').where('region', '==', 'north').where('capital', '==', true);\ncities.where('country', '\u003e', 'Japan').where('region', '==', 'north').where('population', '\u003e', 100000);\ncities.where('country', '\u003e', 'Japan').where('region', '==', 'east').where('capital', '==', true);\ncities.where('country', '\u003e', 'Japan').where('region', '==', 'east').where('population', '\u003e', 100000);\ncities.where('country', '\u003e', 'Japan').where('region', '==', 'west').where('capital', '==', true);\ncities.where('country', '\u003e', 'Japan').where('region', '==', 'west').where('population', '\u003e', 100000);\n```\nAs you can see, SQL offers a very concise and powerful way to express your query. But as they say, ***with great power comes great responsibility***. Always be mindful of the underlying data model when using FireSQL.\n\n## Examples of supported queries:\n\n```sql\nSELECT *\nFROM restaurants\n```\n\n```sql\nSELECT name, price\nFROM restaurants\nWHERE city = 'Chicago'\n```\n\n```sql\nSELECT *\nFROM restaurants\nWHERE category = 'Indian' AND price \u003c 50\n```\n\n```sql\nSELECT *\nFROM restaurants\nWHERE name LIKE 'Best%'\n```\n\n```sql\nSELECT *\nFROM restaurants\nWHERE name LIKE 'Best%' OR city = 'Los Angeles'\n```\n\n```sql\nSELECT *\nFROM restaurants\nWHERE city IN ( 'Raleigh', 'Nashvile', 'Denver' )\n```\n\n```sql\nSELECT *\nFROM restaurants\nWHERE city != 'Oklahoma'\n```\n\n```sql\nSELECT *\nFROM restaurants\nWHERE favorite = true\n```\n\n```sql\nSELECT *\nFROM restaurants\nWHERE favorite -- Equivalent to the previous one\n```\n\n```sql\nSELECT *\nFROM restaurants\nWHERE favorite IS NULL\n```\n\n```sql\nSELECT AVG(price) AS averagePriceInChicago\nFROM restaurants\nWHERE city = 'Chicago'\n```\n\n```sql\nSELECT city, MIN(price), AVG(price), MAX(price)\nFROM restaurants\nWHERE category = 'Indian'\nGROUP BY city\n```\n\n```sql\nSELECT *\nFROM restaurants\nWHERE city = 'Memphis' AND ( price \u003c 40 OR avgRating \u003e 8 )\nORDER BY price DESC, avgRating\n```\n\n```sql\nSELECT *\nFROM restaurants\nWHERE price BETWEEN 25 AND 150\nORDER BY city, price\nLIMIT 10\n```\n\n```sql\nSELECT *\nFROM restaurants\nWHERE city = 'Chicago'\nUNION\nSELECT *\nFROM restaurants\nWHERE price \u003e 200\n```\n","funding_links":[],"categories":["웹"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsayol%2FFireSQL","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjsayol%2FFireSQL","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsayol%2FFireSQL/lists"}