public function lookup(array $keys, array $options = [])
    {
        $options += [
            'className' => Entity::class,
            'sort' => false
        ];

        $serviceKeys = [];
        $this->validateBatch($keys, Key::class, function ($key) use (&$serviceKeys) {
            if ($key->state() !== Key::STATE_NAMED) {
                throw new \InvalidArgumentException(sprintf(
                    'Given $key is in an invalid state. Can only lookup records when given a complete key. ' .
                    'Given path was %s',
                    (string) $key
                ));
            }

            $serviceKeys[] = $key->keyObject();
        });

        $res = $this->connection->lookup($options + $this->readOptions($options) + [
            'projectId' => $this->projectId,
            'keys' => $serviceKeys
        ]);

        $result = [];
        if (isset($res['found'])) {
            foreach ($res['found'] as $found) {
                $result['found'][] = $this->mapEntityResult($found, $options['className']);
            }

            if ($options['sort']) {
                $result['found'] = $this->sortEntities($result['found'], $keys);
            }
        }

        if (isset($res['missing'])) {
            $result['missing'] = [];
            foreach ($res['missing'] as $missing) {
                $key = $this->key(
                    $missing['entity']['key']['path'],
                    $missing['entity']['key']['partitionId']
                );

                $result['missing'][] = $key;
            }
        }

        if (isset($res['deferred'])) {
            $result['deferred'] = [];
            foreach ($res['deferred'] as $deferred) {
                $key = $this->key(
                    $deferred['path'],
                    $deferred['partitionId']
                );

                $result['deferred'][] = $key;
            }
        }

        return $result;
    }