@@ -981,26 +981,28 @@ const id = async function (req, res, next) {
981981 }
982982}
983983
984+ /**
985+ * Create many objects at once with the power of MongoDB bulkWrite() operations.
986+ *
987+ * @see https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/
988+ */
984989const bulkCreate = async function ( req , res , next ) {
985990 res . set ( "Content-Type" , "application/json; charset=utf-8" )
986991 const documents = req . body
987992 let err = { }
988- // TODO: validate documents gatekeeper function?
989993 if ( ! Array . isArray ( documents ) ) {
990994 err . message = "The request body must be an array of objects."
991- //err.status = 406
992995 err . status = 400
993996 next ( createExpressError ( err ) )
994997 return
995998 }
996999 if ( documents . length === 0 ) {
9971000 err . message = "No action on an empty array."
998- //err.status = 406
9991001 err . status = 400
10001002 next ( createExpressError ( err ) )
10011003 return
10021004 }
1003- let gatekeep = documents . filter ( d => {
1005+ const gatekeep = documents . filter ( d => {
10041006 // Each item must be valid JSON, but can't be an array.
10051007 if ( Array . isArray ( d ) || typeof d !== "object" ) return d
10061008 try {
@@ -1018,6 +1020,7 @@ const bulkCreate = async function (req, res, next) {
10181020 next ( createExpressError ( err ) )
10191021 return
10201022 }
1023+
10211024 // TODO: bulkWrite SLUGS? Maybe assign an id to each document and then use that to create the slug?
10221025 // let slug = req.get("Slug")
10231026 // if(slug){
@@ -1030,10 +1033,14 @@ const bulkCreate = async function (req, res, next) {
10301033 // slug = slug_json.slug_id
10311034 // }
10321035 // }
1033- let bulkOps = [ ]
1034- let generatorAgent = getAgentClaim ( req , next )
1036+
1037+ // unordered bulkWrite() operations have better performance metrics.
1038+ let bulkOps = [ { 'ordered' :false } ]
1039+ const generatorAgent = getAgentClaim ( req , next )
10351040 documents . forEach ( d => {
1036- const providedID = d . _id
1041+ // Do not create {}
1042+ if ( Object . keys ( d ) . length === 0 ) continue
1043+ const providedID = d ?. _id
10371044 const id = isValidID ( providedID ) ? providedID : ObjectID ( )
10381045 d = utils . configureRerumOptions ( generatorAgent , d )
10391046 // id is also protected in this case, so it can't be set.
@@ -1060,26 +1067,31 @@ const bulkCreate = async function (req, res, next) {
10601067 }
10611068}
10621069
1070+ /**
1071+ * Update many objects at once with the power of MongoDB bulkWrite() operations.
1072+ * Make sure to alter object __rerum.history as appropriate.
1073+ * The same object may be updated more than once, which will create history branches (not straight sticks)
1074+ *
1075+ * @see https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/
1076+ */
10631077const bulkUpdate = async function ( req , res , next ) {
10641078 res . set ( "Content-Type" , "application/json; charset=utf-8" )
10651079 const documents = req . body
10661080 let err = { }
10671081 let encountered = [ ]
10681082 if ( ! Array . isArray ( documents ) ) {
10691083 err . message = "The request body must be an array of objects."
1070- //err.status = 406
10711084 err . status = 400
10721085 next ( createExpressError ( err ) )
10731086 return
10741087 }
10751088 if ( documents . length === 0 ) {
10761089 err . message = "No action on an empty array."
1077- //err.status = 406
10781090 err . status = 400
10791091 next ( createExpressError ( err ) )
10801092 return
10811093 }
1082- let gatekeep = documents . filter ( d => {
1094+ const gatekeep = documents . filter ( d => {
10831095 // Each item must be valid JSON, but can't be an array.
10841096 if ( Array . isArray ( d ) || typeof d !== "object" ) return d
10851097 try {
@@ -1090,24 +1102,24 @@ const bulkUpdate = async function (req, res, next) {
10901102 // Items must have an @id , or in some cases an id will do
10911103 const idcheck = _contextid ( d [ "@context" ] ) ? d . id ?? d [ "@id" ] : d [ "@id" ]
10921104 if ( ! idcheck ) return d
1093- } )
1105+ } )
1106+ // The empty {}s will cause this error
10941107 if ( gatekeep . length > 1 ) {
10951108 err . message = "All objects in the body of a `/bulkUpdate` must be JSON and must contain a declared identifier property."
10961109 err . status = 400
10971110 next ( createExpressError ( err ) )
10981111 return
10991112 }
1100- let bulkOps = [ ]
1101- let generatorAgent = getAgentClaim ( req , next )
1113+ // unordered bulkWrite() operations have better performance metrics.
1114+ let bulkOps = [ { 'ordered' :false } ]
1115+ const generatorAgent = getAgentClaim ( req , next )
11021116 for ( const objectReceived of documents ) {
11031117 // We know it has an id
11041118 const idReceived = objectReceived [ "@id" ] ?? objectReceived . id
1105- // but we will not update the same thing twice.
1106- if ( encountered . includes ( idReceived ) ) continue
1119+ // Update the same thing twice? can vs should .
1120+ // if(encountered.includes(idReceived)) continue
11071121 encountered . push ( idReceived )
1108- if ( ! idReceived . includes ( process . env . RERUM_ID_PREFIX ) ) {
1109- continue
1110- }
1122+ if ( ! idReceived . includes ( process . env . RERUM_ID_PREFIX ) ) continue
11111123 let id = parseDocumentID ( idReceived )
11121124 let originalObject
11131125 try {
@@ -1116,12 +1128,8 @@ const bulkUpdate = async function (req, res, next) {
11161128 next ( createExpressError ( error ) )
11171129 return
11181130 }
1119- if ( null === originalObject ) {
1120- continue
1121- }
1122- if ( utils . isDeleted ( originalObject ) ) {
1123- continue
1124- }
1131+ if ( null === originalObject ) continue
1132+ if ( utils . isDeleted ( originalObject ) ) continue
11251133 id = ObjectID ( )
11261134 let context = objectReceived [ "@context" ] ? { "@context" : objectReceived [ "@context" ] } : { }
11271135 let rerumProp = { "__rerum" : utils . configureRerumOptions ( generatorAgent , originalObject , true , false ) [ "__rerum" ] }
0 commit comments