beanbagdb 0.5.54 → 0.5.61

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.
@@ -1 +1,64 @@
1
- # Getting started
1
+ # Getting started
2
+
3
+
4
+
5
+ ### Nodes and edges
6
+
7
+ The records stored in BeanBagDB can be organized into a simple directed graph. In in simple directed graph, edges have a direction and there can only be one edge between 2 nodes. Edges can have label based on the context of your data. You can also define rules for these edges based on the schema of the nodes.
8
+
9
+ Example : Consider you have 3 types of nodes : "player", "team", "match" to simulate a game where a teams has multiple players and a player plays a match as part of a team.
10
+ Here are the rules:
11
+ - A player is part of a team (and not vice versa)
12
+ - A team plays a match (and not vice versa)
13
+ - A player cannot be in 2 teams
14
+ - A match can only be between 2 teams (not more than 2)
15
+ - Player does not play a match (but a team does, assuming this is a team sport)
16
+ - A team cannot be part of a team
17
+ - A team cannot have more than 11 players
18
+
19
+ In terms of nodes and edges, the rules translate to:
20
+
21
+ | rule | node 1 | edge label | node 2 | constraint |
22
+ |-------------------------------------------------------|--------|-------------|--------|---------------------------------|
23
+ | 1 A player is part of a team (and not vice versa) | player | is part of | team | player-->team |
24
+ | 2 A team plays a match (and not vice versa) | team | plays | match | |
25
+ | 3 A player cannot be in 2 teams | player | is part of | team | only one such relation |
26
+ | 4 A match can only be between 2 teams (not more than 2) | team | plays | match | only 2 such relation per match |
27
+ | 5 Player does not play a match | player | plays | match | not valid |
28
+ | 6 A team cannot be part of a team | team | is part of | team | not valid |
29
+ | 7 A team cannot have more than 11 players | player | is part of | team | only 11 such relations per team |
30
+
31
+
32
+ These rules can be enforced in the database as "edge_constraints" and are automatically checked when new edges are added to the graph.
33
+
34
+ ```
35
+ - Constraint 1 :
36
+ - node 1 : player
37
+ - edge type : part_of
38
+ - edge label : is part of a
39
+ - node 2 : team
40
+ - max_from_node1 : 1
41
+ - max_to_node2 : 11
42
+ - Constraint 2 :
43
+ - node 1 : team
44
+ - edge type : plays
45
+ - edge label : plays
46
+ - node 2 : match
47
+ - max_from_node1 : None
48
+ - max_to_node2 : 2
49
+ ```
50
+ - `max_from_node1` defines the maximum number of edges of a certain type that can exist from node 1 to node 2. For example, player1 is part of team1; now, player1 cannot be part of any other team.
51
+ - `max_to_node2` defines the maximum number of edges of a certain type that can be directed towards a particular node. For example, team1 and team2 can only play match1.
52
+ - Violations of these results in an error and the edge is not created
53
+ - for both node1 and node2, the general approach is to whitelist a set of schemas for the specified type of edge. It is still valid to add other types of edges from these nodes to others.
54
+ - The schema names in node1 and node2 fixes the schema types for this particular edge type. This means creating a relations "player1--(part_of)-->team1" or "team1--(part_of)-->player1" will result in the same relation "player1--(part_of)-->team1".
55
+ - You can also specify nodes with different schema types as : "player,coach".
56
+ - If you want to allow nodes of all schema types, use "*."
57
+ - Eg :
58
+ - Constraint:
59
+ - node 1: *
60
+ - edge label: interacts with
61
+ - node 2: *
62
+ - If you want to include all except for a few types of nodes, use : "* - schema1, schema2"
63
+ - Eg : "*-tag tagged_as tag" means that nodes of all types (except for tag nodes themselves) can be tagged with a tag.
64
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beanbagdb",
3
- "version": "0.5.54",
3
+ "version": "0.5.61",
4
4
  "description": "A JS library to introduce a schema layer to a No-SQL local database",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
