-
-
Save dev-on2air/b68450a877ab99fe4a93ef7f9c387568 to your computer and use it in GitHub Desktop.
| /***** | |
| * Title: Same Table Backlinks | |
| * License: MIT | |
| * Author: Openside (Team behind On2Air products and BuiltOnAir community) | |
| * Sites: | |
| * https://openside.com - Openside Consulting Services | |
| * https://openside.com/#products - On2Air Products | |
| * https://builtonair.com - All things Airtable Community | |
| * | |
| * Reach out for all your Airtable needs | |
| * | |
| * Explainer Video: https://www.loom.com/share/9f90bb7bf95b4581a12a9750edb3a376 | |
| * | |
| * Description: Typically when creating a Linked Records to another table, | |
| * it will auto-create a field on the other table holding the backlinks back to the current table. | |
| * However, when creating links within the same table, it does not generate the 2nd backlink field. | |
| * | |
| * This script will do that for you. This is useful to see where a single table linked record field | |
| * is in use within the same field. | |
| * | |
| * Instructions: Configure the links array below with the information to generate the links. | |
| * If no links are configured, it will ask for user input, so its ok to leave blank. | |
| * | |
| * The 3 configuration items are: | |
| * table: The table name containing the linked record | |
| * view: (Optional) if set, then will filter records to check based on the view | |
| * source: The field name that is a linked record to the same table that is what | |
| * you are already updating. This field does not get modified. | |
| * dest: The destination field that will determine all the places each record is used as a source. | |
| * This field will get replaced each time script is ran | |
| * | |
| */ | |
| //-------------START CONFIGURATION ------------------// | |
| let links = [ | |
| // { | |
| // table:"Family Tree", | |
| // view: '', | |
| // source:"Parents", | |
| // dest:"Children" | |
| // }, | |
| // { | |
| // table:"Family Tree 2", | |
| // source:"Parents", | |
| // dest:"Children" | |
| // } | |
| ] | |
| //-------------END CONFIGURATION ------------------// | |
| if(!links || !links.length){ | |
| output.markdown("## Select Your Settings to Setup Children Links") | |
| let table = await input.tableAsync("Select The table") | |
| let source = await input.fieldAsync("Select The Source Field",table.id) | |
| let dest = await input.fieldAsync("Select The Destination Field",table.id) | |
| links.push({table:table.id,source:source.name,dest:dest.name}) | |
| } | |
| const syncLinks = async( tableId, source, dest, viewName = '' ) => { | |
| let table = base.getTable(tableId) | |
| output.markdown(`#### Syncing - '${table.name}': '${source}' -> '${dest}'`) | |
| let view = viewName ? table.getView(viewName) : null | |
| let recordsFull = view ? await view.selectRecordsAsync() : await table.selectRecordsAsync() | |
| let records = recordsFull.records | |
| let len = records.length | |
| const findChildren = async() => { | |
| let tree = {} | |
| const setTree = ( parent, kid) => { | |
| if(!tree[parent])tree[parent] = [] | |
| tree[parent].push({id:kid}) | |
| } | |
| for(let t=0;t<records.length;t++){ | |
| let rec = records[t] | |
| if (!tree[rec.id]) tree[rec.id] = [] | |
| let parents = rec.getCellValue(source) | |
| if(!parents)continue; | |
| for(let p=0; p<parents.length; p++){ | |
| setTree(parents[p].id, rec.id) | |
| } | |
| } | |
| output.text("------------------------------------") | |
| let parentKeys = Object.keys(tree) | |
| let queue = [] | |
| for(let i=0; i<parentKeys.length; i++){ | |
| let pKey = parentKeys[i] | |
| let pRecord = await recordsFull.getRecord(pKey) | |
| let kids = tree[pKey] | |
| if((!kids || kids.length === 0) && (!pRecord.getCellValue(dest) || pRecord.getCellValue(dest).length === 0))continue | |
| queue.push({id:pKey,fields:{[dest]: kids || []}}) | |
| if(queue.length === 50){ | |
| await table.updateRecordsAsync(queue) | |
| output.text("... "+(i+1)) | |
| queue = [] | |
| } | |
| } | |
| if(queue.length){ | |
| await table.updateRecordsAsync(queue) | |
| output.text("... "+ parentKeys.length) | |
| queue = [] | |
| } | |
| } | |
| await findChildren() | |
| } | |
| output.clear() | |
| output.markdown("## Table Syncing Starting....") | |
| for(let l=0; l<links.length;l++){ | |
| let link = links[l] | |
| await syncLinks( link.table, link.source, link.dest, link.view || '' ) | |
| } | |
| output.markdown("### Syncing Completed.") | |
| //THIS WAS FOR GENERATING THE PARENT LINKS//TESTING ONLY// | |
| // const getRandomInt = (max) => { | |
| // return Math.floor(Math.random() * Math.floor(max)); | |
| // } | |
| // const setParents = async() => { | |
| // for(let t=0;t<records.length;t++){ | |
| // let rec = records[t] | |
| // let parents = [] | |
| // let otherParent = -1 | |
| // while(parents.length < 2){ | |
| // let pIndex = getRandomInt(len) | |
| // if(pIndex != t && pIndex !== otherParent){//cant be your own parent or have 2 of same | |
| // parents.push({id:records[pIndex].id}) | |
| // otherParent = pIndex | |
| // } | |
| // } | |
| // await table.updateRecordAsync(rec.id,{ | |
| // [SOURCE_FIELD]:parents | |
| // }) | |
| // } | |
| // } | |
| // //await setParents() |
@irenie-beanie try it now
Hi, I have what's probably a dumb question. I used this code in my Airtable script extension with no problem, but when I tried to build an Airtable automation, I get an error message that says "TypeError: output.clear is not a function" and the same thing for output.markdown. Any advice?
Any fix for this issue?
Error
TypeError: output.clear is not a function
at main on line 116
I am running this as part of an Automation
@on2air
Sorry to pester you - do you see any way to fix the error in my last comment?
Would there be a way to do this but have multiple sources linking to a single destination?
If I am reading this correctly, this will only create links, it won't delete them. So if you delete a link in record A pointing to B, then B's link back to A won't ever be deleted.
A fixed version of this that is intended for automations is in my gist here.
ERROR
TypeError: Cannot read properties of null (reading 'length')
at findChildren on line 96
at async syncLinks on line 112
at async main on line 120