MongoDB Union All? - mongodb

I am trying to union 3 collections in one but can't make it work (same as union all). As an example, I have 3 collections:
1:active users
[{_id:'', client_id:'',created:''}]
2:not active users
[{_id:'', visitor_id:'',created:''}]
3:blocked users
[{_id:'', blocked_id:'',created:''}]
what i am trying to do is to combine all 3 collections in one, sort them by date and make sure i get the first 10 results, something like that:
[{_id:'', client_id:'',created:''},
{_id:'', blocked_id:'',created:''},
{_id:'', visitor_id:'',created:''}]
I came up with this code:
collection('active).aggregate([
{
"$facet":{
"active":[
{"$match":{ "client_id": 5cfe83820c19ee3c50c8f323 }}
],
"not_active":[
{"$match":{"cust_id":"5cfe83820c19ee3c50c8f323" }}
],
"blocked":[
{"$match":{"blocked_id":"5cfe83820c19ee3c50c8f323" }}
]
}
},
{
"$skip":0
},
{
"$limit":10
},
{
"$sort":{
"created":-1
}
}
])
but this code return empty array:
[ { active: [], not_active: [], blocked : []} ]
What i am doing wrong?
Thanks

Finally i came up with this code that does the job:
mongo.clients.aggregate([
{$limit:1},{$project:{_id:1}},{$project:{_id:0}},
{$lookup : {from:'active',as:'active',pipeline:[{$match:{}}]}},
{$lookup : {from:'not_active',as:'not_active',pipeline:[{$match:{}}]}},
{$lookup : {from:'blocked',as:'blocked',pipeline:[{$match:{}}]}},
{$project:{Union:{$concatArrays:['$active','$not_active','$blocked']}}},
{$unwind:"$Union"},
{$replaceRoot:{newRoot:"$Union"}},
{$skip:0},
{$limit:10},
{$sort:{'created': -1}}
]).then(x);
Here is more detailed answer:
enter link description here

Related

MongoDB $addToSet inside another $addToSet

I am trying to achieve the below response from query.
{
users:[
user:{
name:"",
email:[
]
},
....user2
]
}
I have something similar to below.
I will put this way. One user can have n number of devices. He may have more than one email per device. I want to group at devices. And user field should have common information for that device. As we think, name will always be same. Device specific attributes also needed like whatKindOfDevice, howManyIssuesAreThereInThatDevice, howManyCanBeAddressedByUpgrade, howManyAreRare,etc.. along with this i need to get all the emails used in that device like owner,user,associate - all emails put.
Think my document Id is not associated with single user, single device. One user can have any number of devices. One device can have n Number of documents.
$group:{
"_id":"user_device_id",
"user": {
"$addToSet": {
"name":"$name",
"deviceName":"$deviceName",
"email": {"$addToSet":{}} //Something Similar I am expecting
}
}
}
If I add email outer user it works - but it affects the response format required.
Is it possible or any other way to get it the similar response through the query?
Let's assume one user can have more than one document. In each doc, there could be same or duplicate email IDs. I am trying to get that together.
Please advise.
Sample Doc:
{
_id:ObjectId,
name:"user1",
email:"a#yahoo.com"
},
{
_id:ObjectId,
name:"user1",
email:"a#device.com"
},
..user2Doc
..user1Doc with another category, duplicate email i.e a#yahoo.com
..user2Doc with new email
..
Well, it seems like you want to get all the email for the particular user and then group all the users.
So, to achieve that you have to do consecutive grouping stages.
I used the below documents:
[
{
name:"user1",
email:"a#yahoo.com"
},
{
name:"user1",
email:"a#device.com"
},
{
name:"user2",
email:"b#yahoo.com"
},
{
name:"user1",
email:"c#device.com"
}
]
Here is the query:
db.collection.aggregate([
{
$group:{
"_id":"$name",
"emails":{
$addToSet:"$email"
},
"name":{
$first:"$name"
}
}
},
{
$group:{
"_id":null,
"users":{
$addToSet:{
"name":"$name",
"email":"$emails"
}
}
}
},
{
$project:{
"_id":0
}
}
]).pretty()
Output:
{
"users" : [
{
"name" : "user1",
"email" : [
"c#device.com",
"a#device.com",
"a#yahoo.com"
]
},
{
"name" : "user2",
"email" : [
"b#yahoo.com"
]
}
]
}
For mare about $group refer here.
Hope this will help :)

