How to get rid of unused tags? And how to merge tags?


After doing some cleaning up of unused tags in my repository, I’ve noticed it’s a time consuming process, because the only way seems to be to browse tags by count (one click), then revert sorting order (second click), delete one tag with 0 items using it, and then start all over again because the tags get redisplayed by Name, Desc.

  1. So, is there a way to stop the resetting of sort criteria after the deletion of every tag?
  2. And is there a simple way to implement an extra button that will take care of all tags used 0 times, once and for all?
  3. And, as an extra feature: is there any way to have tags merging, when they have the same value? (example: two tags, “bravo 1” and “bravo 2”, if I rename the first one to “bravo” it will work, but if I try to rename also the second one to “bravo” it will throw an error, instead of merging it with the first one).

Thanks for any help on this.

Been thinking about (2). An alternative way would be for the page to behave like the item’s tags page, i.e. to allow the selection-for-deletion of one or more tags, and then the effective deletion would happen when users presses the Save button. This way it would be possible to delete also tags used 1 time or more. I’m afraid that would probably need a change of the whole/admin/tags/browse.php page, though.

As usual, any help or comment would be appreciated.

I’ve been working on the “merge” part, and manage to have it working by changing the code (if anybody wants to try to turn the changes into a plugin, is more than welcome, although maybe the changes could be implemented in the core of a future release). Here’s what I’ve done:

file application/controllers/TagsController.php (modified the last 5 lines)

public function renameAjaxAction() {
        $csrf = new Omeka_Form_SessionCsrf;
        $oldTagId = $_POST['pk'];
        $oldTag = $this->_helper->db->findById($oldTagId);
        $oldName = $oldTag->name;
        $newName = trim($_POST['value']);
        $error = __('Error occurred.');

        $oldTag->name = $newName;
        if ($csrf->isValid($_POST)) {
			if ($oldTag->save(false)) {
			} else {
				$newTag = $this->_helper->db->findOrNew($newName);
				$newTagId = $newTag->id;
				$this->_helper->db->mergeTags($oldTagId, $newTagID);
		} else {

and file application/models/Table/Tag.php (new function added)

public function mergeTags($oldTagId, $newTagId) {
        $db = $this->getDb();
        $sql = "UPDATE $db->RecordsTag SET tag_id = $newTagId, time = CURRENT_TIMESTAMP WHERE tag_id = $oldTagId AND record_id NOT IN (SELECT record_id FROM (SELECT DISTINCT record_id FROM $db->RecordsTag WHERE tag_id = $newTagId) AS tmptable)";
		$sql = "DELETE FROM $db->RecordsTag WHERE tag_id = $oldTagId";
		$sql = "DELETE FROM $db->Tag WHERE id = $oldTagId";

Now, here’re some possible improvements, in case anybody wanted to contribute:

  1. before the merging takes place, a message box asking for confirmation;
  2. after the merging has taken place, update the count of the tag, at the same time hiding the emptied one;
  3. error message in case of mishap during the merging.

Hope this helps.

There’s actually a typo in

$this->_helper->db->mergeTags($oldTagId, $newTagID);

the correct one being:

$this->_helper->db->mergeTags($oldTagId, $newTagId);

As for the general issue (removing the last instance of a tag from an Item won’t remove that tag from the list of available tags), I think the problem resides in the application/models/Mixin/Tag.php file: every function removing “taggings” linked to a tag is not checking against the tags table to see whether that specific tag is still used or not in any other Item (and, in negative case, remove it).
Adding such extra feature would probably solve the issue (see also

To fix it, one could edit the deleteTags function in application/models/Mixin/Tag.php as follows:

$findWith['tag'] = $tags;
$findWith['record'] = $this->_record;
$db = $this->_record->getDb();

$taggings = $this->_joinTable->findBy($findWith);


foreach ($taggings as $tagging) {
   $removed[] = $this->_tagTable->find($tagging->tag_id);
foreach ($tags as $tag) {
   $count = $this->_joinTable->count(array('tag' => $tag));
   if ($count == 0) {
      $db->delete($db->Tags, array('name = ?' => $tag));
$nameForHook = strtolower($this->_type);

Hope this helps.