I had this requirement where there was a need to rename some of the existing Managed MetaData Terms, [Old Parent, Old1, Old2, & Old3] to a single new Term [New Term].
Usually, renaming a single term is no big deal. However, this was a Many => One relationship and you cannot have 2 or more Terms with identical names at the same level of hierarchy. So renaming was not an option here. We had to make sure that for all the existing ListItems, the GUIDs of every old term should get replaced with the New Term GUID. Following is the PowerShell script I came up with to do this job.
$exportDir = "C:\Piyush\ChronicleMigration\ReMapTerms" $destWebUrl = "http://win-hk4iec2ge2r:40851/sites/site102" $destLibrary = "TestList" $taxonomyTerm = "Managed Metadata Service" $trmGrp = "TermGroup" $ts = "TermSetMapTest" $fldName = "TermSetMapTest" $oldTerms = @("Old1", "Old2", "Old3", "Old Parent") $strOldTerms = $oldTerms | foreach {$_ + "|"} $newTermName = "New Term" $logFileName = [IO.Path]::Combine($exportDir, ("TermMapLogFile_" + [string](get-date -format "yyyyMMdd_hhmmtt") + ".txt")) Function LogMessage($strMsg, $lgFlNm) { $strMsg | out-file -Filepath $lgFlNm -append } [void][reflection.assembly]::Loadwithpartialname("microsoft.sharepoint") Function AddRemoveTerm($item, $strOldTerm, $oldTerm, $newTerm, $isAlreadyAdded) { [Microsoft.SharePoint.Taxonomy.TaxonomyFieldValueCollection]$taxCollection = $item[$fldName] $tcTemp = new-object Microsoft.SharePoint.Taxonomy.TaxonomyFieldValueCollection($item.Fields[$fldName]) $strFldVal = ([string]$item[$fldName]) if($strFldVal.Contains($strOldTerm)) { foreach($tmVal in $taxCollection) { if($tmVal.Label -eq $oldTerm) { Write-Host ("Removing Term:: " + $oldTerm) LogMessage ("Removing Term:: " + $oldTerm) $logFileName $tcTemp.Add($tmVal) } } foreach($tmVal in $tcTemp) { $taxCollection.Remove($tmVal) } if(($isAlreadyAdded -eq $false) -and ($strFldVal.Contains($newTermName) -eq $false)) { $taxonomyFieldValueNew = new-object Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue($item.Fields[$fldName]) $taxonomyFieldValueNew.TermGuid = $newTerm.Id $taxonomyFieldValueNew.Label = $newTerm.Name $taxCollection.Add($taxonomyFieldValueNew) $isAlreadyAdded = $true Write-Host ("Creating new Mapping:: " + $oldTerm) LogMessage ("Creating new Mapping:: " + $oldTerm) $logFileName } else { $isAlreadyAdded = $true } $tfield = [Microsoft.SharePoint.Taxonomy.TaxonomyField]$item.Fields[$fldName] $tfield.SetFieldValue($item, $taxCollection) $item.SystemUpdate($false); } return $isAlreadyAdded } Function ReMapTerms() { # Load the Site / Web $ds = new-object Microsoft.SharePoint.SPSite($destWebUrl) $dw = $ds.OpenWeb() #Get destination site Term-Store $taxonomySession = Get-SPTaxonomySession -Site $dw.Site # Grab the list $dL = $dw.Lists[$destLibrary] $spQuery = New-Object Microsoft.SharePoint.SPQuery $spQuery.ViewAttributes = "Scope='Recursive'" $listItems = $dL.GetItems($spQuery) Write-Host ("Starting Term mapping ---") -foregroundcolor "green" LogMessage "**************************************************************" $logFileName LogMessage ("Term mapping Started at " + [string](get-date -format "yyyymmdd_hhmmtt")) $logFileName LogMessage "**************************************************************" $logFileName $termStore = $taxonomySession.TermStores[$taxonomyTerm]; $sabGroup = $termStore.Groups[$trmGrp] $termSet = $sabGroup.TermSets[$ts] $termCHNew = $termSet.Terms[$newTermName] foreach($item in $listItems) { Try { Write-Host ("Processing... " + $item.Title) LogMessage ("Processing... " + $item.Title) $logFileName Write-Host ("---------------------------------------------------") -foregroundcolor "DarkCyan" LogMessage "---------------------------------------------------" $logFileName $isAlreadyAdded = $false $len = $oldTerms.Length for($i = 0; $i -lt $len; $i++) { $isAlreadyAdded = AddRemoveTerm $item $strOldTerms[$i] $oldTerms[$i] $termCHNew $isAlreadyAdded } if($isAlreadyAdded -eq $true) { Write-Host ("Saving the current changes.") -foregroundcolor "green" LogMessage "Saving the current changes." $logFileName $item.Update() } } Catch { Write-Host ("Unknown Error:: " + [string]$item.Id + " => " + $_.Exception.Message) -foregroundcolor "red" LogMessage ("Unknown Error:: " + [string]$item.Id + " => " + $_.Exception.Message) $logFileName } Write-Host ("*****************************************************") -foregroundcolor "yellow" LogMessage "*****************************************************" $logFileName } Write-Host ("Term mapping Completed at " + [string](get-date -format "yyyymmdd_hhmmtt")) -foregroundcolor "green" LogMessage ("Term mapping Completed at " + [string](get-date -format "yyyymmdd_hhmmtt")) $logFileName #Disposing web objects to free memory $taxonomySession = $null $dw.Dispose() $ds.Dispose() } # Calling the main function ReMapTerms
Save the script say, with a name, MapSpecificTermsBlog.ps1. Run the script in SharePoint Management Shell.
This is the Screenshot of the existing ListItems MetaData field before executing the script.
Post running the script, the ListItems MetaData field will get changed to this.
Key Takeaways
- This is based on Many => One relationship. So multiple old Terms value will get replaced with a single new Term.
- To convert it to a One => One model, just add a single old value to the variable, $oldTerms.
- $exportDir is your physical location where logs will be written.
- $destWebUrl is your site’s URL.
- $destLibrary is the name of your List/Library where the changes will be done.
- $taxonomyTerm is the name of your Managed MetaData Service
- $trmGrp is the name of your TermGroup.
- $ts is the TermSet name.
- $fldName is the List SiteColumn name.
- $oldTerms is the list of names of Terms that needs to replaced.
- $newTermName is the name of the Term which will replace the old values.