How do I use spring mongo data to query based on the position of an element in an array

I have a mongo collection "test" which contains elements like so (with the nodes array being a set and meaningful order):
"test" : {
"superiorID" : 1,
"nodes" : [
{
"subID" : 2
},
{
"subID" : 1
},
{
"subID" : 3
}
]
}
or
"test" : {
"superiorID" : 4,
"nodes" : [
{
"subID" : 2
},
{
"subID" : 1
},
{
"subID" : 3
}
]
}
I am using spring Criteria to try and build a mongo query which will return to me all elements where the 'subID' equals a user input id 'inputID' AND the 'superiorID' position is NOT before the 'inputID' (if the superior id is even in the sub ids which is not required).
So for example, if my user input was 3 I would NOT want to pull the first document but I WOULD want to pull the second document (first has a superior that exists in the nodes BEFORE the userInput node second's superior id is not equal to the user input).
I know that the $indexOfArray function exists but I don't know how to translate this to Criteria.
You can get the result you are looking for through the aggregation framework. I've made a speude query for you to show what you should be looking for. This returns
showMe = false for doc1 and showMe = true for doc2, which you could obiously match for. You do not need 2 project phases for this query, I only did that to make a working query which is also easy-ish to read. This will not be a very fast query. If you want fast queries you might want to rethink your data structure.
db.getCollection('test').aggregate([
{ "$project":
{
"superiorIndex": {"$indexOfArray" : [ "$nodes.subID","$superiorID" ]},
"inputIndex": {"$indexOfArray" : [ "$nodes.subID",3 ]},
}
},
{ "$project":
{
"showMe" :
{
$cond:
{
if: { $eq: [ "$superiorIndex", -1 ] },
then: true,
else: {$gt:[ "$superiorIndex","$inputIndex"]}
}
}
}
}
])
db.collection.find({nodes.2.subID:2}) that query will lookup 2th element subid from nodes field.

Grouping documents in pairs using mongo aggregation

I have a collection of items,
[ a, b, c, d ]
And I want to group them in pairs such as,
[ [ a, b ], [ b, c ], [ c, d ] ]
This will be used in calculating the differences between each item in the original collection, but that part is solved using several techniques such as the one in this question.
I know that this is possible with map reduce, but I want to know if it's possible with aggregation.
Edit: Here's an example,
The collection of items; each item is an actual document.
[
{ val: 1 },
{ val: 3 },
{ val: 6 },
{ val: 10 },
]
Grouped version:
[
[ { val: 1 }, { val: 3 } ],
[ { val: 3 }, { val: 6 } ],
[ { val: 6 }, { val: 10 } ]
]
The resulting collection (or aggregation result):
[
{ diff: 2 },
{ diff: 3 },
{ diff: 4 }
]
This is something that just cannot be done with the aggregation framework, and the only current MongoDB method available for this type of operation is mapReduce.
The reason being that the a aggregation framework has no way of referring to any other document in the pipeline than the present one. This actually applies to "grouping" pipeline stages as well, since even though things are grouped on a "key" you cant really deal with individual documents in the way you want to.
MapReduce on the other hand has one feature available that allows you to do what you want here, and it's not even "directly" related to aggregation. It is in fact the ability to have "globally scoped variables" across all stages. And having a "variable" to basically "store the last document" is all you need to achieve your result.
So it's quite simple code, and there is in fact no "reduction" required:
db.collection.mapReduce(
function () {
if (lastVal != null)
emit( this._id, this.val - lastVal );
lastVal = this.val;
},
function() {}, // mapper is not called
{
"scope": { "lastVal": null },
"out": { "inline": 1 }
}
)
Which gives you a result much like this:
{
"results" : [
{
"_id" : ObjectId("54a425a99b8bcd6f73e2d662"),
"value" : 2
},
{
"_id" : ObjectId("54a425a99b8bcd6f73e2d663"),
"value" : 3
},
{
"_id" : ObjectId("54a425a99b8bcd6f73e2d664"),
"value" : 4
}
],
"timeMillis" : 3,
"counts" : {
"input" : 4,
"emit" : 3,
"reduce" : 0,
"output" : 3
},
"ok" : 1
}
That's really just picking "something unique" as the emitted _id value rather than anything specific, because all this is really doing is the difference between values on differing documents.
Global variables are usually the solution to these types of "pairing" aggregations or producing "running totals". Right now the aggregation framework has no access to global variables, even though it might well be a nice this to have. The mapReduce framework has them, so it is probably fair to say that they should be available to the aggregation framework as well.
Right now they are not though, so stick with mapReduce.

Mongodb: Trying to find all documents with specific subdocument field, why is my query not working?

Here is an example of a document from the collection I am querying
meteor:PRIMARY> db.research.findOne({_id: 'Z2zzA7dx6unkzKiSn'})
{
"_id" : "Z2zzA7dx6unkzKiSn",
"_userId" : "NtE3ANq2b2PbWSEqu",
"collaborators" : [
{
"userId" : "aTPzFad8DdFXxRrX4"
}
],
"name" : "new one",
"pending" : {
"collaborators" : [ ]
}
}
I want to find all documents within this collection with either _userId: 'aTPzFad8DdFXxRrX4' or from the collaborators array, userId: 'aTPzFad8DdFXxRrX4'
So I want to look though the collection and check if the _userId field is 'aTPzFad8DdFXxRrX4'. If not then check the collaborators array on the document and check if there is an object with userId: 'aTPzFad8DdFXxRrX4'.
Here is the query I am trying to use:
db.research.find({$or: [{_userId: 'aTPzFad8DdFXxRrX4'}, {collaborators: {$in: [{userId: 'aTPzFad8DdFXxRrX4'}]}}] })
It does not find the document and gives me a syntax error. What is my issue here? Thanks
The $in operator is basically a simplified version of $or but you really only have one argument here so you should not even need it. Use dot notation instead:
db.research.find({
'$or': [
{ '_userId': 'aTPzFad8DdFXxRrX4'},
{ 'collaborators.userId': 'aTPzFad8DdFXxRrX4'}
]
})
If you need more than one value then use $in:
db.research.find({
'$or': [
{ '_userId': 'aTPzFad8DdFXxRrX4'},
{ 'collaborators.userId': {
'$in': ['aTPzFad8DdFXxRrX4','aTPzFad8DdFXxRrX5']
}}
]
})

Fetching records without specific subdocument or with specific subdocument and other conditions

I'm stuck!
Consider having these products in a products collection:
{
_id: ObjectId("53678a557e6e9e19dd4ef1c8"),
name: "product 1",
rules: [
{
type: "one_purchase_per_user"
}
],
stock: 0,
unlimited_stock: true
},
{
_id: ObjectId("53678a557e6e9e19dd4ef1c7"),
name: "product 2",
rules: [],
stock: 0,
unlimited_stock: true
}
And a user has already bought to following product: ObjectId("53678a557e6e9e19dd4ef1c8")
I'm trying to receive all products which have a stock greater than 0 or unlimited_stock AND which either dont have the one_purchase_per_user rule or (if they do) havent already been purchased by the specific user with the following query:
db.products.find({
'$and': [
{ '$or': [
{ stock: { '$gt': 0 } },
{ unlimited_stock: true }
] },
{ '$or': [
{ 'rules.type': 'one_purchase_per_user', _id: { $nin: [ObjectId("53678a557e6e9e19dd4ef1c8")] } },
{ 'rules.type': { '$ne': 'one_purchase_per_user' } }
] }
]
})
I get the following error:
error: { "$err" : "assertion src/mongo/db/query/plan_enumerator.cpp:1040" }
If i remove the part about $nin or change the field name on either of the last $or conditions the query succeeds but then the condition makes no sense since its not accomplishing what i want it to do.
I'm running mongodb 2.6.0
Edit: I figured out what caused the error, but i dont know why: I had an index on rules.type, if i removed that index the query works
I've just had a similar issue after an upgrade to mongodb 2.6.1
There's a bug fix probably coming in 2.6.2 - here's the bug report:
https://jira.mongodb.org/browse/SERVER-13714
From a comment in the JIRA issue
It's not exactly $or related but instead a problem with how we generate plans for $not queries that can use an index.
Adding your Products and executing your query I get the following result (using MongoDB shell 2.4.9):
{ "_id" : ObjectId("53678a557e6e9e19dd4ef1c7"), "name" : "product 2",
"rules" : [ ], "stock" : 0, "unlimited_stock" : true }
So it is working for me.
Try to quote $nin and _id like you did it with your other tags: '$nin' and '_id'.