firebaser here
The problem is almost certainly that your query has range checks on two different fields (name and the document path), which isn't possible in Firestore's query model. As the documentation on query limitations says:
In a compound query, range (<, <=, >, >=) and not equals (!=, not-in) comparisons must all filter on the same field.
Your startAt and endAt clauses are just different ways of writing > and < as far as this limitation is concerned.
To understand why the SDK allow you to write this query, but doesn't give you the result you want, we'll have to dive a bit deeper into it, so... 
What is possible, is to pass all relevant fields to startAt and endAt so that it can determine the correct slice across all those field values.
Doing that would also remove the need to even have the where, so it'd be:
firebase.firestore().collectionGroup('attractions')
  .orderBy('name')
  .orderBy(firebase.firestore.FieldPath.documentId())
  .startAt(keywords, cityRef.path),
  .endAt(keywords + '\uf8ff', cityRef.path + "\uf8ff")
  .get()
  ...
But this query now first looks for the documents starting at keywords and then if necessary for cityRef.path in there to disambiguate between multiple results.
What you want is the equivalent of this query:
const docId = firebase.firestore.FieldPath.documentId()l
firebase.firestore().collectionGroup('attractions')
  .orderBy('name')
  .where('name', '>=', keywords),
  .where('name', '<=', keywords + '\uf8ff')
  .orderBy(firebase.firestore.FieldPath.documentId())
  .where(docId, '>=', cityRef.path),
  .where(docId, '<=', cityRef.path + '\uf8ff')
Now it's immediately clear why this isn't possible, because we have range conditions on two fields.
I've been trying to get that working in this jsbin (https://jsbin.com/yiyifin/edit?js,console), so far without success, but I'll post back if I get it working or have a final verdict on why it doesn't work.