Использование карты / уменьшить для сопоставления свойств в коллекции - PullRequest
25 голосов
/ 08 июня 2010

Обновление: продолжение до MongoDB Получить имена всех ключей в коллекции .

Как указывает Кристина , можно использовать карту Монгодба / уменьшить, чтобы перечислить ключи в коллекции:

db.things.insert( { type : ['dog', 'cat'] } );
db.things.insert( { egg : ['cat'] } );
db.things.insert( { type :  [] }); 
db.things.insert( { hello : []  } );

mr = db.runCommand({"mapreduce" : "things",
"map" : function() {
    for (var key in this) { emit(key, null); }
},  
"reduce" : function(key, stuff) { 
   return null;
}}) 

db[mr.result].distinct("_id")

//output: [ "_id", "egg", "hello", "type" ]

Пока мы хотим получить только ключи, расположенные на первом уровне глубины, это прекрасно работает. Однако он не сможет получить те ключи, которые находятся на более глубоких уровнях. Если мы добавим новую запись:

db.things.insert({foo: {bar: {baaar: true}}})

И мы снова запустим приведенный выше фрагмент карты + уменьшение +, мы получим:

[ "_id", "egg", "foo", "hello", "type" ] 

Но мы не получим ключи bar и baaar , которые вложены в структуру данных. Вопрос в том, как мне получить все ключи, независимо от их уровня глубины. В идеале, я бы хотел, чтобы скрипт шел на все уровни глубины, создавая вывод, такой как:

["_id","egg","foo","foo.bar","foo.bar.baaar","hello","type"]      

Заранее спасибо!

Ответы [ 3 ]

26 голосов
/ 10 июня 2010

ОК, это немного сложнее, потому что вам нужно использовать некоторую рекурсию.

Чтобы рекурсия состоялась, вам нужно иметь возможность хранить некоторые функции на сервере.

Шаг 1: определите некоторые функции и разместите их на стороне сервера

isArray = function (v) {
  return v && typeof v === 'object' && typeof v.length === 'number' && !(v.propertyIsEnumerable('length'));
}

m_sub = function(base, value){
  for(var key in value) {
    emit(base + "." + key, null);
    if( isArray(value[key]) || typeof value[key] == 'object'){
      m_sub(base + "." + key, value[key]);
    }
  }
}

db.system.js.save( { _id : "isArray", value : isArray } );
db.system.js.save( { _id : "m_sub", value : m_sub } );

Шаг 2: определить карту и сократить функции

map = function(){
  for(var key in this) {
    emit(key, null);
    if( isArray(this[key]) || typeof this[key] == 'object'){
      m_sub(key, this[key]);
    }
  }
}

reduce = function(key, stuff){ return null; }

Шаг 3: запустите карту и посмотрите на результаты

mr = db.runCommand({"mapreduce" : "things", "map" : map, "reduce" : reduce,"out": "things" + "_keys"});
db[mr.result].distinct("_id");

Результаты, которые вы получите:

["_id", "_id.isObjectId", "_id.str", "_id.tojson", "egg", "egg.0", "foo", "foo.bar", "foo.bar.baaaar", "hello", "type", "type.0", "type.1"]

Здесь есть одна очевидная проблема, мы добавляем несколько неожиданных полей: 1. _id данные 2. .0 (на яйце и типа)

Шаг 4: Некоторые возможные исправления

Для задачи # 1 решение относительно просто. Просто измените функцию map. Изменить это:

emit(base + "." + key, null); if( isArray...

к этому:

if(key != "_id") { emit(base + "." + key, null); if( isArray... }

Задача № 2 немного сложнее. Вы хотели все ключи и технически "egg.0" является действительным ключом. Вы можете изменить m_sub, чтобы игнорировать такие цифровые клавиши. Но также легко увидеть ситуацию, когда это имеет неприятные последствия. Скажем, у вас есть ассоциативный массив внутри обычного массива, затем вы хотите, чтобы появилось «0». Я оставлю вам остальное от этого решения.

7 голосов
/ 28 апреля 2012

Вдохновляясь ответами Gates VP и Kristina, я создал инструмент с открытым исходным кодом под названием Variety, который делает именно это: https://github.com/variety/variety

Надеюсь, вы найдете это полезным. Дайте мне знать, если у вас есть вопросы или какие-либо проблемы с их использованием.

0 голосов
/ 02 июля 2018

как простая функция;

const getProps = (db, collection) => new Promise((resolve, reject) => {
  db
  .collection(collection)
  .mapReduce(function() {
    for (var key in this) { emit(key, null) }
  }, (prev, next) => null, {
    out: collection + '_keys'
  }, (err, collection_props) => {
    if (err) reject(err)

    collection_props
    .find()
    .toArray()
    .then(
      props => resolve(props.map(({_id}) => _id))
    )
  })
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...