js-bao 0.3.0-alpha.3 → 0.3.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -848,6 +848,154 @@ const productSummary = await Product.query(
848
848
  // Returns: [{ id: "...", name: "...", price: ... }, ...]
849
849
  ```
850
850
 
851
+ ### Includes (Related Data)
852
+
853
+ Load related records alongside query results using `include`. Each include spec tells the engine how to resolve a relationship between models.
854
+
855
+ **Include types:**
856
+
857
+ | Type | Relationship | Example |
858
+ |------|-------------|---------|
859
+ | `refersTo` | Record has a FK field pointing to one target | Order → Customer |
860
+ | `hasMany` | Target records have a FK field pointing back to this record | Customer → Orders |
861
+ | `refersToMany` | Record has a StringSet field containing multiple target IDs | Post → Tags |
862
+
863
+ #### refersTo — Scalar foreign key
864
+
865
+ Load a single related record via a foreign key field on the source.
866
+
867
+ ```typescript
868
+ // Schema: Order has a `customerId` field
869
+ const orders = await Order.query({}, {
870
+ include: [{
871
+ model: "customers",
872
+ type: "refersTo",
873
+ sourceField: "customerId", // FK field on Order
874
+ as: "customer", // result key (optional, defaults to model name)
875
+ projection: { name: 1 }, // only load specific fields
876
+ }],
877
+ });
878
+ // Each order gets: order._related.customer = { id, name }
879
+ ```
880
+
881
+ #### hasMany — Reverse foreign key
882
+
883
+ Load multiple related records that reference this record.
884
+
885
+ ```typescript
886
+ // Schema: Comment has an `articleId` field pointing to Article
887
+ const articles = await Article.query({}, {
888
+ include: [{
889
+ model: "comments",
890
+ type: "hasMany",
891
+ foreignKey: "articleId", // FK field on Comment pointing back
892
+ as: "comments",
893
+ limit: 10, // cap per parent
894
+ sort: { createdAt: -1 }, // newest first
895
+ }],
896
+ });
897
+ // Each article gets: article._related.comments = [{ id, text, ... }, ...]
898
+ ```
899
+
900
+ #### refersToMany — StringSet of IDs
901
+
902
+ Load multiple related records referenced by a StringSet field.
903
+
904
+ ```typescript
905
+ // Schema: Post has a `tagIds` StringSet field containing Tag IDs
906
+ const posts = await Post.query({}, {
907
+ include: [{
908
+ model: "tags",
909
+ type: "refersToMany",
910
+ sourceField: "tagIds", // StringSet field on Post
911
+ as: "tags",
912
+ projection: { name: 1 },
913
+ }],
914
+ });
915
+ // Each post gets: post._related.tags = [{ id, name }, ...]
916
+ ```
917
+
918
+ #### End-to-end example: write and query
919
+
920
+ ```typescript
921
+ // 1. Create tags
922
+ const techTag = await Tag.create({ id: generateULID(), name: "Tech" });
923
+ const newsTag = await Tag.create({ id: generateULID(), name: "News" });
924
+
925
+ // 2. Create a post with references to tags via StringSet
926
+ const post = await Post.create({ id: generateULID(), title: "Hello World" });
927
+ await Post.addToSet(post.id, { tagIds: [techTag.id, newsTag.id] });
928
+
929
+ // 3. Create a comment referencing the post
930
+ await Comment.create({
931
+ id: generateULID(),
932
+ postId: post.id,
933
+ text: "Great post!",
934
+ });
935
+
936
+ // 4. Query posts with all related data in one call
937
+ const results = await Post.query({}, {
938
+ include: [
939
+ {
940
+ model: "tags",
941
+ type: "refersToMany",
942
+ sourceField: "tagIds",
943
+ as: "tags",
944
+ },
945
+ {
946
+ model: "comments",
947
+ type: "hasMany",
948
+ foreignKey: "postId",
949
+ as: "comments",
950
+ sort: { createdAt: -1 },
951
+ },
952
+ ],
953
+ });
954
+
955
+ // Result:
956
+ // results.data[0]._related.tags = [{ id: "...", name: "Tech" }, { id: "...", name: "News" }]
957
+ // results.data[0]._related.comments = [{ id: "...", postId: "...", text: "Great post!" }]
958
+ ```
959
+
960
+ #### Nested includes
961
+
962
+ Includes can be nested to load relationships on related records:
963
+
964
+ ```typescript
965
+ const articles = await Article.query({}, {
966
+ include: [{
967
+ model: "comments",
968
+ type: "hasMany",
969
+ foreignKey: "articleId",
970
+ as: "comments",
971
+ include: [{
972
+ model: "users",
973
+ type: "refersTo",
974
+ sourceField: "authorId",
975
+ as: "author",
976
+ projection: { name: 1 },
977
+ }],
978
+ }],
979
+ });
980
+ // article._related.comments[0]._related.author = { id, name }
981
+ ```
982
+
983
+ #### Filtering included records
984
+
985
+ Apply filters to narrow which related records are loaded:
986
+
987
+ ```typescript
988
+ const posts = await Post.query({}, {
989
+ include: [{
990
+ model: "comments",
991
+ type: "hasMany",
992
+ foreignKey: "postId",
993
+ filter: { status: "approved" }, // only approved comments
994
+ as: "comments",
995
+ }],
996
+ });
997
+ ```
998
+
851
999
  ### Single Document Queries
852
1000
 
853
1001
  ```typescript
package/dist/codegen.cjs CHANGED
@@ -1189,7 +1189,7 @@ var SchemaExtractor = class {
1189
1189
  // package.json
1190
1190
  var package_default = {
1191
1191
  name: "js-bao",
1192
- version: "0.3.0-alpha.3",
1192
+ version: "0.3.0-alpha.4",
1193
1193
  description: "A library providing data modeling capabilities which support live updates and queries.",
1194
1194
  types: "dist/index.d.ts",
1195
1195
  type: "module",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-bao",
3
- "version": "0.3.0-alpha.3",
3
+ "version": "0.3.0-alpha.4",
4
4
  "description": "A library providing data modeling capabilities which support live updates and queries.",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",