package/src/index.js CHANGED
@@ -344,6 +344,7 @@ export class BeanBagDB {
344
344
  async create(schema, data, meta = {}, settings = {}) {
345
345
  this._check_ready_to_use();
346
346
  if(!schema){throw new DocCreationError(`No schema provided`)}
347
+ if(schema=="setting_edge"){throw new DocCreationError("This type of record can only be created through the create_edge api")}
347
348
  if(Object.keys(data).length==0){throw new DocCreationError(`No data provided`)}
348
349
  try {
349
350
  let doc_obj = await this._insert_pre_checks(schema, data,meta, settings);
@@ -440,7 +441,6 @@ export class BeanBagDB {
440
441
  * - Retrieves the document based on the provided search criteria.
441
442
  * - Checks the revision ID to detect potential conflicts. (To be implemented: behavior when the `rev_id` does not match).
442
443
  * - Validates editable fields against `schema.settings.editable_fields` (or allows editing of all fields if not specified).
443
- * - Performs primary key conflict checks if multiple records are allowed (`single_record == false`).
444
444
  * - Encrypts fields if encryption is required by the schema settings.
445
445
  * - Updates the `meta` fields (such as `updated_on` and `updated_by`) and saves the updated document to the database.
446
446
  *
@@ -448,7 +448,7 @@ export class BeanBagDB {
448
448
  * @returns {Object} The result of the document update operation.
449
449
  *
450
450
  * **Errors**:
451
- * - Throws an error if a document with the same primary keys already exists (and `single_record == false`).
451
+ * - Throws an error if a document with the same primary keys already exists .
452
452
  * - Throws a `DocUpdateError` if a primary key conflict is detected during the update.
453
453
  *
454
454
  * @throws {DocUpdateError} - If a document with conflicting primary keys already exists.
@@ -655,6 +655,119 @@ export class BeanBagDB {
655
655
  }
656
656
  }
657
657
 
658
+ ///////////////////////////////////////////////////////////
659
+ //////////////// simple directed graph ////////////////////////
660
+ //////////////////////////////////////////////////////////
661
+
662
+ async create_edge(node1,node2,edge_name,edge_label=""){
663
+ this._check_ready_to_use();
664
+ if(!edge_name){throw new ValidationError("edge_name required")}
665
+ if(Object.keys(node1)==0){throw new ValidationError("node1 required")}
666
+ if(Object.keys(node2)==0){throw new ValidationError("node2 required")}
667
+
668
+ let n1 = await this.read(node1)
669
+ let n2 = await this.read(node2)
670
+ let edges_constraint
671
+
672
+ try {
673
+ let d = await this.read({schema:"setting_edge_constraint",data:{name:edge_name}})
674
+ edges_constraint = d["doc"]["data"]
675
+ let errors = []
676
+ let node1id = n1.doc._id
677
+ let node2id = n2.doc._id
678
+ let val_check = this._check_nodes_edge(edges_constraint.node1,edges_constraint.node2,n1.doc.schema,n2.doc.schema)
679
+
680
+ if (val_check.valid){
681
+ if(val_check.swapped){
682
+ // swapping required
683
+ node1id = n2.doc._id
684
+ node2id = n1.doc._id
685
+ }
686
+ }else{
687
+ this.errors.push("Invalid nodes.This config of nodes not allowed")
688
+ }
689
+
690
+ let records = await this.search({selector:{schema:"system_edge","data.edge_name":edge_name}})
691
+
692
+ if(edges_constraint.max_from_node1!=-1){
693
+ let filter1 = records.docs.filter((itm)=>itm.data.node1==node1id)
694
+ if(filter1.length>=edges_constraint.max_from_node1){
695
+ errors.push("max limit reached")
696
+ }
697
+ }
698
+
699
+ if(edges_constraint.max_to_node2!=-1){
700
+ let filter2 = records.docs.filter((itm)=>itm.data.node2==node2id)
701
+ if(filter1.length>=edges_constraint.max_from_node1){
702
+ errors.push("max limit reached")
703
+ }
704
+ }
705
+
706
+ if(errors.length==0){
707
+ let edge = await this.create("system_edge",{node1: node1id , node2: node1id ,edge_name:edge_name })
708
+ return edge
709
+ }else{
710
+ throw new RelationError(errors)
711
+ }
712
+
713
+ } catch (error) {
714
+ if(error instanceof DocNotFoundError){
715
+ let doc = {node1:"*",node2:"*",name:edge_name,label:edge_label}
716
+ let new_doc = await this.create("system_edge_constraint",doc)
717
+ let edge = await this.create("system_edge",{node1: n1.doc._id,node2: n2.doc._id,edge_name:edge_name })
718
+ return edge
719
+ }else{
720
+ throw error
721
+ }
722
+ }
723
+ }
724
+
725
+ _check_node_schema_match(rule, schema) {
726
+ /**
727
+ * Check if the schema matches the rule. The rule can be:
728
+ * - "*" for any schema
729
+ * - "*-n1,n2" for all schemas except n1 and n2
730
+ * - "specific_schema" or "schema1,schema2" for specific schema matches
731
+ */
732
+ if (rule === "*") {
733
+ return true;
734
+ }
735
+
736
+ if (rule.startsWith("*-")) {
737
+ // Exclude the schemas listed after "*-"
738
+ const exclusions = rule.slice(2).split(",");
739
+ return !exclusions.includes(schema);
740
+ }
741
+
742
+ // Otherwise, check if schema matches the specific rule (comma-separated for multiple allowed schemas)
743
+ const allowedSchemas = rule.split(",");
744
+ return allowedSchemas.includes(schema);
745
+ }
746
+
747
+ _check_nodes_edge(node1Rule, node2Rule, schema1, schema2) {
748
+ /**
749
+ * Check if the edge between schema1 (node1) and schema2 (node2) is valid based on the rules
750
+ * node1Rule and node2Rule. Also checks if the nodes should be swapped.
751
+ *
752
+ */
753
+ // Check if schema1 matches node1Rule and if schema2 matches node2Rule
754
+ const matchesNode1 = this._check_node_schema_match(node1Rule, schema1);
755
+ const matchesNode2 = this._check_node_schema_match(node2Rule, schema2);
756
+
757
+ // Check if schema1 matches node2Rule and schema2 matches node1Rule (for swapping condition)
758
+ const matchesSwappedNode1 = this._check_node_schema_match(node2Rule, schema1);
759
+ const matchesSwappedNode2 = this._check_node_schema_match(node1Rule, schema2);
760
+
761
+ // If the schemas match their respective rules (node1 and node2), the edge is valid
762
+ if (matchesNode1 && matchesNode2) { return { valid: true, swapped: false }}
763
+
764
+ // If swapping makes it valid, indicate that the nodes should be swapped
765
+ if (matchesSwappedNode1 && matchesSwappedNode2) { return { valid: true, swapped: true }}
766
+ // Otherwise, the edge is invalid
767
+ return { valid: false, swapped: false };
768
+ }
769
+
770
+
658
771
  ///////////////////////////////////////////////////////////
659
772
  //////////////// Internal methods ////////////////////////
660
773
  //////////////////////////////////////////////////////////
@@ -803,7 +916,10 @@ export class BeanBagDB {
803
916
  this.util_validate_data(schemaDoc.schema, data);
804
917
 
805
918
  // validate meta
806
- this.util_validate_data(sys_sch.editable_metadata_schema, meta);
919
+ if(Object.keys.length>0){
920
+ this.util_validate_data(sys_sch.editable_metadata_schema, meta)
921
+ }
922
+
807
923
 
808
924
  // duplicate meta.link check
809
925
  if (meta.link) {
@@ -1170,4 +1286,29 @@ constructor(errors=[]){
1170
1286
  this.name = "EncryptionError";
1171
1287
  this.errors = errors
1172
1288
  }
1289
+ }
1290
+
1291
+ /**
1292
+ * Custom error class for relation error.
1293
+ *
1294
+ * @extends {Error}
1295
+ */
1296
+ export class RelationError extends Error {
1297
+ /**
1298
+ *
1299
+ * @extends {Error}
1300
+ * @param {ErrorItem[]} [errors=[]] - An array of error objects, each containing details about validation failures.
1301
+ */
1302
+ constructor(errors=[]){
1303
+ let error_messages
1304
+ if(Array.isArray(errors)){
1305
+ error_messages = errors.map(item=>` ${(item.instancePath||" ").replace("/","")} ${item.message} `)
1306
+ }else {
1307
+ error_messages = [errors]
1308
+ }
1309
+ let message = `Error in relation of the simple digraph : ${error_messages.join(",")}`
1310
+ super(message)
1311
+ this.name = "RelationError";
1312
+ this.errors = errors
1313
+ }
1173
1314
  }
@@ -2,7 +2,7 @@ export const schema_schema = {
2
2
  name: "schema",
3
3
  description:"Meta-schema or the schema for defining other schemas",
4
4
  system_generated:true,
5
- version:0.5,
5
+ version:0.56,
6
6
  schema: {
7
7
  type: "object",
8
8
  additionalProperties: false,
@@ -68,12 +68,6 @@ export const schema_schema = {
68
68
  },
69
69
  maxItems: 50,
70
70
  description:"Once set, all the data in this field will be encrypted before storing it in the database. Encryption key must be provided during the time of BeanBagDB initialization and must be managed by the user as it is NOT stored in the database"
71
- },
72
- single_record: {
73
- type: "boolean",
74
- default: false,
75
- description:
76
- "If set, only a single records with this schema will be allowed to insert in the database",
77
71
  }
78
72
  },
79
73
  required :["primary_keys","non_editable_fields","encrypted_fields"]
@@ -83,7 +77,7 @@ export const schema_schema = {
83
77
  },
84
78
  settings: {
85
79
  primary_keys: ["name"],
86
- editable_fields: ["schema", "settings","description"],
80
+ non_editable_fields:[],
87
81
  encrypted_fields:[]
88
82
  },
89
83
  };
@@ -91,9 +85,9 @@ export const schema_schema = {
91
85
  export const system_schemas = {
92
86
  keys: {
93
87
  system_generated:true,
94
- version:0.5,
88
+ version:0.53,
95
89
  description:"To store user defined key. this can include anything like API tokens etc. There is a special method to fetch this. The values are encrypted",
96
- name: "system_keys",
90
+ name: "system_key",
97
91
  schema: {
98
92
  type: "object",
99
93
  additionalProperties: true,
@@ -116,22 +110,20 @@ export const system_schemas = {
116
110
  type: "string",
117
111
  minLength: 1,
118
112
  maxLength: 5000,
119
- pattern: "^[a-zA-Z][a-zA-Z0-9_]*$",
120
113
  },
121
114
  },
122
115
  },
123
116
  settings: {
124
117
  primary_keys: ["name"],
125
118
  encrypted_fields:["value"],
126
- non_editable_fields:[],
127
- single_record: false
119
+ non_editable_fields:[]
128
120
  },
129
121
  },
130
122
  settings: {
131
123
  version:0.5,
132
124
  system_generated:true,
133
125
  description:"The system relies on these settings for proper functioning or enabling optional features.",
134
- name: "system_settings",
126
+ name: "system_setting",
135
127
  schema: {
136
128
  required:["name","value"],
137
129
  type: "object",
@@ -157,44 +149,124 @@ export const system_schemas = {
157
149
  settings: {
158
150
  primary_keys: ["name"],
159
151
  non_editable_fields: ["name"],
160
- encrypted_fields:[],
161
- single_record:false
152
+ encrypted_fields:[]
153
+ },
154
+ },
155
+ edges_constraints:{
156
+ name:"system_edge_constraint",
157
+ system_generated:true,
158
+ version:0.5,
159
+ description: "To define edge constraints for simple directed graph of records.",
160
+ schema:{
161
+ type: "object",
162
+ additionalProperties: true,
163
+ required:["node1","node2","edge_type"],
164
+ properties: {
165
+ node1: {
166
+ type: "string",
167
+ minLength: 1,
168
+ maxLength: 500,
169
+ pattern: "^(\\*|(\\*-[a-zA-Z0-9_-]+)(,[a-zA-Z0-9_-]+)*|[a-zA-Z0-9_-]+(,[a-zA-Z0-9_-]+)*)$"
170
+ },
171
+ node2: {
172
+ type: "string",
173
+ minLength: 1,
174
+ maxLength: 500,
175
+ pattern: "^(\\*|(\\*-[a-zA-Z0-9_-]+)(,[a-zA-Z0-9_-]+)*|[a-zA-Z0-9_-]+(,[a-zA-Z0-9_-]+)*)$"
176
+ },
177
+ name:{
178
+ type: "string",
179
+ minLength: 1,
180
+ maxLength: 500,
181
+ pattern: "^[a-zA-Z][a-zA-Z0-9_]*$",
182
+ },
183
+ label:{
184
+ type: "string",
185
+ maxLength: 500,
186
+ },
187
+ note:{
188
+ type: "string",
189
+ maxLength: 5000,
190
+ },
191
+ max_from_node1:{
192
+ type:"number",
193
+ default: -1,
194
+ },
195
+ max_to_node2:{
196
+ type:"number",
197
+ default: -1,
198
+ }
199
+ }
200
+ },
201
+ settings: {
202
+ primary_keys: ["name"],
203
+ non_editable_fields:["name"],
204
+ encrypted_fields:[]
205
+ },
206
+ },
207
+ edges:{
208
+ name:"system_edge",
209
+ system_generated:true,
210
+ version:0.5,
211
+ description: "To define edges in the simple directed graph of records.",
212
+ schema:{
213
+ type: "object",
214
+ additionalProperties: true,
215
+ required:["node1","node2","edge_type"],
216
+ properties: {
217
+ node1: {
218
+ type: "string",
219
+ },
220
+ node2: {
221
+ type: "string",
222
+ },
223
+ edge_name:{
224
+ type: "string",
225
+ }
226
+ }
227
+ },
228
+ settings: {
229
+ primary_keys: ["node1","node2","edge_type"],
230
+ non_editable_fields:["edge_type"],
231
+ encrypted_fields:[]
232
+ },
233
+ },
234
+ media:{
235
+ name:"system_media",
236
+ system_generated:true,
237
+ version:0.5,
238
+ description: "To store images as Base64",
239
+ schema:{
240
+ type: "object",
241
+ additionalProperties: true,
242
+ required:["imageBase64","caption","source"],
243
+ properties: {
244
+ imageBase64: {
245
+ type: "string",
246
+ "media": {
247
+ "binaryEncoding": "base64"
248
+ }
249
+ },
250
+ caption: {
251
+ type: "string",
252
+ },
253
+ source:{
254
+ type: "string",
255
+ }
256
+ }
257
+ },
258
+ settings: {
259
+ primary_keys: ["caption"],
260
+ non_editable_fields:[],
261
+ encrypted_fields:[]
162
262
  },
163
263
  }
164
- // ,
165
- // edges:{
166
- // name:"edges",
167
- // description:"To relate one document to another.Labels can be configured using the db_network setting document.This stores edges of a simple directed graph",
168
- // schema:{
169
- // required:["graph","node1","node2","label"],
170
- // type:"object",
171
- // additionalProperties: false,
172
- // properties : {
173
- // graph:{
174
- // type:"string",
175
- // minLength:3,
176
- // default:"default",
177
- // maxLength:100,
178
- // description:"Name of the graph in which this edge is being added. This is managed by the db_network setting "
179
- // },
180
- // node1:{
181
- // type:"string",
182
- // description:"the source of this directed edge. this must be a valid document id."
183
- // }
184
- // }
185
- // },
186
- // settings :{
187
- // primary_key:["graph","node1","node2","label"],
188
- // non_editable_fields:["node1","node2","graph"],
189
- // encrypted_fields:[],
190
- // single_record:false
191
- // }
192
- // }
193
264
  };
194
265
 
195
266
  // this is not stored in the DB. only for validating the metadata during doc update
196
267
  export const editable_metadata_schema = {
197
268
  additionalProperties: false,
269
+ type:"object",
198
270
  properties:{
199
271
  tags:{
200
272
  type:"array",
@@ -0,0 +1,313 @@
1
+ // to test database operations. assuming the class is initialized successfully
2
+ // to test initialization of the BeanBagDB class
3
+ import { get_pdb_doc } from "./pouchdb.js";
4
+ import assert, { throws, strictEqual, rejects } from "assert";
5
+ import {
6
+ BeanBagDB,
7
+ DocCreationError,
8
+ EncryptionError,
9
+ ValidationError,
10
+ DocNotFoundError,
11
+ DocUpdateError,
12
+ } from "../src/index.js";
13
+
14
+ import * as chai from "chai";
15
+ import chaiAsPromised from "chai-as-promised";
16
+
17
+ chai.use(chaiAsPromised);
18
+
19
+ // Then either:
20
+ const expect = chai.expect;
21
+
22
+ let database; // this is the global db object
23
+
24
+ describe("Edge constraint insertion tests", async () => {
25
+ const constraint_docs_invalid = [
26
+ [
27
+ "error when node1 empty",
28
+ {
29
+ node1: "",
30
+ node2: "*",
31
+ name: "sample",
32
+ label: "Sample",
33
+ },
34
+ ],
35
+ [
36
+ "error when node2 empty ",
37
+ {
38
+ node1: "*",
39
+ node2: "",
40
+ name: "sample",
41
+ label: "Sample",
42
+ },
43
+ ],
44
+ [
45
+ "error when edge name empty",
46
+ {
47
+ node1: "*",
48
+ node2: "*",
49
+ name: "",
50
+ label: "Sample",
51
+ },
52
+ ],
53
+ [
54
+ "error when node 1 invalid 1",
55
+ {
56
+ node1: "*3434",
57
+ node2: "*",
58
+ name: "sample",
59
+ label: "Sample",
60
+ },
61
+ ],
62
+ [
63
+ "error when node 1 invalid 2",
64
+ {
65
+ node1: "1212121 ssd sdsd",
66
+ node2: "*",
67
+ name: "sample",
68
+ label: "Sample",
69
+ },
70
+ ],
71
+ [
72
+ "error when node 1 invalid 3",
73
+ {
74
+ node1: "sample1-sample2",
75
+ node2: "*",
76
+ name: "sample",
77
+ label: "Sample",
78
+ },
79
+ ],
80
+ [
81
+ "error when node 1 invalid 4",
82
+ {
83
+ node1: "*+sample1-sample2",
84
+ node2: "*",
85
+ name: "sample",
86
+ label: "Sample",
87
+ },
88
+ ],
89
+ [
90
+ "error when edge name is invalid",
91
+ {
92
+ node1: "*",
93
+ node2: "*",
94
+ name: "Sample one",
95
+ label: "Sample",
96
+ },
97
+ ],
98
+ ];
99
+
100
+ before(async () => {
101
+ // adding a schema
102
+ let doc_obj = get_pdb_doc("test_database_41", "qwe33rtyuiopaqwsde1254");
103
+ database = new BeanBagDB(doc_obj);
104
+ await database.ready(); // Ensure the database is ready before running tests
105
+ try {
106
+ //console.log(test_schema)
107
+ //let a = await database.create("schema",test_schema)
108
+ //console.log("Ready for more tests...");
109
+ } catch (error) {
110
+ console.log("error in before");
111
+ console.log(error);
112
+ }
113
+ });
114
+
115
+ constraint_docs_invalid.forEach((element, index) => {
116
+ it(`${element[0]}`, async () => {
117
+ await rejects(async () => {
118
+ await database.create("setting_edge_constraint", element[1]);
119
+ }, DocCreationError);
120
+ });
121
+ });
122
+ });
123
+
124
+ describe("Edge insertion test", async () => {
125
+ // it('read docs', async () => {
126
+ // try {
127
+ // let udata = await database3.search({selector:{"schema":"book"}})
128
+ // assert(udata.docs.length==2)
129
+ // } catch (error) {
130
+ // //console.log(error)
131
+ // throw error
132
+ // }
133
+ // })
134
+
135
+ const edge_docs_invalid = [
136
+ [
137
+ "error when invalid node 1",
138
+ {
139
+ node1: "sample",
140
+ node2: "*",
141
+ name: "does not matter",
142
+
143
+ },
144
+ ],
145
+ [
146
+ "error when node 2 invalid ",
147
+ {
148
+ node1: { link: "does_not_exists" },
149
+ node2: "invalid,must be a criteria obj",
150
+ name: "sample",
151
+ },
152
+ ],
153
+
154
+ [
155
+ "error when edge name invalid",
156
+ {
157
+ node1: { link: "sample1" },
158
+ node2: { link: "sample2" },
159
+ name: "sample",
160
+
161
+ },
162
+ ],
163
+ [
164
+ "error when both nodes are same",
165
+ {
166
+ node1: {},
167
+ node2: "*",
168
+ name: "Sample one"
169
+ },
170
+ ],
171
+ ];
172
+
173
+ before(async () => {
174
+ // adding a schema
175
+ let doc_obj = get_pdb_doc("test_database_41", "qwe33rtyuiopaqwsde1254");
176
+ database = new BeanBagDB(doc_obj);
177
+ await database.ready(); // Ensure the database is ready before running tests
178
+ try {
179
+ let schemas = [
180
+ {
181
+ name: "player",
182
+ description:"Player",
183
+ schema: {
184
+ additionalProperties:true,
185
+ type:"object",
186
+ properties: {
187
+ name: {
188
+ type: "string",
189
+ },
190
+ },
191
+ },
192
+ settings: {
193
+ primary_keys: ["name"],
194
+ encrypted_fields: [],
195
+ non_editable_fields: ["name"],
196
+ },
197
+ },
198
+ {
199
+ name: "team",
200
+ description:"Team",
201
+ schema: {
202
+ additionalProperties:true,
203
+ type:"object",
204
+ properties: {
205
+ name: {
206
+ type: "string",
207
+ },
208
+ },
209
+ },
210
+ settings: {
211
+ primary_keys: ["name"],
212
+ encrypted_fields: [],
213
+ non_editable_fields: ["name"],
214
+ },
215
+ },
216
+ {
217
+ name: "match",
218
+ description:"Match",
219
+ schema: {
220
+ type:"object",
221
+ additionalProperties:true,
222
+ properties: {
223
+ name: {
224
+ type: "string",
225
+ },
226
+ },
227
+ },
228
+ settings: {
229
+ primary_keys: ["name"],
230
+ encrypted_fields: [],
231
+ non_editable_fields: ["name"],
232
+ },
233
+ },
234
+ ];
235
+ for (let index = 0; index < schemas.length; index++) {
236
+ try {
237
+ const element = schemas[index];
238
+ await database.create("schema", element);
239
+ } catch (error) {
240
+ console.log("not heeee")
241
+ console.log(error)
242
+ }
243
+
244
+ }
245
+ const records = [
246
+ ["player", { name: "player1" }],
247
+ ["player", { name: "player2" }],
248
+ ["player", { name: "player3" }],
249
+ ["player", { name: "player4" }],
250
+ ["player", { name: "player5" }],
251
+ ["player", { name: "player6" }],
252
+ ["player", { name: "player7" }],
253
+ ["player", { name: "player8" }],
254
+ ["player", { name: "player9" }],
255
+ ["player", { name: "player10" }],
256
+ ["player", { name: "player11" }],
257
+ ["player", { name: "player12" }],
258
+ ["player", { name: "player13" }],
259
+ ["player", { name: "player14" }],
260
+ ["player", { name: "player15" }],
261
+ ["team", { name: "team1" }],
262
+ ["team", { name: "team2" }],
263
+ ["match", { name: "match1" }],
264
+ ["match", { name: "match2" }],
265
+ ["system_edge_constraint",{ name:"part_of",node1:"player",node2:"team", max_from_node1:1, max_to_node2:5 , label:"is part of "}],
266
+ ["system_edge_constraint",{name: "plays",node1:"team",node2:"match",max_from_node1 : 1, max_to_node2: 2 , label:"plays in" }]
267
+ ];
268
+ for (let index = 0; index < records.length; index++) {
269
+ const element = records[index];
270
+ let data1 = element[1];
271
+ await database.create(element[0], data1, { link: data1.name });
272
+ }
273
+ } catch (error) {
274
+ console.log("error in before....");
275
+ console.log(error);
276
+ }
277
+ });
278
+
279
+ edge_docs_invalid.forEach((element, index) => {
280
+ it(`${element[0]}`, async () => {
281
+ await rejects(async () => {
282
+ await database.create("setting_edge", element[1]);
283
+ }, DocCreationError);
284
+ });
285
+ });
286
+
287
+
288
+ let valid_edges = [
289
+ {node1:{link:"player1"},node2:{link:"team1"},edge_name:"part_of"},
290
+ {node1:{link:"player2"},node2:{link:"team1"},edge_name:"part_of"},
291
+ {node1:{link:"player3"},node2:{link:"team1"},edge_name:"part_of"},
292
+ {node1:{link:"player4"},node2:{link:"team1"},edge_name:"part_of"},
293
+ {node1:{link:"player5"},node2:{link:"team1"},edge_name:"part_of"},
294
+ {node1:{link:"player6"},node2:{link:"team2"},edge_name:"part_of"},
295
+ {node1:{link:"player7"},node2:{link:"team2"},edge_name:"part_of"}
296
+ ]
297
+
298
+ valid_edges.forEach((element, index) => {
299
+ it(`adds an edge`, async () => {
300
+ try {
301
+ let edge = await database.create_edge(element.node1,element.node2,element.name)
302
+ console.log(edge)
303
+ } catch (error) {
304
+ console.log(error)
305
+ }
306
+
307
+ });
308
+ });
309
+
310
+
311
+
312
+
313
+ });
@@ -1656,7 +1656,7 @@ describe("Doc search tests", async () => {
1656
1656
  it('all docs', async () => {
1657
1657
  try {
1658
1658
  let udata = await database3.search({selector:{}})
1659
- assert(udata.docs.length==8)
1659
+ assert(udata.docs.length==11)
1660
1660
  } catch (error) {
1661
1661
  //console.log(error)
1662
1662
  throw error
@@ -1676,7 +1676,7 @@ describe("Doc search tests", async () => {
1676
1676
  it('read docs 2', async () => {
1677
1677
  try {
1678
1678
  let udata = await database3.search({selector:{"schema":"schema"}})
1679
- assert(udata.docs.length==4) // schema,book,setting,key
1679
+ assert(udata.docs.length==7) // schema,book,setting,key,edge,edge_constraints
1680
1680
  } catch (error) {
1681
1681
  //console.log(error)
1682
1682
  throw error
@@ -19,15 +19,12 @@ let database; // this is the global db object
19
19
 
20
20
  describe("Testing plugin load", async () => {
21
21
 
22
-
23
22
  before(async () => {
24
23
  let doc_obj = get_pdb_doc("test_database_30", "qwertyuiopaqwsde1254");
25
24
  database = new BeanBagDB(doc_obj);
26
25
  await database.ready(); // Ensure the database is ready before running tests
27
26
  console.log("Ready for more tests...");
28
27
  });
29
-
30
-
31
28
 
32
29
  it('successfully loads the plugin', async () => {
33
30
  try {
@@ -41,10 +38,10 @@ describe("Testing plugin load", async () => {
41
38
 
42
39
  it('successfully runs the loaded the plugin method', async () => {
43
40
  try {
44
-
45
- let command = await database["txtcmd"].parse("new/system_keys")
46
- console.log(command)
47
- assert (1 ==2)
41
+ //await database.load_plugin("txtcmd",text_command)
42
+ let command = await database.plugins["txtcmd"].parse("new/system_keys")
43
+ //console.log(command)
44
+ assert (command.valid ==true)
48
45
  } catch (error) {
49
46
  console.log(error)
50
47
  throw error