Ambiguous reference in new .GetPage

I’m trying to use the new .GetPage to pull in content, and it’s failing because I have the same filename in different sections. That is, because 004777.md exists both in /post and /quote, I can’t retrieve either one:

{{ with $.Site.GetPage "/post" }}
  {{ with .GetPage "004777.md" }}
    {{ .Title }}
  {{end}}
{{end}}
error calling GetPage: failed to resolve path from page "/post": page reference "004777.md" is ambiguous

I can’t just use $.Site.GetPage "/post/004777.md" because the actual path is /post/0047/004777.md (I don’t like having 3,200 blog entries all in one directory…).

Is there a workaround?

-j

@vassudanagunta may have an answer for you.

The relative path from /post to /post/0047/004777.md is 0047/004777.md, not 004777.md. The same logic as your file system or relative links in web pages.

So any of the following will work (assuming no Hugo bug):

{{ with $.Site.GetPage "/post" }}
  {{ with .GetPage "0047/004777.md" }}
    {{ .Title }}
  {{end}}
{{end}}
{{ with $.GetPage "/post/0047/004777.md" }}
    {{ .Title }}
{{end}}
{{ with $.Site.GetPage "/post/0047/004777.md" }}
    {{ .Title }}
{{end}}

Pre v0.45, $.Site.GetPage "004777.md" would ALWAYS have returned the same one of the two identically named pages you have, no matter your page context. Even worse, existing links that tested correctly when you authored them could get swapped out from under you with no warning if anyone added another page with the same filename. You’d never know unless you manually tested all your previously published pages.

Yes, but according to the docs: “The Page variant will, if given a relative path – i.e. a path without a leading / – try look for the page relative to the current page.”

It’s the relative to the current page part that doesn’t work in 0.45; the Page version of .GetPage is always doing a global search.

More to the point, the template doesn’t necessarily know the relative path of the content file that’s being pulled in, hence my use of the search functionality.

-j

I did not write those docs. They are wrong. Relative paths do not have “search” semantics. As I said, same as file systems or the web.

Hugo didn’t have “search” functionality before v0.45 either. It always picked one of set of files with the same filename and linked to that one no matter where you were trying to link from.

So the only functional difference in 0.45 is that now it complains when the search returns more than one result.

-j

So the only functional difference in 0.45 is that now it complains when the search returns more than one result.

No, all of the functional differences are explained in my original pull request, excepting changes that @bep made to the logic on top.

More to the point, the template doesn’t necessarily know the relative path of the content file that’s being pulled in, hence my use of the search functionality.

How can your template know such a specific filename but not know its specific path?

Something doesn’t jive.

First I’ve seen that link, but like most people, I look for documentation before trawling GitHub commits and pull requests. Hopefully the docs site will be updated with this information.

Because it’s a page parameter linking across sections, a result of migrating 14 years of MovableType archives. Entry IDs were unique across all blogs in its DB, but that constraint isn’t enforced by Hugo. Basically, GetPage behaves like a search, so I tried to use it as one. When it works, it’s faster than doing a range where across all the pages in a section, which is what I did before GetPage supported content pages.

Looks like the only real fix is to break ~75 out of 3,200 permalinks and then change the ID range for new entries in the other blogs. Making the ID ranges unique going forward will keep my posting script from generating new duplicate file names:

last=$(basename $(ls content/$section/[0-9]*.md | tail -1) .md | sed -e 's/^0*//')
next=$(printf "%06d" $(( $last + 1 ))).md
hugo new --editor emacs $section/$next

-j

Better solution: Have your template figure out the subdir (e.g. 0047) since it appears to be the first 4 chars of your post ID. It should be easy and makes sense, since it is precisely the logic of your content layout.

i.e. you can construct the actual relative or absolute path, no “search” needed.

You won’t need to break any permalinks or make the other changes you describe above. Should work as is.

In this particular instance, yes. In the general case, no. Converting MT 2.64 to Hugo 0.19 was a non-trivial task.

-j

One final alternative: Open an issue for a config setting to disable ambiguity detection. It’s a trivial code change. It’s something that I considered but there was never the discussion about my design that I was hoping for where we’d discuss whether such things would be necessary.

No, I prefer either

  1. file enhancement bug to make the Page .GetPage a scoped search to match the docs, or
  2. file doc bug to make it match the code. :slight_smile:

-j

I doubt #1 is going to happen, because ref linking happens via an index, not a search. Such a change would introduce a performance hit, and, frankly, I don’t think Hugo should support such functionality.

The previous code never did a search. It simply indexed all pages by filename, but only indexed one of a set of dupe filenames. In other words, your previous template worked by luck. If, for example, the dupe in /quote got indexed instead, you’d be exactly where you are now. In addition, there was no guarantee which file of a dupe set would be indexed, so one day Hugo’s index logic changes and all your links to /post/004777.md could get switched to /quote/004777.md, or you add another dupe in another directory that the existing logic gives precedence and your link is silently changed, and you’d never know unless one of your readers noticed and bothered to tell you.

#2 makes sense :slight_smile:

Who knows… you may have incorrect links now (in your site built before v0.45)!

Well, technically it worked perfectly right up to the moment I created the first duplicate entry ID, and then it worked by luck :slight_smile:. And since I do full diffs of public every time I upgrade to a new Hugo release, I can even say that it always picked the right page (index walking the tree in alphabetical order, maybe?).

I’m going to have to check my other large site’s use of GetPage, because many of its ~18,000 content pages link to others based on not-necessarily-unique names across 40+ nested sections. They’re categorized recipes collected from across the net and converted from MasterCook’s XMLish format to MD, so having Hugo stop dead when it finds an ambiguous reference from one recipe to another is A Good Thing.

In fact, a quick find | sed | sort | uniq -c | sort -rn reveals that 1,347 of my content files on that site have non-unique names. While it’s unlikely there are many references to corn-chowder.md, there are definitely quite a few to pizza-dough.md. I may simply be able to insert the full path into the content files when I generate them from the XMLish.

-j

1 Like

@vassudanagunta Shouldn’t it be possible for the Page version of .GetPage to do a search for a page just as .Site.GetPage does?

Like @jgreely mentioned, I also have all blog posts with unique titles. It would be beneficial to allow searches and return the page object if the searched name is unique. That way, the user does not have to type the full path.

That’s exactly the feature I like. I would want to link to a “my-awesome-post-on-foobar” post. I don’t care to know if that post is living in /posts or /notes or /anything. And I’m happy that {{< relref .. >}} and {{< ref .. >}} work that way.

@kaushalmodi It is possible and that is what it does. The only difference is that if you have two or more files named my-awesome-post-on-foobar and you use that simple name as a ref you will get an ambiguity error. That’s a good thing. You can resolve the ambiguity by switching to an unambiguous ref in those cases, or rename one of the dupes.

It’s all there in the PR linked above as well as in the forum discussion that you saw a couple of months ago :slight_smile:

And I’m happy that {{< relref … >}} and {{< ref … >}} work that way.

Part of my design was that ref, relref and GetPage all have identical semantics.

Yes. I completely agree with that error if ambiguity is there. I was concerned about the {{ with .GetPage "004777.md" }} code that the OP reported as not working even if there is one and only one 004777.md from that level (/post) down, albeit in one of the sub-directories, if not directly in the current directory. … or may be I misunderstood the problem and joined the conversation incorrectly.

Update: Added the part in bold above.