-
-
Save 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() |
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.
@irenie-beanie try it now