Несмотря на то, что вы не можете делать это в режиме реального времени, вы можете запускать несколько сокращений карт для объединения данных вместе с помощью опции «уменьшить» в MongoDB 1.8+ map / reduce (см. http: // www.mongodb.org/display/DOCS/MapReduce#MapReduce-Outputoptions). Вам нужно иметь некоторый ключ в обеих коллекциях, который вы можете использовать в качестве _id.
Например, предположим, что у вас есть коллекция users
и comments
, и вы хотите иметь новую коллекцию который имеет некоторую демографическую информацию пользователя для каждого комментария.
Предположим, что коллекция users
имеет следующие поля:
И тогда коллекция comments
имеет следующие поля:
Вы сделал бы это map / reduce:
var mapUsers, mapComments, reduce;
db.users_comments.remove();
// setup sample data - wouldn't actually use this in production
db.users.remove();
db.comments.remove();
db.users.save({firstName:"Rich",lastName:"S",gender:"M",country:"CA",age:"18"});
db.users.save({firstName:"Rob",lastName:"M",gender:"M",country:"US",age:"25"});
db.users.save({firstName:"Sarah",lastName:"T",gender:"F",country:"US",age:"13"});
var users = db.users.find();
db.comments.save({userId: users[0]._id, "comment": "Hey, what's up?", created: new ISODate()});
db.comments.save({userId: users[1]._id, "comment": "Not much", created: new ISODate()});
db.comments.save({userId: users[0]._id, "comment": "Cool", created: new ISODate()});
// end sample data setup
mapUsers = function() {
var values = {
country: this.country,
gender: this.gender,
age: this.age
};
emit(this._id, values);
};
mapComments = function() {
var values = {
commentId: this._id,
comment: this.comment,
created: this.created
};
emit(this.userId, values);
};
reduce = function(k, values) {
var result = {}, commentFields = {
"commentId": '',
"comment": '',
"created": ''
};
values.forEach(function(value) {
var field;
if ("comment" in value) {
if (!("comments" in result)) {
result.comments = [];
}
result.comments.push(value);
} else if ("comments" in value) {
if (!("comments" in result)) {
result.comments = [];
}
result.comments.push.apply(result.comments, value.comments);
}
for (field in value) {
if (value.hasOwnProperty(field) && !(field in commentFields)) {
result[field] = value[field];
}
}
});
return result;
};
db.users.mapReduce(mapUsers, reduce, {"out": {"reduce": "users_comments"}});
db.comments.mapReduce(mapComments, reduce, {"out": {"reduce": "users_comments"}});
db.users_comments.find().pretty(); // see the resulting collection
На этом этапе у вас будет новая коллекция под названием users_comments
, которая содержит объединенные данные, и теперь вы можете ее использовать. Эти уменьшенные коллекции имеют _id
, который является ключом, который вы излучали в ваших функциях карты, а затем все значения являются под-объектом внутри клавиши value
- значения не находятся на верхнем уровне этих сокращенных документов .
Это несколько простой пример. Вы можете повторить это с большим количеством коллекций столько, сколько хотите продолжать наращивать уменьшенную коллекцию. Вы также можете делать сводки и агрегации данных в процессе. Вероятно, вы бы определили более одной функции сокращения, так как логика для агрегирования и сохранения существующих полей становится более сложной.
Вы также заметите, что теперь есть один документ для каждого пользователя со всеми комментариями этого пользователя в массив. Если бы мы объединили данные, которые имеют отношения «один к одному», а не «один ко многим», это было бы неплохо, и вы могли бы просто использовать функцию уменьшения следующим образом:
reduce = function(k, values) {
var result = {};
values.forEach(function(value) {
var field;
for (field in value) {
if (value.hasOwnProperty(field)) {
result[field] = value[field];
}
}
});
return result;
};
Если вы хотите чтобы сгладить коллекцию users_comments
, так что это один документ за комментарий, дополнительно выполните это:
var map, reduce;
map = function() {
var debug = function(value) {
var field;
for (field in value) {
print(field + ": " + value[field]);
}
};
debug(this);
var that = this;
if ("comments" in this.value) {
this.value.comments.forEach(function(value) {
emit(value.commentId, {
userId: that._id,
country: that.value.country,
age: that.value.age,
comment: value.comment,
created: value.created,
});
});
}
};
reduce = function(k, values) {
var result = {};
values.forEach(function(value) {
var field;
for (field in value) {
if (value.hasOwnProperty(field)) {
result[field] = value[field];
}
}
});
return result;
};
db.users_comments.mapReduce(map, reduce, {"out": "comments_with_demographics"});
Этот метод определенно не должен выполняться «на лету». Он подходит для задания cron или что-то вроде этого, которое периодически обновляет объединенные данные. Вероятно, вы захотите запустить ensureIndex
в новой коллекции, чтобы убедиться, что запросы, которые вы выполняете против этого, выполняются быстро (помните, что ваши данные все еще находятся внутри клавиши value
, поэтому, если вы хотите индексировать comments_with_demographics
на время комментария created
, это будет db.comments_with_demographics.ensureIndex({"value.created": 1});
Вы можете jest.mock
Keyframes
. Я не уверен, откуда вы импортируете его, но если вы смоделируете все до функции загрузки и передадите шпионскую функцию next
, вы можете подтвердить передачу цветов в next
.
Анимации сложно проверить, и тщательное тестирование потребует много насмешек. Это не могло быть для этого.