protected function findRawNodesByPath($path, Workspace $workspace, array $dimensions = null, $onlyShadowNodes = false)
    {
        $path = strtolower($path);
        if ($path === '' || ($path !== '/' && ($path[0] !== '/' || substr($path, -1, 1) === '/'))) {
            throw new \InvalidArgumentException('"' . $path . '" is not a valid path: must start but not end with a slash.', 1284985489);
        }

        if ($path === '/') {
            return [$workspace->getRootNodeData()];
        }

        $addedNodes = [];
        $workspaces = [];
        while ($workspace !== null) {
            /** @var $node NodeData */
            foreach ($this->addedNodes as $node) {
                if (($node->getPath() === $path && $node->matchesWorkspaceAndDimensions($workspace, $dimensions)) && ($onlyShadowNodes === false || $node->isInternal())) {
                    $addedNodes[] = $node;
                    // removed nodes don't matter here because due to the identity map the right object will be returned from the query and will have "removed" set.
                }
            }

            $workspaces[] = $workspace;
            $workspace = $workspace->getBaseWorkspace();
        }
        $queryBuilder = $this->createQueryBuilder($workspaces);
        if ($dimensions !== null) {
            $this->addDimensionJoinConstraintsToQueryBuilder($queryBuilder, $dimensions);
        }
        $this->addPathConstraintToQueryBuilder($queryBuilder, $path);
        if ($onlyShadowNodes) {
            $queryBuilder->andWhere('n.movedTo IS NOT NULL AND n.removed = TRUE');
        }

        $query = $queryBuilder->getQuery();
        $nodes = $query->getResult();
        return array_merge($nodes, $addedNodes);
    }