function buildGetCollectionResponse(aMatches, oResponse) {
		const mQueryOptions = getQueryOptions(aMatches[1]);
		function getExpandLevels() {
			if (mQueryOptions.$apply.includes("ExpandLevels")) {
				let sExpandLevels = mQueryOptions.$apply.match(/,ExpandLevels=(.+)\)/)[1];
				sExpandLevels = decodeURIComponent(sExpandLevels);
				return new Map(JSON.parse(sExpandLevels).map(
					(o) => [o.NodeID, o.Levels ?? Number.MAX_SAFE_INTEGER]));
			}
		}

		if ("$apply" in mQueryOptions) {
			if (mQueryOptions.$apply.includes("TopLevels")) {
				// "EMPLOYEES?$apply=orderby(AGE)"
				// + "/com.sap.vocabularies.Hierarchy.v1.TopLevels(HierarchyNodes=$root/EMPLOYEES"
				// + ",HierarchyQualifier='OrgChart',NodeProperty='ID',Levels=" + iLevels + ")"
				// Note: "Levels" is optional!
				const iLevels = mQueryOptions.$apply.includes(",Levels=")
					? parseInt(mQueryOptions.$apply.match(/,Levels=(\d+)/)[1])
					: Infinity;
				let aRows = topLevels(iLevels - 1, getExpandLevels()); // Note: already cloned
				if ("$filter" in mQueryOptions) {
					if (mQueryOptions.$filter.includes("LimitedRank")) {
						// e.g. LimitedRank lt 5 and DistanceFromRoot lt 2
						// e.g. LimitedRank gt 4 and DistanceFromRoot lt 2
						const aFilterMatches = mQueryOptions.$filter.match(
							/LimitedRank%20(gt|lt)%20(\d+)%20and%20DistanceFromRoot%20lt%20(\d+)/);
						const bGreater = aFilterMatches[1] === "gt";
						const iLimitedRank = parseInt(aFilterMatches[2]);
						const iDistanceFromRoot = parseInt(aFilterMatches[3]);
						aRows = aRows.filter((oNode, i) => {
							oNode.LimitedRank = "" + i; // Edm.Int64
							return (bGreater ? i > iLimitedRank : i < iLimitedRank)
								&& oNode.DistanceFromRoot < iDistanceFromRoot;
						});
						if (mQueryOptions.$orderby === "LimitedRank%20desc") {
							aRows = aRows.reverse();
						}
					} else { // ID%20eq%20'1'
						const aIDs = mQueryOptions.$filter.split("%20or%20").map(
							(sID_Predicate) => sID_Predicate.split("%20eq%20")[1].slice(1, -1));
						aRows = aRows.filter((oNode, i) => {
							oNode.LimitedRank = "" + i; // Edm.Int64
							return aIDs.includes(oNode.ID);
						});
					}
				}
				selectCountSkipTop(aRows, mQueryOptions, oResponse);
				return;
			}

			if (mQueryOptions.$apply.includes("descendants")) {
				// "EMPLOYEES?$apply=ancestors($root/EMPLOYEES,OrgChart,ID
				// + ",filter(STATUS%20ne%20'Out'),keep%20start)"
				// + "/descendants($root/EMPLOYEES,OrgChart,ID,filter(ID%20eq%20'" + sParentId
				// + "'),1)/orderby(AGE)"
				let aDescendants = [];
				const addDescendants = (sParentId, bAll) => {
					mChildrenByParentId[sParentId]?.forEach((oChild) => {
						aDescendants.push(oChild);
						if (bAll) {
							addDescendants(oChild.ID, true);
						}
					});
				};
				const [, sParentId, bSingle]
					= mQueryOptions.$apply.match(/,filter\(ID%20eq%20'([^']*)'\)(,1)?/);
				addDescendants(sParentId, !bSingle);
				if ("$filter" in mQueryOptions) {
					const bNot = mQueryOptions.$filter.startsWith("not%20(");
					if (bNot) {
						// not%20(ID%20eq%20'5.1.10'%20or%20ID%20eq%20'5.1.11')
						mQueryOptions.$filter
							= mQueryOptions.$filter.slice("not%20(".length, -")".length);
					}
					const aIDs = mQueryOptions.$filter
						.split("%20or%20")
						.map((sID_Predicate) => sID_Predicate.split("%20eq%20")[1].slice(1, -1));
					aDescendants
						= aDescendants.filter((oChild) => aIDs.includes(oChild.ID) !== bNot);
				}
				selectCountSkipTop(aDescendants, mQueryOptions, oResponse);
				return;
			}

			// "$orderby=AGE&$select=ID,MANAGER_ID,Name,AGE"
			// + "&$apply=ancestors($root/EMPLOYEES,OrgChart,ID,filter(ID%20eq%20'3'),1)"
			const sChildId = mQueryOptions.$apply.match(/,filter\(ID%20eq%20'([^']*)'\)/)[1];
			const sParentId = mNodeById[sChildId].MANAGER_ID;
			selectCountSkipTop([mNodeById[sParentId]], mQueryOptions, oResponse);
			return;
		}

		if ("$filter" in mQueryOptions) {
			const sFilter = mQueryOptions.$filter;
			let aRows;
			if (sFilter.startsWith("contains(")) {
				// contains(ID,'5')%20or%20contains(Name,'5')
				const sValue = sFilter.split("%20or%20")[0].slice(sFilter.indexOf("'") + 1, -2);
				aRows = Object.values(mNodeById).filter((oNode) => {
					return oNode.ID.includes(sValue) || oNode.Name.includes(sValue);
				});
			} else {
				// ID%20eq%20'0'%20or%20ID%20eq%20'1'%20or%20ID%20eq%20'1.1'
				const aIDs = sFilter.split("%20or%20")
					.map((sID_Predicate) => sID_Predicate.split("%20eq%20")[1].slice(1, -1));
				if (aIDs.length > 1 || aIDs[0] === "0") {
					// side effect for all rows or just the root
					iRevision += 1;
				}
				aRows = aIDs.map((sId) => mNodeById[sId]);
			}
			selectCountSkipTop(aRows, mQueryOptions, oResponse);
			return;
		}

		selectCountSkipTop(aAllNodes, mQueryOptions, oResponse);
	}
Builds a response for any GET query on the "EMPLOYEES" collection.

@param {string[]} aMatches - The matches against the RegExp
@param {object} oResponse - Response object to fill