Почему курсор MongoDB может быть проиндексирован так, как если бы он был массивом?

#javascript #arrays #mongodb

#javascript #массивы #mongodb

Вопрос:

Я заметил, что если я выполняю скрипт JavaScript с помощью mongo команды, скрипт может обрабатывать объект cursor, как если бы это был массив.

 var conn = new Mongo('localhost:27017');
var db = conn.getDB('learn');
db.test.remove({});
db.test.insert({foo: 'bar'});
var cur = db.test.find();
print(cur[0].foo);   //prints: bar
print(cur[1]); // prints: undefined
 

Похоже, что это должно быть за пределами возможностей языка JavaScript, поскольку нет способа «перегрузить оператор подстрочного индекса». Итак, как это на самом деле работает?

Комментарии:

1. К каждому документу можно получить доступ по его индексу … если вы обращаетесь к большему количеству документов, он возвращает «undefined»

2. Он не может манипулировать оператором, но он может манипулировать индексами. Поскольку они представляют собой всего лишь подмножество свойств, их можно определить как получатели и / или установщики . Object.defineProperty(cursor, '0', { get: ... });

3. @JonathanLonowski Но без «отсутствия метода» пришлось бы определять оператор для каждого индекса, что здесь не представляется практичным..

4. @user2864740 Нет, вероятно, это непрактично при доступе к машинному коду, как упоминал dt0xff. Но в основном я указывал на то, что можно определить объект, подобный массиву, без перегрузки оператора.

5. @JonathanLonowski, это не тот случай. Просто посмотрите на класс курсора github.com/mongodb/mongo/blob/master/src/mongo/shell/query.js , здесь у нас нет логики, подобной этой, и это то, что я проверил в первую очередь, но для монго будет глупо делать это, да. Но это может быть удобно в некоторых случаях, если вы пытаетесь эмулировать это поведение.

Ответ №1:

Как говорится в документации, это особая способность драйвера. Он автоматически преобразуется cursor[0] в cursor.toArray()[0] . Вы можете доказать это, переопределив toArray() функцию print или new Error().stack вернув callstack обратно. Вот оно:

 at DBQuery.a.toArray ((shell):1:32)
at DBQuery.arrayAccess (src/mongo/shell/query.js:290:17)
at (shell):1:2
 

Как вы можете видеть, индексирование вызывает arrayAccess . Как? Здесь у нас есть dbQueryIndexAccess функция, которая вызывает arrayAccess .

 v8::Handle<v8::Value> arrayAccess = info.This()->GetPrototype()->ToObject()->Get(
                v8::String::New("arrayAccess"));
...
v8::Handle<v8::Function> f = arrayAccess.As<v8::Function>();
...
return f->Call(info.This(), 1, argv);
 

И здесь у нас есть код, который устанавливает обработчик индексированного свойства для этой функции. ВАУ, API версии 8 дает нам возможность добавлять этот обработчик!

 DBQueryFT()->InstanceTemplate()->SetIndexedPropertyHandler(dbQueryIndexAccess);
 

… и вводит его в класс курсора JS, который изначально определен в JS.

 injectV8Function("DBQuery", DBQueryFT(), _global);
 

Tl; dr: он взломан в исходном коде C оболочки mongo.