PageSourceSearch

https://docs.getsquid.ai/assets/js/cbeaabd8.558d0e7c.js

js getsquid.ai collected 2026-10-05 12:03:17 UTC 46,225 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunksquid_docs=globalThis.webpackChunksquid_docs||[]).push([[6199],{1245(e,n,s){s.r(n),s.d(n,{assets:()=>d,contentTitle:()=>c,default:()=>h,frontMatter:()=>o,metadata:()=>t,toc:()=>l});const t=JSON.parse('{"id":"sdk/client-sdk/database/queries","title":"Queries","description":"Create fine-grained queries for your data from any source, including joining data across multiple databases with real-time query support.","source":"@site/docs/sdk/client-sdk/database/queries.md","sourceDirName":"sdk/client-sdk/database","slug":"/sdk/client-sdk/database/queries","permalink":"/docs/sdk/client-sdk/database/queries","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"sidebar_custom_props":{"emoji":"\ud83d\udd0d"}},"sidebar":"myAutogeneratedSidebar","previous":{"title":"Field projection","permalink":"/docs/sdk/client-sdk/database/project-fields"},"next":{"title":"Transactions","permalink":"/docs/sdk/client-sdk/database/transactions"}}');var r=s(4848),i=s(8453);const o={sidebar_custom_props:{emoji:"\ud83d\udd0d"}},c="Queries",d={},l=[{value:"Create fine-grained queries for your data from any source, including joining data across multiple databases with real-time query support.",id:"create-fine-grained-queries-for-your-data-from-any-source-including-joining-data-across-multiple-databases-with-real-time-query-support",level:6},{value:"Why Use Queries",id:"why-use-queries",level:2},{value:"Overview",id:"overview",level:2},{value:"Quick Start",id:"quick-start",level:2},{value:"Core Concepts",id:"core-concepts",level:2},{value:"Querying a single document",id:"querying-a-single-document",level:3},{value:"Querying multiple documents from a collection",id:"querying-multiple-documents-from-a-collection",level:3},{value:"De-referencing document references",id:"de-referencing-document-references",level:3},{value:"Joining data across collections and connectors",id:"joining-data-across-collections-and-connectors",level:3},{value:"Choosing the left side of the join",id:"choosing-the-left-side-of-the-join",level:4},{value:"Grouping the join results",id:"grouping-the-join-results",level:4},{value:"OR queries",id:"or-queries",level:3},{value:"Limits and sorting",id:"limits-and-sorting",level:3},{value:"Field projection",id:"field-projection",level:3},{value:"Pagination",id:"pagination",level:3},{value:"Query helpers",id:"query-helpers",level:3},{value:"Filtering by document ID",id:"filtering-by-document-id",level:3},{value:"Error Handling",id:"error-handling",level:2},{value:"Best Practices",id:"best-practices",level:2}];function a(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",h6:"h6",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"queries",children:"Queries"})}),"\n",(0,r.jsx)(n.h6,{id:"create-fine-grained-queries-for-your-data-from-any-source-including-joining-data-across-multiple-databases-with-real-time-query-support",children:"Create fine-grained queries for your data from any source, including joining data across multiple databases with real-time query support."}),"\n",(0,r.jsx)(n.h2,{id:"why-use-queries",children:"Why Use Queries"}),"\n",(0,r.jsx)(n.p,{children:"You need to retrieve specific subsets of data from your database, filter by conditions, sort results, join data across collections, or subscribe to real-time updates. The query API lets you do all of this with a chainable, type-safe interface."}),"\n",(0,r.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,r.jsx)(n.admonition,{title:"Note",type:"note",children:(0,r.jsxs)(n.p,{children:["Squid uses ",(0,r.jsx)(n.a,{href:"https://rxjs.dev/",children:"RxJs"})," for handling streams and observables. Learn more about RxJS and streaming updates in\nthe ",(0,r.jsx)(n.a,{href:"https://rxjs.dev/guide/overview",children:"RxJs documentation"}),"."]})}),"\n",(0,r.jsx)(n.p,{children:"When querying for document(s), you have the option of consuming a single snapshot or a stream of snapshots."}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["When consuming a snapshot, you receive the latest version of the document(s) as a ",(0,r.jsx)(n.code,{children:"Promise"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["When consuming a stream of snapshots, the result is an ",(0,r.jsx)(n.a,{href:"https://rxjs.dev/",children:"RxJs"})," ",(0,r.jsx)(n.code,{children:"Observable"})," that emits a new snapshot every time the query result changes."]}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"The Squid Client SDK's usage of snapshots and streams of data enables you to continuously receive real-time updates\nfrom your data sources with minimal overhead and setup."}
1),"\n",(0,r.jsx)(n.h2,{id:"quick-start",children:"Quick Start"}),"\n",(0,r.jsxs)(n.p,{children:["Build a query by chaining filter methods on a ",(0,r.jsx)(n.a,{href:"/docs/sdk/client-sdk/database/collection-reference",children:"collection reference"}),", then call ",(0,r.jsx)(n.code,{children:"snapshot()"})," to get results:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"interface User {\n  id: string;\n  name: string;\n  age: number;\n  role: string;\n}\n\n// Query all admin users over 18\nconst admins = await squid\n  .collection<User>('users')\n  .query()\n  .gt('age', 18)\n  .eq('role', 'admin')\n  .snapshot();\n\n// Access data from each result\nfor (const userRef of admins) {\n  console.log(userRef.data.name);\n}\n"})}),"\n",(0,r.jsx)(n.h2,{id:"core-concepts",children:"Core Concepts"}),"\n",(0,r.jsx)(n.h3,{id:"querying-a-single-document",children:"Querying a single document"}),"\n",(0,r.jsxs)(n.p,{children:["To query a single document, call the ",(0,r.jsx)(n.code,{children:"snapshot"})," or ",(0,r.jsx)(n.code,{children:"snapshots"})," method on a ",(0,r.jsx)(n.a,{href:"/docs/sdk/client-sdk/database/document-reference",children:"document reference"}),". The result is the document data directly (type ",(0,r.jsx)(n.code,{children:"T | undefined"}),"):"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const user = await squid.collection<User>('users').doc('user_id').snapshot();\nif (user) {\n  console.log(user.name);\n}\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Alternatively, ",(0,r.jsx)(n.code,{children:"subscribe"})," to changes on this document using the ",(0,r.jsx)(n.code,{children:"snapshots"})," method. Each time the document changes, the observable emits the latest data."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"squid\n  .collection<User>('users')\n  .doc('user_id')\n  .snapshots()\n  .subscribe((user) => {\n    if (user) {\n      console.log(user.name);\n    }\n  });\n"})}),"\n",(0,r.jsx)(n.admonition,{title:"Securing your queries",type:"tip",children:(0,r.jsxs)(n.p,{children:["Squid's backend security rules allow you to control who can perform each specific query. These rules receive the\n",(0,r.jsx)(n.code,{children:"QueryContext"})," as a parameter, which contains the query. Set up backend security to restrict read access to specific collections or database connectors. To learn how to restrict read and write privileges for your databases, view the ",(0,r.jsx)(n.a,{href:"/docs/security/security-rules/secure-data-access",children:"docs on security rules"}),"."]})}),"\n",(0,r.jsx)(n.h3,{id:"querying-multiple-documents-from-a-collection",children:"Querying multiple documents from a collection"}),"\n",(0,r.jsxs)(n.p,{children:["When querying documents from a collection, use the ",(0,r.jsx)(n.code,{children:"query"})," method to build a query."]}),"\n",(0,r.jsxs)(n.p,{children:["Squid provides options to consume either a single query result using the ",(0,r.jsx)(n.code,{children:"snapshot"})," method or a stream of query results using the ",(0,r.jsx)(n.code,{children:"snapshots"})," method. When consuming a stream of query results in this way, the observable emits a new value each time the query results change."]}),"\n",(0,r.jsx)(n.p,{children:"Here's an example of how to get a single query snapshot that returns all the admins with an age above 18:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const users = await squid\n  .collection<User>('users')\n  .query()\n  .gt('age', 18)\n  .eq('role', 'admin')\n  .snapshot();\n"})}),"\n",(0,r.jsxs)(n.p,{children:["The following example receives streaming query results using the ",(0,r.jsx)(n.code,{children:"snapshots"})," method:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const usersObs = squid\n  .collection<User>('users')\n  .query()\n  .gt('age', 18)\n  .eq('role', 'admin')\n  .snapshots();\n\n/* Subscribe to the users observable, and log the data each time a new value is received */\nusersObs.subscribe((users) => {\n  console.log(\n    'Got new snapshot:',\n    users.map((user) => user.data)\n  );\n});\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Query also supports returning a stream of changes using the ",(0,r.jsx)(n.code,{children:"changes"})," method. The observable\nreturned by this method contains three different arrays that track changes made to the collection:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"inserts"}),": Contains document references to new insertions into the collection"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"updates"}),": Contains document references to updates in the collection"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"deletes"}),": Contains data from deleted documents"]}),"\n"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const usersObs = squid\n  .collection<User>('users')\n  .query()\n  .gt('age', 18)\n  .eq('role', 'admin')\n  .changes();\n\nusersObs.subscribe((changes) => {\n  // Logs all new insertions into the collection of admins who are 18+\n  console.log(\n    'Inserts:',\n    changes.inserts.map((user) => user.data)\n  );\n  // Logs new updates in the collection where the user is now an admin who is 18+\n  console.log(\n    'Updates:',\n    changes.updates.map((user) => user.data)\n  );\n  // The deletes array contains the actual deleted data without a doc reference\n  console.log('Deletes:', changes.deletes);\n});\n"})}),"\n",(0,r.jsx)(n.h3,{id:"de-referencing-document-references",children:"De-referencing document references"}),"\n",(0,r.jsxs)(n.p,{children:["When querying data from a collection, you receive document references. You can access the data from the document by\ncalling the ",(0,r.jsx)(n.code,{children:"data"})," getter."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const usersObs = squid\n  .collection<User>('users')\n  .query()\n  .snapshots()\n  .pipe(map((user) => user.data));\n"})}),"\n",(0,r.jsxs)(n.p,{children:["To receive the document data directly without calling the ",(0,r.jsx)(n.code,{children:"data"})," getter, call the ",(0,r.jsx)(n.code,{children:"dereference"})," method."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const usersDataObs = squid\n  .collection<User>('users')\n  .query()\n  .dereference()\n  .snapshots();\n"})}),"\n",(0,r.jsx)(n.h3,{id:"joining-data-across-collections-and-connectors",children:"Joining data across collections and connectors"}),"\n",(0,r.jsx)(n.p,{children:"Squid allows you to join multiple queries and listen for result changes. This feature is made even more powerful by the ability to join data from different data sources."}),"\n",(0,r.jsxs)(n.p,{children:["For example, you can join a ",(0,r.jsx)(n.code,{children:"dept"})," collection and ",(0,r.jsx)(n.code,{children:"employees"})," collection with a query to return all the employees above age 18 with their departments:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const departmentCollection = squid.collection<Dept>('dept');\nconst employeeCollection = squid.collection<Employee>('employees');\nconst employeeQuery = employeeCollection.query().gt('age', 18);\n\nconst joinObs = departmentCollection\n  .joinQuery('d')\n  .join(employeeQuery, 'e', {\n    left: 'id',\n    right: 'deptId',\n  })\n  .snapshots();\n\njoinObs.subscribe((joinResult) => {\n  // Use the join result here\n});\n"})}),"\n",(0,r.jsxs)(n.p,{children:["In the above code, each query is assigned an alias: ",(0,r.jsx)(n.code,{children:"d"})," for the ",(0,r.jsx)(n.code,{children:"dept"})," collection and ",(0,r.jsx)(n.code,{children:"e"})," for the ",(0,r.jsx)(n.code,{children:"employees"})," collection.\nThe join condition joins the ",(0,r.jsx)(n.code,{children:"deptId"})," field in the ",(0,r.jsx)(n.code,{children:"employees"})," collection with the ",(0,r.jsx)(n.code,{children:"id"})," field in the ",(0,r.jsx)(n.code,{children:"dept"})," collection."]}),"\n",(0,r.jsxs)(n.p,{children:["While this example shows joining two collections from the built-in database, you can join from separate database connectors by providing the connector IDs in the collection references. For example, if you had two database connectors with connector IDs of ",(0,r.jsx)(n.code,{children:"connectorA"})," and ",(0,r.jsx)(n.code,{children:"connectorB"}),", you can perform the same join as above by adding those connector IDs:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const departmentCollection = squid.collection<Dept>('dept', 'connectorA');\nconst employeeCollection = squid.collection<Employee>('employees', 'connectorB');\n"})}),"\n",(0,r.jsx)(n.p,{children:"By default, Squid performs left joins. This means that in this example, every department is included in the join result, including empty departments. For example, suppose there are two peo
1ple in department A over the age of 18, but no people in department B are over the age of 18. When performing the join query, the result is the following:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"type ResultType = Array<{\n  d: DocumentReference<Dept>;\n  e: DocumentReference<Employee> | undefined;\n}>;\n\njoinResult ===\n  [\n    { d: { data: { id: 'A' } }, e: { data: { id: 'employee1' } } },\n    { d: { data: { id: 'A' } }, e: { data: { id: 'employee2' } } },\n    { d: { data: { id: 'B' } }, e: undefined },\n  ];\n"})}),"\n",(0,r.jsxs)(n.p,{children:["To exclude results with undefined data, perform an inner join. To perform an inner join, pass ",(0,r.jsx)(n.code,{children:"{ isInner: true }"})," as the fourth parameter of the ",(0,r.jsx)(n.code,{children:"join"})," method. In the following example, department B returned:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const departmentCollection = squid.collection<Dept>('dept');\nconst employeeCollection = squid.collection<Employee>('employees');\nconst employeeQuery = employeeCollection.query().gt('age', 18);\n\nconst joinObs = departmentCollection\n  .joinQuery('d')\n  .join(\n    employeeQuery,\n    'e',\n    {\n      left: 'id',\n      right: 'deptId',\n    },\n    {\n      isInner: true,\n    }\n  )\n  .snapshots();\n\njoinObs.subscribe((joinResult) => {\n  // Use the join result here\n});\n\ntype ResultType = Array<{\n  d: DocumentReference<Dept>;\n  e: DocumentReference<Employee>; // Note no `| undefined`\n}>;\n\njoinResult ===\n  [\n    { d: { data: { id: 'A' } }, e: { data: { id: 'employee1' } } },\n    { d: { data: { id: 'A' } }, e: { data: { id: 'employee2' } } },\n  ];\n"})}),"\n",(0,r.jsxs)(n.p,{children:["To write a join between three collections, add another join to the query. For example, given collections for ",(0,r.jsx)(n.code,{children:"employees"}),", ",(0,r.jsx)(n.code,{children:"dept"})," and ",(0,r.jsx)(n.code,{children:"company"}),", you can perform the following join:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const departmentCollection = squid.collection<Dept>('dept');\nconst employeeCollection = squid.collection<Employee>('employees');\nconst employeeQuery = employeeCollection.query().gt('age', 18);\nconst companyQuery = squid.collection<Company>('company').query();\n\nconst joinObs = departmentCollection\n  .joinQuery('d')\n  .join(employeeQuery, 'e', {\n    left: 'id',\n    right: 'deptId',\n  })\n  .join(companyQuery, 'c', {\n    left: 'companyId',\n    right: 'id',\n  })\n  .snapshots();\n"})}),"\n",(0,r.jsxs)(n.p,{children:["The above example joins ",(0,r.jsx)(n.code,{children:"employees"})," with ",(0,r.jsx)(n.code,{children:"dept"})," and ",(0,r.jsx)(n.code,{children:"dept"})," with ",(0,r.jsx)(n.code,{children:"company"}),". The resulting object has the following type:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"type Result = Array<{\n  e: DocumentReference<Employee>;\n  d: DocumentReference<Dept> | undefined;\n  c: DocumentReference<Company> | undefined;\n}>;\n"})}),"\n",(0,r.jsx)(n.h4,{id:"choosing-the-left-side-of-the-join",children:"Choosing the left side of the join"}),"\n",(0,r.jsxs)(n.p,{children:["To choose the left side of a join, pass ",(0,r.jsx)(n.code,{children:"leftAlias"})," in the ",(0,r.jsx)(n.code,{children:"options"})," object as the fourth parameter of the ",(0,r.jsx)(n.code,{children:"join"})," method. For example, to join ",(0,r.jsx)(n.code,{children:"employee"})," with ",(0,r.jsx)(n.code,{children:"dept"}),"\nand ",(0,r.jsx)(n.code,{children:"employee"})," with ",(0,r.jsx)(n.code,{children:"company"}),", choose the left side of the join for the ",(0,r.jsx)(n.code,{children:"company"})," collection as shown in the following:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const departmentCollection = squid.collection<Dept>('dept');\nconst employeeCollection = squid.collection<Employee>('employees');\nconst employeeQuery = employeeCollection.query().gt('age', 18);\nconst companyQuery = squid.collection<Company>
1('company').query();\n\nconst joinObs = departmentCollection\n  .joinQuery('d')\n  .join(employeeQuery, 'e', {\n    left: 'id',\n    right: 'deptId',\n  })\n  .join(companyQuery, 'c', {\n{ left: 'companyId', right: 'id' },\n{ leftAlias: 'e' }\n)\n.snapshots();\n"})}),"\n",(0,r.jsx)(n.h4,{id:"grouping-the-join-results",children:"Grouping the join results"}),"\n",(0,r.jsxs)(n.p,{children:["To group your join results so that repeat entries are combined, call the ",(0,r.jsx)(n.code,{children:"grouped()"})," method on the join query.\nFor example, when joining ",(0,r.jsx)(n.code,{children:"employees"})," with ",(0,r.jsx)(n.code,{children:"dept"})," and to join ",(0,r.jsx)(n.code,{children:"dept"}),"\nwith ",(0,r.jsx)(n.code,{children:"company"}),", use ",(0,r.jsx)(n.code,{children:"grouped"})," to receive only one entry user."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const departmentCollection = squid.collection<Dept>('dept');\nconst employeeCollection = squid.collection<Employee>('employees');\nconst employeeQuery = employeeCollection.query().gt('age', 18);\nconst companyQuery = squid.collection<Company>('company').query();\n\nconst joinObs = departmentCollection\n  .joinQuery('d')\n  .join(employeeQuery, 'e', {\n    left: 'id',\n    right: 'deptId',\n  })\n  .join(companyQuery, 'c', {\n    left: 'companyId',\n    right: 'id',\n  })\n  .grouped()\n  .snapshots();\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Without the ",(0,r.jsx)(n.code,{children:"grouped()"})," method, this query returns results of this type:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"type Result = Array<{\n  // The same user may return more than once (one for each department and company)\n  e: DocumentReference<Employee>;\n  // The same department may return more than once (one for each company)\n  d: DocumentReference<Dept> | undefined;\n  c: DocumentReference<Company> | undefined;\n}>;\n"})}),"\n",(0,r.jsxs)(n.p,{children:["With the ",(0,r.jsx)(n.code,{children:"grouped()"})," method, the query returns results of this type:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"type Result = Array<{\n  e: DocumentReference<Employee>;\n  d: Array<{\n    d: DocumentReference<Dept>;\n    c: Array<DocumentReference<Company>>;\n  }>;\n}>;\n"})}),"\n",(0,r.jsxs)(n.p,{children:["You can use the ",(0,r.jsx)(n.code,{children:"grouped()"})," method with ",(0,r.jsx)(n.code,{children:"dereference()"})," to get the result data without the ",(0,r.jsx)(n.code,{children:"DocumentReference"}),"."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const departmentCollection = squid.collection<Dept>('dept');\nconst employeeCollection = squid.collection<Employee>('employees');\nconst employeeQuery = employeeCollection.query().gt('age', 18);\nconst companyQuery = squid.collection<Company>('company').query();\n\nconst joinObs = departmentCollection\n  .joinQuery('d')\n  .join(employeeQuery, 'e', {\n    left: 'id',\n    right: 'deptId',\n  })\n  .join(companyQuery, 'c', {\n    left: 'companyId',\n    right: 'id',\n  })\n  .grouped()\n  .dereference()\n  .snapshots();\n"})}),"\n",(0,r.jsx)(n.p,{children:"This query returns results of this type:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"type Result = Array<{\n  e: Employee;\n  d: Array<{\n    d: Dept;\n    c: Array<Company>;\n  }>;\n}>;\n"})}),"\n",(0,r.jsx)(n.h3,{id:"or-queries",children:"OR queries"}),"\n",(0,r.jsxs)(n.p,{children:["Combine multiple queries on the same collection using the ",(0,r.jsx)(n.code,{children:"or()"})," method on the ",(0,r.jsx)(n.a,{href:"/docs/sdk/client-sdk/database/collection-reference",children:"collection reference"}),". Results are deduplicated and sorted by the first query's sort order:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const usersCollection = squid.collection<User>('users');\n\nconst adminQuery = usersCollection.query().eq('role', 'admin');\nconst recentQuery = usersCollection.query().gt('createdAt', '2024-01-01');\n\n// Returns users who are admins OR were created after 2024-01-01\nconst results = await usersCollection.or(adminQuery, recentQuery).snapshot();\n"})}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsxs)(n.p,{children:["All queries passed to ",(0,r.jsx)(n.code,{children:"or()"})," must have the same sort order. At least one query must be provided."]})}),"\n",(0,r.jsx)(n.h3,{id:"limits-and-sorting",children:"Limits and s
1orting"}),"\n",(0,r.jsx)(n.p,{children:"Squid provides the ability to sort and limit queries, which can be useful for optimizing the performance of your\napplication and improving the user experience."}),"\n",(0,r.jsxs)(n.p,{children:["To sort a query, use the ",(0,r.jsx)(n.code,{children:"sortBy"})," method and specify the field to sort on, as well as an optional parameter to\nspecify the sort order. If no sort order is provided, the query defaults to ascending order."]}),"\n",(0,r.jsx)(n.p,{children:"Here's an example of sorting a query by age in descending order:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const users = await squid\n  .collection<User>('users')\n  .query()\n  .gt('age', 18)\n  .eq('role', 'admin')\n  .sortBy('age', false)\n  .snapshot();\n"})}),"\n",(0,r.jsxs)(n.p,{children:["To limit the number of results returned by a query, use the ",(0,r.jsx)(n.code,{children:"limit"})," method and specify the maximum number of\nresults to return. If no limit is provided, the query defaults to ",(0,r.jsx)(n.code,{children:"1000"}),", which is also the largest maximum allowed. To retrieve more than 1000 items, use ",(0,r.jsx)(n.a,{href:"#pagination",children:"pagination"}),"."]}),"\n",(0,r.jsx)(n.p,{children:"Here's an example of limiting a query to 10 results:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const users = await squid\n  .collection<User>('users')\n  .query()\n  .gt('age', 18)\n  .eq('role', 'admin')\n  .limit(10)\n  .snapshot();\n"})}),"\n",(0,r.jsx)(n.p,{children:"You can also use sorting and limiting together in the same query, as shown in the following example:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const users = await squid\n  .collection<User>('users')\n  .query()\n  .gt('age', 18)\n  .eq('role', 'admin')\n  .sortBy('age', false)\n  .limit(10)\n  .snapshot();\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Another feature is ",(0,r.jsx)(n.code,{children:"limitBy"}),". This returns only the first ",(0,r.jsx)(n.code,{children:"limit"})," documents which have the same values in each field in ",(0,r.jsx)(n.code,{children:"fields"}),'. This enables queries such as "return the 5 youngest users in each city" (see example).']}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const users = await squid\n  .collection<User>('users')\n  .query()\n  .sortBy('state')\n  .sortBy('city')\n  .sortBy('age')\n  .sortBy('name')\n  .limitBy(5, ['state', 'city'])\n  .snapshot();\n"})}),"\n",(0,r.jsxs)(n.p,{children:["The returned query will have a maximum of 5 documents for each ",(0,r.jsx)(n.code,{children:"state"})," and ",(0,r.jsx)(n.code,{children:"city"})," combination. Effectively, this query returns the 5 youngest users in each city (with age ties broken by name)."]}),"\n",(0,r.jsx)(n.admonition,{title:"Note",type:"note",children:(0,r.jsxs)(n.p,{children:["All ",(0,r.jsx)(n.code,{children:"fields"})," in the ",(0,r.jsx)(n.code,{children:"limitBy"})," clause must appear in the first ",(0,r.jsx)(n.code,{children:"n"})," ",(0,r.jsx)(n.code,{children:"sortBy"})," clauses for the query (where ",(0,r.jsx)(n.code,{children:"n"})," is the number of fields). In the above example, ",(0,r.jsx)(n.code,{children:"state"})," and ",(0,r.jsx)(n.code,{children:"city"})," (which appear in the ",(0,r.jsx)(n.code,{children:"limitBy"}),") must have a ",(0,r.jsx)(n.code,{children:"sortBy()"})," in the query (and their ",(0,r.jsx)(n.code,{children:"sortBy"})," must be before the ones ",(0,r.jsx)(n.code,{children:"age"})," or ",(0,r.jsx)(n.code,{children:"name"}),")."]})}),"\n",(0,r.jsx)(n.h3,{id:"field-projection",children:"Field projection"}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.code,{children:"projectFields"})," method allows you to specify which fields to return from a query, reducing bandwidth and improving performance by only fetching the data you need."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const users = await squid\n  .collection<User>
1('users')\n  .query()\n  .projectFields(['name', 'age'])\n  .dereference()\n  .snapshot();\n\n// Results contain only the projected fields: name and age\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Field projection works with real-time subscriptions, nested fields via dot notation, and all other query methods. For complete documentation including validation rules, subscription behavior, and error handling, see ",(0,r.jsx)(n.a,{href:"/docs/sdk/client-sdk/database/project-fields",children:"Field projection"}),"."]}),"\n",(0,r.jsx)(n.h3,{id:"pagination",children:"Pagination"}),"\n",(0,r.jsxs)(n.p,{children:["Squid provides a powerful way to paginate query results through the ",(0,r.jsx)(n.code,{children:"paginate"})," method on the query.\nThe ",(0,r.jsx)(n.code,{children:"paginate"})," method accepts a ",(0,r.jsx)(n.code,{children:"PaginationOptions"})," object as a parameter, which contains the following properties:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"pageSize"}),": A number that defaults to 100."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"subscribe"}),": A boolean that indicates whether to subscribe to real-time updates on the query. By default, this is set to ",(0,r.jsx)(n.code,{children:"true"}),"."]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["Upon invocation, the ",(0,r.jsx)(n.code,{children:"paginate"})," method returns a ",(0,r.jsx)(n.code,{children:"Pagination"})," object with the following properties:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"observeState"}),": An observable that emits the current pagination state, defined as ",(0,r.jsx)(n.code,{children:"PaginationState"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"next"}),": A function that resolves a promise with the succeeding page state."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"prev"}),": A function that resolves a promise with the preceding page state."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"waitForData"}),": A function that returns a promise resolving with the current pagination state, once the loading process\nhas finished."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"unsubscribe"}),": A function that instructs the pagination object to unsubscribe from the query and clear its internal\nstate."]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.code,{children:"PaginationState"})," object contains the following properties:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"data"}),": An array holding the current page's data."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"hasNext"}),": A boolean indicating the availability of a next page."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"hasPrev"}),": A boolean indicating the availability of a previous page."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"isLoading"}),": A boolean indicating whether the pagination is in the process of loading data."]}),"\n"]}),"\n",(0,r.jsx)("br",{}),"\n",(0,r.jsx)(n.p,{children:"The following is a sample usage of pagination:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const pagination = squid\n  .collection<User>('users')\n  .query()\n  .gt('age', 18)\n  .eq('role', 'admin')\n  .sortBy('age', false)\n  .dereference()\n  .paginate({ pageSize: 10 });\n\nlet data = await pagination.waitForData();\nconsole.log(data); // Outputs the first page of data\ndata = await pagination.next();\nconsole.log(data); // Outputs the second page of data\npagination.unsubscribe();\n"})}),"\n",(0,r.jsxs)(n.p,{children:["When requested, the pagination object will actively subscribe to real-time updates on the query to maintain the most\ncurrent state. This means that the ",(0,r.jsx)(n.code,{children:"PaginationState"})," object is updated with the latest data as it changes."]}),"\n",(0,r.jsx)(n.admonition,{title:"Note",type:"note",children:(0,r.jsx)(n.p,{children:"To preserve real-time updates or to accommodate edge cases such as receiving empty pages due to server updates, the\npagination object may execute more than a single query to display a page."})}),"\n",(0,r.jsx)(n.h3,{id:"query-helpers",children:"Query helpers"}),"\n",(0,r.jsxs)(n.p,{children:["In addition to using the helper functions, queries can be constructed using the ",(0,r.jsx)(n.code,{children:"where"})," function. The ",(0,r.jsx)(n.code,{children:"where"})," function\naccepts three parameters: the field to query, the operator to use, and the value to compare against."]}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Helper"}),(0,r.jsx)(n.th,{children:"Before"}),(0,r.jsx)(n.th,{children:"After"}),(0,r.jsx)(n.th,{children:"Explanation"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"eq"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', '==', 'bar')"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"eq('foo', 'bar')"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," is equal to ",(0,r.jsx)(n.code,{children:"bar"})]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"neq"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', '!=', 'bar')"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"neq('foo', 'bar')"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," is not equal to ",(0,r.jsx)(n.code,{children:"bar"})]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"in"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', 'in', ['bar'])"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"in('foo', ['bar'])"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"}
1)," is in the specified list"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"nin"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', 'not in', ['bar'])"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"nin('foo', ['bar'])"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," is not in the specified list"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"gt"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', '>', 'bar')"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"gt('foo', 'bar')"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," is greater than ",(0,r.jsx)(n.code,{children:"bar"})]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"gte"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', '>=', 'bar')"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"gte('foo', 'bar')"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," is greater than or equal to ",(0,r.jsx)(n.code,{children:"bar"})]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"lt"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', '<', 'bar')"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"lt('foo', 'bar')"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," is less than ",(0,r.jsx)(n.code,{children:"bar"})]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"lte"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', '<=', 'bar')"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"lte('foo', 'bar')"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," is less than or equal to ",(0,r.jsx)(n.code,{children:"bar"})]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"like (case sensitive)"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', 'like_cs', '%bar%')"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"like('foo', '%bar%')"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," matches pattern ",(0,r.jsx)(n.code,{children:"%bar%"})," (CS)"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"like"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', 'like', '%bar%')"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"like('foo', '%bar%', false)"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," matches pattern ",(0,r.jsx)(n.code,{children:"%bar%"})," (CI)"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"notLike (case sensitive)"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', 'not like_cs', '%bar%')"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"notLike('foo', '%bar%')"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," does not match pattern ",(0,r.jsx)(n.code,{children:"%bar%"})," (CS)"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"notLike"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', 'not like', '%bar%')"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"notLike('foo', '%bar%', false)"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," does not match pattern ",(0,r.jsx)(n.code,{children:"%bar%"})," (CI)"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"arrayIncludesSome"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', 'array_includes_some', ['bar'])"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"arrayIncludesSome('foo', ['bar'])"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," array includes some of the values"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"arrayIncludesAll"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', 'array_includes_all', ['bar'])"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"arrayIncludesAll('foo', ['bar'])"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," array includes all of the values"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"arrayNotIncludes"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"where('foo', 'array_not_includes', ['bar'])"})}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"arrayNotIncludes('foo', ['bar'])"})}),(0,r.jsxs)(n.td,{children:["Checks if ",(0,r.jsx)(n.code,{children:"foo"})," array does not include any value"]})]})]})]}),"\n",(0,r.jsx)(n.h3,{id:"filtering-by-document-id",children:"Filtering by document ID"}),"\n",(0,r.jsxs)(n.p,{children:["Every collection exposes a ",(0,r.jsx)(n.code,{children:"__docId__"})," pseudo-field for filtering by document identity without knowing the underlying primary key column names. Use it directly in ",(0,r.jsx)(n.code,{children:"where()"})," and the helper functions, or use the ",(0,r.jsx)(n.code,{children:"docId()"})," and ",(0,r.jsx)(n.code,{children:"docIds()"})," shortcuts:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"// Single document by ID (shortcut for eq('__docId__', id)):\nawait squid.collection<User>('users').query().docId('user_abc123').snapshot();\n\n// Multiple documents by ID (shortcut for in('__docId__', ids)):\nawait squid.collection<User>('users').query().docIds(['user_1', 'user_2']).snapshot();\n\n// Combined with regular conditions:\nawait squid\n  .collection<User>
1('users')\n  .query()\n  .docIds(['user_1', 'user_2'])\n  .eq('status', 'active')\n  .snapshot();\n"})}),"\n",(0,r.jsxs)(n.p,{children:["For collections with composite primary keys, pass an object containing all key fields. Each object matches as one unit, so ",(0,r.jsx)(n.code,{children:"[{ orgId: 'o1', itemId: 'i1' }, { orgId: 'o2', itemId: 'i2' }]"})," never matches mixed combinations:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"await squid\n  .collection('inventory', 'CONNECTOR_ID')\n  .query()\n  .in('__docId__', [\n    { orgId: 'o1', itemId: 'i1' },\n    { orgId: 'o2', itemId: 'i2' },\n  ])\n  .snapshot();\n"})}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.code,{children:"__docId__"})," value returned on query results can be passed straight back into another query. A few constraints to be aware of:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Single primary keys support the full operator set, including range and ",(0,r.jsx)(n.code,{children:"like"})," operators."]}),"\n",(0,r.jsxs)(n.li,{children:["Composite primary keys support only the equality family: ",(0,r.jsx)(n.code,{children:"=="}),", ",(0,r.jsx)(n.code,{children:"!="}),", ",(0,r.jsx)(n.code,{children:"in"}),", and ",(0,r.jsx)(n.code,{children:"not in"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["Array operators are not supported on ",(0,r.jsx)(n.code,{children:"__docId__"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["Composite-key ",(0,r.jsx)(n.code,{children:"__docId__"})," filters are not supported on Elasticsearch and DynamoDB connectors (single-key filters work everywhere)."]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["See ",(0,r.jsx)(n.a,{href:"/docs/sdk/client-sdk/database/document-id",children:"Document IDs"})," for how IDs are structured per connector."]}),"\n",(0,r.jsx)(n.h2,{id:"error-handling",children:"Error Handling"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Error"}),(0,r.jsx)(n.th,{children:"Cause"}),(0,r.jsx)(n.th,{children:"Solution"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Invalid field name"}),(0,r.jsx)(n.td,{children:"Filtering or sorting on a field that does not exist in the collection"}),(0,r.jsx)(n.td,{children:"Verify field names match your collection schema and TypeScript interface"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Limit exceeded"}),(0,r.jsx)(n.td,{children:"Requesting more than the maximum 1000 results"}),(0,r.jsxs)(n.td,{children:["Use ",(0,r.jsx)(n.code,{children:"limit(1000)"})," or less, or use ",(0,r.jsx)(n.a,{href:"#pagination",children:"pagination"})," for larger result sets"]}
1)]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsxs)(n.td,{children:[(0,r.jsx)(n.code,{children:"limitBy"})," sort mismatch"]}),(0,r.jsxs)(n.td,{children:["Fields in ",(0,r.jsx)(n.code,{children:"limitBy"})," do not appear in the first ",(0,r.jsx)(n.code,{children:"sortBy"})," clauses"]}),(0,r.jsxs)(n.td,{children:["Ensure all ",(0,r.jsx)(n.code,{children:"limitBy"})," fields have corresponding ",(0,r.jsx)(n.code,{children:"sortBy"})," clauses in the correct order"]})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Empty query result"}),(0,r.jsx)(n.td,{children:"No documents match the filter conditions"}),(0,r.jsx)(n.td,{children:"Verify filter values; check that the data exists in the expected collection"})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:"Security rule rejection"}),(0,r.jsxs)(n.td,{children:["The query was blocked by ",(0,r.jsx)(n.a,{href:"/docs/security/security-rules",children:"security rules"})]}),(0,r.jsx)(n.td,{children:"Verify the user has read permission for the collection"})]})]})]}),"\n",(0,r.jsx)(n.h2,{id:"best-practices",children:"Best Practices"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsxs)(n.strong,{children:["Use ",(0,r.jsx)(n.code,{children:"snapshot()"})," for one-time reads"]})," and ",(0,r.jsx)(n.code,{children:"snapshots()"})," only when you need real-time updates. Unnecessary subscriptions consume resources."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Apply filters server-side"})," rather than fetching all documents and filtering in your application code:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"// Recommended: server-side filtering\nconst activeUsers = await squid\n  .collection<User>('users')\n  .query()\n  .eq('status', 'active')\n  .snapshot();\n\n// Avoid: fetching everything and filtering client-side\nconst allUsers = await squid\n  .collection<User>('users')\n  .query()\n  .snapshot();\nconst activeUsers = allUsers.filter((u) => u.data.status === 'active');\n"})}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Set explicit limits"})," on queries to avoid unexpectedly large result sets. The default limit is 1000."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsxs)(n.strong,{children:["Use ",(0,r.jsx)(n.code,{children:"dereference()"})]})," when you only need the data and not the document references, to simplify downstream code."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsxs)(n.strong,{children:["Use ",(0,r.jsx)(n.a,{href:"/docs/sdk/client-sdk/database/project-fields",children:"field projection"})]})," when you only need a subset of fields, to reduce bandwidth."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Unsubscribe from observables"})," when the component or listener is destroyed, to prevent memory leaks:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:"template=frontend",children:"const subscription = squid\n  .collection<User>('users')\n  .query()\n  .snapshots()\n  .subscribe((users) => {\n    // handle updates\n  });\n\n// When done:\nsubscription.unsubscribe();\n"})}),"\n"]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(a,{...e})}):a(e)}},8453(e,n,s){s.d(n,{R:()=>o,x:()=>c});var t=s(6540);const r={},i=t.createContext(r);function o(e){const n=t.useContext(i);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),t.createElement(i.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.