Пример работы с множествами в MongoDB. В качестве множеств используем массивы. Извлечение данных реализуем на Aggregation Pipeline, в частности с помощью выражений для работы с множествами. По ходу создадим структуру данных, имитирующую взаимоотношения между полами. Будет интересно.
Для начала создадим файл по имени app.js следующего содержания:
В самом начале файла закомментированы все движения которые мы будет предпринимать в процессе изложения, последовательно.
Запускаем mongo Shell. Загружаем созданный выше файл app.js, заполняем коллекцию по имени sets тестовыми данными:
Получаем все документы коллекциии sets:
Что мы имеем: три парня - Майк, Ник, Алекс, три девушки: Мэри, Люси, Анна.
Схематично взаимоотношения нашей тестовой публики можно изобразить примерно так:
Майк у нас плейбой:
Мэри тоже не особенно старается упорядочить свои связи:
У остальных тоже все непросто, в общем все как у людей :).
Найдем всех у кого есть пара:
Как мы с вами уже успели заметить выше, у Майка и Мэри с этим все более чем в порядке :).
Для того чтобы убедиться в этом еще раз предлагаю выяснить у кого больше одного партнера:
Знакомые все лица :).
Теперь посмотрим как обстоят дела у наших плейбоя и ... нетрудной девушки между собой:
Было бы странно если бы было иначе :).
А как дела у Мэри с мужским полом вообще:
Ее любят Майк и Ник.
Теперь предлагаю подтвердить это, ответив на вопрос что общего у Майка и Ника:
Действительно, они оба любят Мэри, может быть даже одновременно :).
А что общего у Мэри и Люси:
Оказывается у них целых две общих страсти: обе встречаются с Майком, но мечтают об Алексе, представим себе что он успешный коммерс и у него куча бабла :).
Теперь убедимся в том, что Майк действительно любит Мэри и Люси:
Все как надо. Переходим к экшену :).
Попытаемся отсортировать девушек, которых любит Майк в порядке убывания "любви":
В настоящий момент у нас нет предмета сортировки, т.к. Майк еще ни разу не "полюбил" ни одну из девушек, поэтому документы отсортированы в порядке их хранения на жестком диске.
Позволим ему сделать это пару раз с Мэри и один раз с Люси:
Получаем сортированный список любимых девушек Майка еще раз:
Теперь все по фэн-шую.
И на десерт - графы. Построим социальные графы Майка и Мэри:
Вся публика в сборе. Добавим в нашу компанию еще одного персонажа по имени Джейк:
Построим социальные графы Майка и Мэри еще раз:
И ничего не случилось, т.к. Джейк пока еще не успел завязать отношения ни с одним из героев нашего повествования.
Познакомим Джейка с Анной, допустим в результате он запал не нее:
Построим социальные графы Майка и Мэри еще раз:
Bingo, Джейк появился как в графе Майка так и в графе Мэри, хоть и через тридцать три... в общем спалился :).
Вот такая история. Более откровенные варианты развития событий изобретаем самостоятельно :).
Для начала создадим файл по имени app.js следующего содержания:
// load(app.js), populate(), find(),
// findByName('Mike'), findByName('Mary')
// findByRelation(), findByRelation('love', 1)
// findRelations({ _id: "Mike" }, "Mary"), findRelations({ sex: true }, "Mary")
// findCommon('Mike', 'Nick'), findCommon('Mary', 'Lucy')
// find({love: 'Mike'})
// findByRate('Mike', 'love'})
// fck('Mike', 'Mary'), fck('Mike', 'Mary'), fck('Mike', 'Lucy')
// findByRate('Mike', 'love'})
// buildGraph('Mike'), buildGraph('Mary')
// create('Jake')
// buildGraph('Mike'), buildGraph('Mary')
// likes('Jake', 'Anna')
// buildGraph('Mike'), buildGraph('Mary')
var setsCollection = 'sets';
var populate = function() {
db[setsCollection].remove({});
return db[setsCollection].insert([
{"_id": "Mike", "sex": true, "love": ["Mary", "Lucy"], "likes": ["Anna"]},
{"_id": "Nick", "sex": true, "love": ["Mary"], "likes": ["Lucy"], "liked": ["Anna"]},
{"_id": "Alex", "sex": true, "likes": ["Anna"], "liked": ["Mary", "Lucy"]},
{"_id": "Mary", "sex": false, "love": ["Mike", "Nick"], "likes": ["Alex"]},
{"_id": "Lucy", "sex": false, "love": ["Mike"], "likes": ["Alex"], "liked": ["Nick"]},
{"_id": "Anna", "sex": false, "likes": ["Nick"], "liked": ["Mike", "Alex"]}
]);
};
var find = function(query) {
return db[setsCollection].find(query || {}).sort({_id: 1});
};
var findByName = function(name) {
return db[setsCollection].findOne({_id: name});
};
// common relations, num > 0 - playboy || whore
var findByRelation = function(name, num) {
var arr = [{$match: {}}, {$project: {}}, {$match: {}}, {$sort: {_id: -1}}];
name = name || 'love';
num = num || 0;
arr[0].$match[name] = {$exists: true};
arr[1].$project[name] = {$size: '$' + name};
arr[2].$match[name] = {$gt: num};
return db[setsCollection].aggregate(arr);
};
// match's ({ _id: "Mike" } || { sex: true }) relations with project ("Mary")
var findRelations = function(match, project) {
return db[setsCollection].aggregate([
{$match: match || {}},
{$project: {
love: {$setIntersection: [ "$love", [project] ]},
likes: {$setIntersection: [ "$likes", [project] ]},
liked: {$setIntersection: [ "$liked", [project] ]}
}},
{$project: {
love: { $cond: [ {$and: [{$ne: ["$love", null]}, {$size: "$love"}]} , true, false ] },
likes: { $cond: [ {$and: [{$ne: ["$likes", null]}, {$size: "$likes"}]} , true, false ] },
liked: { $cond: [ {$and: [{$ne: ["$liked", null]}, {$size: "$liked"}]} , true, false ] }
}},
{$sort: {_id: 1}}
]);
};
// common relations of first ("Mike") and second ("Nick")
var findCommon = function(first, second) {
return db[setsCollection].aggregate([
{$match: { _id: {$in: [first, second] }}},
{$group: {
_id: null,
alove: { $first: "$love" }, blove: {$last: "$love"},
alikes: { $first: "$likes" }, blikes: {$last: "$likes"},
aliked: { $first: "$liked" }, bliked: {$last: "$liked"}
}},
{$project: {
love: { $setIntersection: ["$alove", "$blove"] },
likes: { $setIntersection: ["$alikes", "$blikes"] },
liked: { $setIntersection: ["$aliked", "$bliked"] }
}}
]);
};
var fck = function(first, second) {
var bulk = db[setsCollection].initializeUnorderedBulkOp(),
first_update = {$inc: {}},
second_update = {$inc: {}};
first_update.$inc['rate.' + second] = second_update.$inc['rate.' + first] = 1;
bulk.find({_id: first, love: second}).update(first_update);
bulk.find({_id: second, love: first}).update(second_update);
return bulk.execute();
};
var findByRate = function(rate, connection) {
var query = {}, sort = {};
if (rate) {
sort['rate.' + rate] = -1;
if (connection) query[connection] = rate;
}
return db[setsCollection].find(query || {}).sort(sort);
};
var buildGraph = function(name) {
return {
_id: name,
graph: build(name, Array.isArray(name) ? name : [name])
};
function build(arr, diff, found) {
found = found || [];
var aggr = [
{$match: {}},
{$project: {
graph: {
$setDifference: [
{$setUnion: [
{$ifNull: ["$love", []]},
{$ifNull: ["$likes", []]},
{$ifNull: ["$liked", []]}
]},
diff
]
}
}}
];
if (Array.isArray(arr)) {
aggr[0].$match._id = {$in: arr};
aggr[1].$project.graph.$setDifference[0].$setUnion.unshift(found);
aggr[2] = {$unwind: "$graph"};
aggr[3] = {$group: {_id: null, graph: {$addToSet: "$graph"}}};
} else {
aggr[0].$match._id = arr;
}
var cursor = db[setsCollection].aggregate(aggr);
if (!cursor.hasNext()) return found;
var obj = cursor.next();
if (!obj.graph.length) return found;
return build(obj.graph, diff.concat(obj.graph), found.concat(obj.graph));
}
};
var create = function(name, sex) {
return db[setsCollection].insert({_id: name, sex: sex || true});
};
var likes = function(first, second) {
var arr = getRelations(first, second);
if (arr.length < 2) return printjson({error: 'Invalid params'});
var first_rel = arr[0], second_rel = arr[1];
if (first_rel.love || second_rel.love) return printjson({error: 'Already in love'});
if (first_rel.likes || second_rel.liked) return printjson({error: 'Already likes'});
var bulk = db[setsCollection].initializeUnorderedBulkOp(),
first_update = {},
second_update = {};
if (first_rel.liked || second_rel.likes) { // love
first_update.$pull = {liked: second};
first_update.$push = {love: second};
second_update.$pull = {likes: first};
second_update.$push = {love: first};
} else {
first_update.$push = {likes: second};
second_update.$push = {liked: first};
}
bulk.find({_id: first}).update(first_update);
bulk.find({_id: second}).update(second_update);
return bulk.execute();
};
function getRelations(first, second) {
var arr = [];
var relations = db[setsCollection].aggregate([
{$match: {_id: {$in: [first, second]}}},
{$project: {
love: {$setIntersection: [ "$love", {$cond: [{$eq: ["$_id", first]}, [second], [first] ]} ]},
likes: {$setIntersection: [ "$likes", {$cond: [{$eq: ["$_id", first]}, [second], [first] ]} ]},
liked: {$setIntersection: [ "$liked", {$cond: [{$eq: ["$_id", first]}, [second], [first] ]} ]}
}},
{$project: {
love: { $cond: [ {$and: [{$ne: ["$love", null]}, {$size: "$love"}]}, true, false ] },
likes: { $cond: [ {$and: [{$ne: ["$likes", null]}, {$size: "$likes"}]}, true, false ] },
liked: { $cond: [ {$and: [{$ne: ["$liked", null]}, {$size: "$liked"}]}, true, false ] }
}}
]);
while (relations.hasNext()) {
arr.push(relations.next());
}
return arr;
}
В самом начале файла закомментированы все движения которые мы будет предпринимать в процессе изложения, последовательно.
Запускаем mongo Shell. Загружаем созданный выше файл app.js, заполняем коллекцию по имени sets тестовыми данными:
Получаем все документы коллекциии sets:
Что мы имеем: три парня - Майк, Ник, Алекс, три девушки: Мэри, Люси, Анна.
{ { {
"_id": "Mike", "_id": "Nick", "_id": "Alex",
"love": ["Mary", "Lucy"], "love": ["Mary"],
"likes": ["Anna"] "likes": ["Lucy"], "likes": ["Anna"],
"liked": ["Anna"] "liked": ["Mary", "Lucy"]
} } }
{ { {
"_id": "Mary", "_id": "Lucy", "_id": "Anna",
"love": ["Mike", "Nick"], "love": ["Mike"],
"likes": ["Alex"] "likes": ["Alex"], "likes": ["Nick"],
"liked": ["Nick"] "liked": ["Mike", "Alex"]
} } }
Схематично взаимоотношения нашей тестовой публики можно изобразить примерно так:
Майк у нас плейбой:
Мэри тоже не особенно старается упорядочить свои связи:
У остальных тоже все непросто, в общем все как у людей :).
Найдем всех у кого есть пара:
Как мы с вами уже успели заметить выше, у Майка и Мэри с этим все более чем в порядке :).
Для того чтобы убедиться в этом еще раз предлагаю выяснить у кого больше одного партнера:
Знакомые все лица :).
Теперь посмотрим как обстоят дела у наших плейбоя и ... нетрудной девушки между собой:
Было бы странно если бы было иначе :).
А как дела у Мэри с мужским полом вообще:
Ее любят Майк и Ник.
Теперь предлагаю подтвердить это, ответив на вопрос что общего у Майка и Ника:
Действительно, они оба любят Мэри, может быть даже одновременно :).
А что общего у Мэри и Люси:
Оказывается у них целых две общих страсти: обе встречаются с Майком, но мечтают об Алексе, представим себе что он успешный коммерс и у него куча бабла :).
Теперь убедимся в том, что Майк действительно любит Мэри и Люси:
Все как надо. Переходим к экшену :).
Попытаемся отсортировать девушек, которых любит Майк в порядке убывания "любви":
В настоящий момент у нас нет предмета сортировки, т.к. Майк еще ни разу не "полюбил" ни одну из девушек, поэтому документы отсортированы в порядке их хранения на жестком диске.
Позволим ему сделать это пару раз с Мэри и один раз с Люси:
Получаем сортированный список любимых девушек Майка еще раз:
Теперь все по фэн-шую.
И на десерт - графы. Построим социальные графы Майка и Мэри:
Вся публика в сборе. Добавим в нашу компанию еще одного персонажа по имени Джейк:
Построим социальные графы Майка и Мэри еще раз:
И ничего не случилось, т.к. Джейк пока еще не успел завязать отношения ни с одним из героев нашего повествования.
Познакомим Джейка с Анной, допустим в результате он запал не нее:
Построим социальные графы Майка и Мэри еще раз:
Bingo, Джейк появился как в графе Майка так и в графе Мэри, хоть и через тридцать три... в общем спалился :).
Вот такая история. Более откровенные варианты развития событий изобретаем самостоятельно :).





















Комментариев нет:
Отправить комментарий
Комментарий будет опубликован после модерации