Get record by key

Viewing 0 reply threads
  • Author
    Posts
  • October 27, 2019 at 2:58 AM #37439

    Sam Moffatt
    Participant

    I had a situation where I wanted to automatically download a set of files from the web and store them as Tap Forms document. In my case I have two forms: one parent form that stores the URL I want to grab data from as a base and second form that I want put new records with more details keyed for me by source URL.

    It takes a few parameters to operate (the last three are optional):

    • formName: The name of the source form for the records; can also be a form ID (required).
    • keyFieldId: The field ID of the key field in the record (required).
    • keyValue: The value to match as the key (required).
    • createIfMissing: Control if a record is created if none is found (default: true).
    • earlyTermination: Control if function terminates as soon as a match is found (default: true).
    • alwaysScanOnMiss: Control if we should assume concurrent access (default: false)

    I use this with my Script Manager idiom, so I have something like this:

    document.getFormNamed('Script Manager').runScriptNamed('getLinkedRecord');
    let currentRecord = getRecordFromFormWithKey('Location Tracker', source_id, entry.href, true, false);
    

    The early termination option is interesting and how I’d advise using it is dependent upon your use case. If you set early termination, when the script is iterating through all of the records it will return the first match it finds. If you only expect to call this function once, maybe twice, then this will work fine for you because depending on your ordering you will avoid a full traversal of your recordset.

    If you are running this in a loop or some other construct where you expect to call the function multiple times then disabling early termination ensures that everything is indexed on the first pass. If you expect to have many cache hits, then this will improve performance significantly as subsequent calls will immediately return based on a direct lookup. Record creation is added as a method here so that newly created records can be immediately indexed in the system avoiding future cache misses.

    The worst case for this method is a cache miss. After the first scan of the records, it assumes no other concurrent access. If you don’t want this behaviour, there is another flag to always scan if a miss isn’t found and the form has already been scanned.

    Here’s the script:

    // ========== getRecordFromFormWithKey Start ========== //
    // NAME: Get Record From From With Key
    // VERSION: 1.0
    
    /**
     * Get a record from a form with a key field, optionally creating it if missing.
     * Note: assumes your key field is unique.
     *
     * formName           The name of the source form for the records (required).
     * keyFieldId         The field ID of the key field in the record (required).
     * keyValue           The value to match as the key (required).
     * createIfMissing    Control if a record is created if none is found (default: true).
     * earlyTermination   Control if function terminates as soon as a match is found (default: true).
     * alwaysScanOnMiss   Control if we should assume concurrent access (default: false)
     */
    function getRecordFromFormWithKey(formName, keyFieldId, keyValue, createIfMissing = true, earlyTermination = true, alwaysScanOnMiss = false)
    {
    	// Check if our basic parameters are set.
    	if (!formName || !keyFieldId || !keyValue)
    	{
    		throw new Error("Missing required parameters");
    	}
    	
    	// Determine the target form (check if we were given an ID or else assume a name)
    	let targetForm = undefined;
    	if (formName.match(/frm-/))
    	{
    		targetForm = document.getFormWithId(formName);
    	}
    	else
    	{
    		targetForm = document.getFormNamed(formName);
    	}
    
    	// Check if our global variable has been setup.
    	if (!indexedRecordIndex)
    	{
    		var indexedRecordIndex = {};	
    	}
    	
    	// Flag for if we've indexed this form already.
    	if (!indexedRecordState)
    	{
    		var indexedRecordState = {};
    	}
    
    	// Create a key for this form-field combination.
    	// Form+Field is the key.
    	let indexedRecordKey = targetForm.getId() + "_" + keyFieldId;
    
    	// Check to see if this particular link field has been setup.
    	if (!indexedRecordIndex[indexedRecordKey])
    	{
    		indexedRecordIndex[indexedRecordKey] = {};
    	}
    
    	
    	// Short circuit if we have an exact match.
    	if (indexedRecordIndex[indexedRecordKey][keyValue])
    	{
    		return indexedRecordIndex[indexedRecordKey][keyValue];
    	}
    	
    	// No immediate match, check to see if we should scan everything.
    	// alwaysScanOnMiss forces this code path each execution.
    	// The check to indexedRecordState is if this has been indexed.
    	if (alwaysScanOnMiss || !indexedRecordState[indexedRecordKey])
    	{	
    		// Brute force search :(
    		let records = targetForm.getRecords();
    
    		// Iterate through all of the records and look for a match.
    		for (currentRecord of records)
    		{
    			// Set up a reverse link for this value.
    			let recordKeyValue = currentRecord.getFieldValue(keyFieldId);
    			indexedRecordIndex[indexedRecordKey][recordKeyValue] = currentRecord;
    
    			// If this is a match and early termination is setup, return immediately.
    			if (earlyTermination && recordKeyValue == keyValue)
    			{
    				return currentRecord;
    			}
    		}
    		
    		// Flag this record-field as being indexed.
    		indexedRecordState[indexedRecordKey] = true;
    	}
    
    	// Check to see if we got a match here and return if the key exists.
    	if (indexedRecordIndex[indexedRecordKey][keyValue])
    	{
    		return indexedRecordIndex[indexedRecordKey][keyValue];
    	}
    	else if (createIfMissing)
    	{
    		// If createIfMissing is set, create a new record.
    		// Note: it's expected the caller will call document.saveAllChanges();
    		let newRecord = targetForm.addNewRecord();
    		// Set the key value to our search value.
    		newRecord.setFieldValue(keyFieldId, keyValue);
    		// Link this up to save us another lookup in future.
    		indexedRecordIndex[indexedRecordKey][keyValue] = newRecord;
    		// And now we return the new record. 
    		return newRecord;
    	}
    	
    	// If we didn't find anything, return undefined.
    	return undefined;
    }
    // ========== getRecordFromFormWithKey End ========== //
    
Viewing 0 reply threads

You must be logged in to reply to this topic.