Пример работы с множествами в 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, Джейк появился как в графе Майка так и в графе Мэри, хоть и через тридцать три... в общем спалился :).
Вот такая история. Более откровенные варианты развития событий изобретаем самостоятельно :).
Комментариев нет:
Отправить комментарий
Комментарий будет опубликован после модерации