You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					102 lines
				
				3.9 KiB
			
		
		
			
		
	
	
					102 lines
				
				3.9 KiB
			| 
											3 years ago
										 | "use strict"; | ||
|  | 
 | ||
|  | Object.defineProperty(exports, "__esModule", { | ||
|  |   value: true | ||
|  | }); | ||
|  | exports.populateProperty = populateProperty; | ||
|  | 
 | ||
|  | const isValueSearchable = value => ['string', 'bigint', 'number'].includes(typeof value) && value !== null && value !== ''; | ||
|  | /** | ||
|  |  * It populates one property in given records | ||
|  |  * | ||
|  |  * @param {Array<BaseRecord>} records   array of records to populate | ||
|  |  * @param {PropertyDecorator} property  Decorator for the reference property to populate | ||
|  |  * @private | ||
|  |  * @hide | ||
|  |  */ | ||
|  | 
 | ||
|  | 
 | ||
|  | async function populateProperty(records, property) { | ||
|  |   const decoratedResource = property.resource(); | ||
|  | 
 | ||
|  |   if (!records || !records.length) { | ||
|  |     return records; | ||
|  |   } | ||
|  | 
 | ||
|  |   const referencedResource = property.reference(); | ||
|  | 
 | ||
|  |   if (!referencedResource) { | ||
|  |     throw new Error([`There is no reference resource named: "${property.property.reference}"`, `for property: "${decoratedResource.id()}.properties.${property.propertyPath}"`].join('\n')); | ||
|  |   } // I will describe the process for following data:
 | ||
|  |   // - decoratedResource = 'Comment'
 | ||
|  |   // - referenceResource = 'User'
 | ||
|  |   // property.path = 'userId'
 | ||
|  |   // first, we create externalIdsMap[1] = null where 1 is userId. This make keys unique and assign
 | ||
|  |   // nulls to each of them
 | ||
|  | 
 | ||
|  | 
 | ||
|  |   const externalIdsMap = records.reduce((memo, baseRecord) => { | ||
|  |     const foreignKeyValue = baseRecord.get(property.propertyPath, { | ||
|  |       includeAllSiblings: true | ||
|  |     }); // 2 kind of properties returning arrays
 | ||
|  |     //   - the one with the array type
 | ||
|  |     //   - the one which are nested within an arrays (fetched by the help of
 | ||
|  |     //   the options { includeAllSiblings: true } in baseRecord.get().
 | ||
|  |     // so we have to take it all into consideration
 | ||
|  | 
 | ||
|  |     if (Array.isArray(foreignKeyValue)) { | ||
|  |       return foreignKeyValue.reduce((arrayMemo, valueInArray) => ({ ...arrayMemo, | ||
|  |         ...(isValueSearchable(valueInArray) ? { | ||
|  |           [valueInArray]: valueInArray | ||
|  |         } : {}) | ||
|  |       }), memo); | ||
|  |     } | ||
|  | 
 | ||
|  |     if (!isValueSearchable(foreignKeyValue)) { | ||
|  |       return memo; | ||
|  |     } | ||
|  | 
 | ||
|  |     memo[foreignKeyValue] = foreignKeyValue; | ||
|  |     return memo; | ||
|  |   }, {}); | ||
|  |   const uniqueExternalIds = Object.values(externalIdsMap); // when no record has reference filled (ie `userId`) = return input `records`
 | ||
|  | 
 | ||
|  |   if (!uniqueExternalIds.length) { | ||
|  |     return records; | ||
|  |   } // now find all referenced records: all users
 | ||
|  | 
 | ||
|  | 
 | ||
|  |   const referenceRecords = await referencedResource.findMany(uniqueExternalIds); // even if record has value for this reference - it might not have the referenced record itself
 | ||
|  |   // this happens quite often in mongodb where there are no constrains on the database
 | ||
|  | 
 | ||
|  |   if (!referenceRecords || !referenceRecords.length) { | ||
|  |     return records; | ||
|  |   } // now assign these users to `externalIdsMap` instead of the empty object we had. To speed up
 | ||
|  |   // assigning them to record#populated we will do in the next step by calling:
 | ||
|  |   // `externalIdsMap[id]` to get populated record instead of finding them in an array
 | ||
|  | 
 | ||
|  | 
 | ||
|  |   referenceRecords.forEach(referenceRecord => { | ||
|  |     // example: externalIds[1] = { ...userRecord } | null (if not found)
 | ||
|  |     const foreignKeyValue = referenceRecord.id(); | ||
|  |     externalIdsMap[foreignKeyValue] = referenceRecord; | ||
|  |   }); | ||
|  |   return records.map(record => { | ||
|  |     // first lets extract all the existing params from the given record which belongs to given
 | ||
|  |     // property. Usually it will be just one element, but for arrays and items nested inside arrays
 | ||
|  |     // there will be more like this for array:
 | ||
|  |     // {
 | ||
|  |     //    'professions.0': '5f7462621eb3495ea0f0edd7',
 | ||
|  |     //    'professions.1': '5f7462621eb3495ea0f0edd6',
 | ||
|  |     // }
 | ||
|  |     const referenceParams = record.selectParams(property.propertyPath, { | ||
|  |       includeAllSiblings: true | ||
|  |     }) || {}; // next we copy the exact params structure to record.populated changing the value with found
 | ||
|  |     // record
 | ||
|  | 
 | ||
|  |     Object.entries(referenceParams).forEach(([path, foreignKeyValueItem]) => { | ||
|  |       record.populate(path, externalIdsMap[foreignKeyValueItem]); | ||
|  |     }); | ||
|  |     return record; | ||
|  |   }); | ||
|  | } |