mongodb - how to use $project with $or - mongodb

I have a following collection:
_id,
one,
two,
messages: [{
sender
seen
}]
and I need to get count for 'messages' in each collection, where (one=1 AND sender IS NOT 1) OR (two=1 AND sender IS NOT 1)
'$or': [
{ 'one': 1, 'messages.sender': {$ne: 1},
{ 'two': 1, 'messages.sender': {$ne: 1}
]
edit
When I try to $project with $or, returns me nothing
Conversation.aggregate([
{
'$or': [
{ 'one': 1, 'messages.sender': {$ne: 1},
{ 'two': 1, 'messages.sender': {$ne: 1}
]
},
{
$project: {
count: { $size:"$messages" }
}
}
]).exec(function (err, c) {
res.json(c);
});
I'm really stuck with this and have no clue how to get the results.
Thanks for help

You can use $cond operator in 2.6 version as a work around solution.
Assuming that we have the next collection :
{
"_id" : ObjectId("5ad469bb18a5a3164402d279"),
"Show" : true,
"Name" : "Alamir"
}
{
"_id" : ObjectId("5ad469c318a5a3164402d27a"),
"Show" : false,
"Name" : "Alamir"
}
and we want to project the result based on 'Show' filed as (If show == true then show the field , else hide the filed from the result set )
So the query will be :
db.myCollection.aggregate([
{ $project :
{ Name : 1 ,
Show :
{
$cond : { if : { $eq : ["$Show" , true] } , then : "$Show" , else : "" }
}
}}])

Related

How can I query mongodb collection for an array nested in an array of a document?

I have a mongo collection containing structurally similar documents as illustrated below-
{
"_id" : ObjectId("mongoid"),
"type" : "chemical",
"sourceId" : "27553452120",
"array1" : [
{
"cid" : "1235689",
"outcome" : "test",
"relation" : "=",
"array2" : [
{
"name" : "test1"
},
{
"name" : "test2"
},
{
"value" : 1.628,
"name" : "test3"
},
{
"value" : 1.63,
"name" : "test4"
}
]
}
]
}
I want to query this collection for a case, where array1.array2.length > 1
I tried following sample query on mongo shell:
db.collection.find({"array1.array2":{$exists:true},$where:"this.array1.array2.length>1"}).limit(1).pretty()
but it fails stating
Error: error: {
"ok" : 0,
"errmsg" : "TypeError: this.array1.array2 is undefined :\n#:1:15\n",
"code" : 139,
"codeName" : "JSInterpreterFailure"
}
How can this query be achieved?
This should solve your problem.
db.test.aggregate(
{
$match:{"array1.array2": {$nin:[null,[]]}}
}
).pretty();
Try this
db.collection.find({"array1.array2":{$exists: true, $ne : []} })
According to description as mentioned into above question please try executing following aggregate query as a solution.
db.collection.aggregate(
// Pipeline
[
// Stage 1
{
$match: {
array1: {
$elemMatch: {
array2: {
$exists: true
}
}
}
}
},
// Stage 2
{
$project: {
array2size: {
$size: {
$arrayElemAt: ['$array1.array2', 0]
}
},
array1: 1
}
},
// Stage 3
{
$match: {
'array2size': {
$gt: 0
}
}
},
]
);
What about this way:
db.test.find({"array1.array2.1":{$exists:1}})
This is more flexible, it allow you to query for any length not only 1.
Last ".1" it is zero based array index. So if index 1 exists it means array has at least 2 elements. You can search for < N by changing $exists to 0. If no index 3 in array it means array has less then 4 elements.
If you need to search for exact size you can use $size operator
db.test.find({"array1.array2":{$size:5}})

Mongo query for date range in ISODate format

I'm trying to create this query to use with jasper reports but something is wrong with my syntax, i have 2 ISODates in my db and i want to query between then, in this query I use $match and $gt, $lt.
Anyway something is not correct because the console says error but doesn`t specify it.
Obs: The problem is something with the $match and the $lt and $gt. because without them it is possible to query.
Code:
{
runCommand : {
aggregate : 'saleCalculation',
pipeline : [
{
$match: {
processingInfo.modifiedDate: {$gt : ISODate("2016-05-01T09:29:40.572Z")},
{$lt : ISODate("2016-06-01T09:29:40.572Z")}
}
},
{
$project : {
header.documentCode : 1,
header.transactionDate : 1,
header.metadata.entityName : 1,
lines : 1
}
},
{ $unwind : '$lines'},
{ $unwind :'$lines.calculatedTax.details' }
]
}
}
Jasper Manual says i need to do something like that:
http://community.jaspersoft.com/wiki/how-query-mongo-isodate-data-parameter
but i didn't understand that
Try this code :
{
runCommand : {
aggregate : 'saleCalculation',
pipeline : [
{
$match: {
processingInfo.modifiedDate: {$gt : ISODate("2016-05-01T09:29:40.572Z"),
$lt : ISODate("2016-06-01T09:29:40.572Z")}
}
},
{
$project : {
header.documentCode : 1,
header.transactionDate : 1,
header.metadata.entityName : 1,
lines : 1
}
},
{ $unwind : '$lines'},
{ $unwind :'$lines.calculatedTax.details' }
]
}
}
Try with this pipeline. Make sure to use double quotes.
[
{ "$match": {"processingInfo.modifiedDate": { "$gt":ISODate("2010-03-26T18:40:59.000Z"), "$lt":ISODate("2016-09-28T18:40:59.000Z")}}},
{
"$project" : {
"header.documentCode": 1,
"header.transactionDate": 1,
"header.metadata.entityName": 1,
"lines" : 1
}
},
{ "$unwind": "$lines"},
{ "$unwind": "$lines.calculatedTax.details" }
]
I tried this command on mongo shell and it worked without any error.
db.runCommand({"aggregate": "saleCalculation", "pipeline" : [
{ "$match": {"processingInfo.modifiedDate": { "$gt":ISODate("2010-03-26T18:40:59.000Z"), "$lt":ISODate("2016-09-28T18:40:59.000Z")}}},
{
"$project" : {
"header.documentCode": 1,
"header.transactionDate": 1,
"header.metadata.entityName": 1,
"lines" : 1
}
},
{ "$unwind": "$lines"},
{ "$unwind": "$lines.calculatedTax.details" }
]
})

In MongoDB how to find documents where property is object but not Array

I have collection users where each contains property gyms e.g.:
{
"_id" : ObjectId("aaaaa"),
"firstName" : "first",
"lastName" : "second",
"email" : "aaa#aaaa",
"password" : "aaaaa",
"gyms" : [
{
"name" : "aaaaaa",
"address" : "aaaaaaaaaaaaa"
}
]
}
However when I run db.users.aggregate(..) I get:
exception: Value at end of $unwind field path '$gyms' must be an Array, but is a Object
It seems that some of the users documents contain gym:{} or no gym at all rather than array. I need to find these documents, how would I do that?
EDIT:
Aggregate command I run:
db.users.aggregate({ $unwind: "$gyms" }, { $match: { "gyms.address": { $exists: true } } } )
Using {$type: "object"} gives you results containing documents with arrays too. So, to check for pure objects, just add an extra check to ensure that arrays are discarded, viz., obj.0.key: {$exists: false}.
In your case,
db.users.find({"gyms": {$type: "object"}, "gyms.0.name": {$exists: false}}).map(function(u) {
// modify u.gyms to your satisfaction
db.conversations.save(c);
})
Try this may be it also help you
db.users.aggregate([{
"$match": {
"$nor": [{
"gyms": {
"$exists": false
}
}, {
"gyms": {
"$size": 0
}
}, {
"gyms": {
"$size": 1
}
}]
}
}, {
"$unwind": "$gyms"
}]).pretty()
Try something like this:
> db.test.drop()
> db.test.insert({ "x" : ["an array"] })
> db.test.insert({ "x" : { "type" : "object" } })
> db.test.find({ "x" : { "$type" : 3 } })
{ "x" : { "type" : "object" } }

Mongodb $nin querying sub-document

Here is the sample document of my MongoDB:
user: {
_id:1,
name:'xyz',
age:12,
mobile:21321312,
transaction:[
{
trans_id:1,
prod:'a',
purchasedAt:ISODate("2015-02-01"),
},
{
trans_id:2,
prod:'b',
purchasedAt:ISODate("2015-02-01")
},
{
trans_id:3,
prod:'c',
purchasedAt:ISODate("2014-11-24")
}
]
}
I want to get the users who have purchased product 'a' on date '2015-02-01' but not have purchased the product 'b' or 'c' on same day. So I tried querying:
db.user.find({transaction:{$elemMatch:{prod:'a',purchasedAt:ISODate("2015-02-01")}}, transaction:{$elemMatch:{prod:{$nin:['b', 'c']}, purchasedAt:ISODate("2015-02-01")}}})
But the query seems to return wrong result. It contains some users who have purchased product 'c' on the same day. So I tried:
db.user.find({transaction:{$elemMatch:{prod:{$in:['a'], $nin:['b','c']}, purchasedAt:ISODate("2015-02-01")}}})
db.user.find({$and:[{transaction:{$elemMatch:{prod:'a',purchasedAt:ISODate("2015-02-01")}}}, {transaction:{$elemMatch:{prod:{$nin:['b', 'c']}, purchasedAt:ISODate("2015-02-01")}}}]})
But none seems to work. I always get the product with same purchased date which is in $nin part. I have tried other queries also but those are of same kind as above (like querying with dot '.' operator) and are trivial to mention here. Is there any way to get the result I want?
Try this:
db.test.find({
"$and" : [
{ "transaction" : {
"$elemMatch" : { "prod" : "a", "purchasedAt" : ISODate("2015-02-01") }
} },
{ "transaction" : { "$not" : {
"$elemMatch" : { "prod" : "b", "purchasedAt" : ISODate("2015-02-01") }
} } },
{ "transaction" : { "$not" : {
"$elemMatch" : { "prod" : "c", "purchasedAt" : ISODate("2015-02-01") }
} } }
]
})
Following two ways you can find out your results
1> Using mongo aggregation
db.collectionName.aggregate({
"$unwind": "$user.transaction"
}, {
"$match": {
"$and": [{
"user.transaction.prod": "a"
}, {
"user.transaction.prod": {
"$nin": ["b", "c"]
}
}, {
"user.transaction.purchasedAt": ISODate("2015-02-01T00:00:00Z")
}]
}
}).pretty()
2> Using find with projection
db.collectionName.find({
"user.transaction.prod": "a",
"user.transaction.purchasedAt": ISODate("2015-02-01T00:00:00Z")
},
{
"user.transaction.$": 1
}).pretty()

Mongodb count() of internal array

I have the following MongoDB collection db.students:
/* 0 */
{
"id" : "0000",
"name" : "John"
"subjects" : [
{
"professor" : "Smith",
"day" : "Monday"
},
{
"professor" : "Smith",
"day" : "Tuesday"
}
]
}
/* 1 */
{
"id" : "0001",
"name" : "Mike"
"subjects" : [
{
"professor" : "Smith",
"day" : "Monday"
}
]
}
I want to find the number of subjects for a given student. I have a query:
db.students.find({'id':'0000'})
that will return the student document. How do I find the count for 'subjects'? Is it doable in a simple query?
If query will return just one element :
db.students.find({'id':'0000'})[0].subjects.length;
For multiple elements in cursor :
db.students.find({'id':'0000'}).forEach(function(doc) {
print(doc.subjects.length);
})
Do not forget to check existence of subjects either in query or before check .length
You could use the aggregation framework
db.students.aggregate(
[
{ $match : {'_id': '0000'}},
{ $unwind : "$subjects" },
{ $group : { _id : null, number : { $sum : 1 } } }
]
);
The $match stage will filter based on the student's _id
The $unwind stage will deconstruct your subjects array to multiple documents
The $group stage is when the count is done. _id is null because you are doing the count for only one user and only need to count.
You will have a result like :
{ "result" : [ { "_id" : null, "number" : 187 } ], "ok" : 1 }
Just another nice and simple aggregation solution:
db.students.aggregate([
{ $match : { 'id':'0000' } },
{ $project: {
subjectsCount: { $cond: {
if: { $isArray: "$subjects" },
then: { $size: "$subjects" },
else: 0
}
}
}
}
]).then(result => {
// handle result
}).catch(err => {
throw err;
});
Thanks!