MongoDB find query using $and and $nor operator - mongodb

I have a collection as below
{
"_id" : ObjectId("55bec0793bed809b2cb658ad"),
"lock_1" : ["desc_1","desc_2"],
"lock_2" : ["desc_1"]
},
{
"_id" : ObjectId("55bec0793bed809b2cb658ab"),
"lock_1" : ["desc_1","desc_2","desc_3"],
"lock_2" : ["desc_1"]
}
{
"_id" : ObjectId("55bec0793bed809b2cb658ac"),
"lock_1" : ["desc_1"],
"lock_2" : []
},
{
"_id" : ObjectId("55bec0793bed809b2cb658ae"),
"lock_1" : [],
"lock_2" : ["desc_1"]
},
{
"_id" : ObjectId("55bec0793bed809b2cb658aj"),
"lock_1" : ["desc_3","desc_4"],
"lock_2" : ["desc_5"]
},
{
"_id" : ObjectId("55bec0793bed809b2cb658ak"),
"lock_1" : [],
"lock_2" : []
}
I have retrieved all the documents having "desc_1" in both "lock_1" and "lock_2" array by using below query, returning first two documents, which is correct.
db.locks.find( { $and: [ {lock_1 : "desc_1"} , {lock_2 : "desc_1"} ] } )
Now, I am trying to get documents which does not satisfy above condition. I tried using below query but it is returning last two documents.
db.locks.find( { $nor: [ {lock_1 : "desc_1"} , {lock_2 : "desc_1"} ] } )
How to retrieve documents where "desc_1" is either not present or present in one of the arrays?

What you need here is the $or operator which performs a logical OR operation and the $ne operator to test the contents of fields directly.
db.locks.find({
"$or": [
{ "lock_1": { "$ne": "desc_1" } },
{ "lock_2": { "$ne": "desc_1" } }
]
})

db.locks.find({
$or: [
{ lock_1 : { $nin: [ "desc_1" ] }} ,
{ lock_2 : { $nin: [ "desc_1" ] }}
]
})
reference:
$or
$nin

Related

How to search mongodb

I have two JSONs in a collection in mongodb and would like to write a bson.M filter to fetch the first JSON below.
I tried with the filter below to get the first JSON but got no result.
When the first JSON is in the collection, I get result when i use the filter
but when i have both JSONs, I do not get a result. Need help.
filter := bson.M{"type": "FPF", "status": "REGISTERED","fpfInfo.fpfInfoList.ssai.st": 1, "fpfInfo.fpfInfoList.infoList.dn": "sim"}
{
"_id" : "47f6ad68-d431-4b69-9899-f33d828f8f9c",
"type" : "FPF",
"status" : "REGISTERED",
"fpfInfo" : {
"fpfInfoList" : [
{
"ssai" : {
"st" : 1
},
"infoList" : [
{
"dn" : "sim"
}
]
}
]
}
},
{
"_id" : "347c8ed2-d9d1-4f1a-9672-7e8a232d2bf8",
"type" : "FPF",
"status" : "REGISTERED",
"fpfInfo" : {
"fpfInfoList" : [
{
"ssai" : {
"st" : 1,
"ds" : "000004"
},
"infoList" : [
{
"dn" : "sim"
}
]
}
]
}
}
db.collection.aggregate([
{
"$unwind": "$fpfInfo.fpfInfoList"
},
{
"$match": {
"fpfInfo.fpfInfoList.ssai.ds": {
"$exists": false
},
"fpfInfo.fpfInfoList.infoList.dn": "sim",
"fpfInfo.fpfInfoList.ssai.st": 1
}
}
])
Playground

Mongodb query match with if condition form multiple fields with null values

I have a json where i am trying to filter my pipeline with multiple fields where some fields can have null value as well. sender.client and receiver.client may not always values
Below is the part of sample json:
{"sender" : {
"id" : "5d95",
"name" : "Name1",
"phone" : "123456",
"client" : "spec1"
},
"receiver" : {
"id" : "5d95683",
"name" : "name2",
"phone" : "342235",
"client" : "spec1"
}
}
{"sender" : {
"id" : "52fes",
"name" : "Name2",
"phone" : "3334321",
"client" : "spec2"
},
"receiver" : {
"id" : "5efse",
"name" : "name3",
"phone" : "7353344",
"client" : "spec1"
}
}
I am aiming to filter with condition if (sender.client = spec1 or receiver.client =spec1) then display all fields, i.e if the name of the client matches then i have to display other required fields. I have been trying with $project and $match but match is not working with $cond , so i have taken alternate route with $eq but it's not helping me filter out my requirement. Below is my code:
{
"$project" : {
"sendername" : {
"$cond" : {
"if" : {
"$eq" : [
"$sender.client",
"spec1"
]
},
"then" : "$sender.name",
"else" : 0.0
}
},
"sendername1" : {
"$cond" : {
"if" : {
"$eq" : [
"$receiver.client",
"spec1"
]
},
"then" : "$receiver.name",
"else" : 0.0
}
}
}
}
I want to use $match inpace of $eq. How do i accomplish this?
The $match with $exprcan filter based on the condition: (sender.client = spec1 or receiver.client = spec1)
I am aiming to filter with condition if (sender.client = spec1 or
receiver.client = spec1) then display all fields, i.e if the name of
the client matches then i have to display other required fields.
You can add further filter to match the name field (there are two fields with "name", sender.name and receiver.name; which one to match upon is not clear. I am assuming it can be any one).
var queryFilter = { $cond: {
if: { $or: [
{ $eq: [ "$sender.client", "spec1" ] },
{ $eq: [ "$receiver.client", "spec1" ] }
]
},
then: true,
else: false
}
};
db.test.aggregate( [
{
$match: {
$expr: { $eq: [ queryFilter, true ],
$or: [ { "sender.name" : "name3" }, { "receiver.name" : "name3" } ] }
}
}
] )
The match filter is: $expr: {...} and $or: [...].
Further, you can add other stages (e.g., $project) after the $match stage, as needed.

Mongodb use slice and elementmatch

I am trying to retrieve elements in an array in mongo db. I would like to retrieve the 15 first elements which do not match a pattern
So let's imagine I have
{
"_id" : ObjectId("s4dcsd5s4d6c54s6d"),
"items" : [
{
type : "TYPE_1",
text : "blablabla"
},
{
type : "TYPE_2",
text : "blablabla"
},
{
type : "TYPE_3",
text : "blablabla"
},
{
type : "TYPE_1",
text : "blablabla"
},
{
type : "TYPE_2",
text : "blablabla"
},
{
type : "TYPE_1",
text : "blablabla"
}
]
}
So currently I have more element to match compared to the element to not match that's why I use nin. but it is to simplifiy
If I use
db.history.find({ "_id" : ObjectId("s4dcsd5s4d6c54s6d")}, { "items" : { "$elemMatch" : { "type" : { "$nin" : [ "TYPE_2" , "TYPE_3"]}}}, "items" : { $slice : [0, 2]}}).pretty()
It seems that the element match is not taken into account (inverse as well if i put element match after slice)
Then if I do:
db.history.find({ "_id" : ObjectId("s4dcsd5s4d6c54s6d")}, { "items" : { "$elemMatch" : { "type" : { "$nin" : [ "TYPE_2" , "TYPE_3"]}}, $slice : [0, 2]}}).pretty()
An error is thrown by mongo
Do you know how I can do?
Thanks a lot
You can't use $elemMatch for your case since it will only return the first element. From documentation :
$elemMatch The $elemMatch operator limits the contents of an
field from the query results to contain only the first element
matching the $elemMatch condition.
You can do an aggregation query which will do the following:
match your _id
unwind your items array to have one record per items in the array
match the types $nin your array [ "TYPE_2" , "TYPE_3"]
limit the number of result
The query is :
db.history.aggregate([{
$match: {
_id: ObjectId("s4dcsd5s4d6c54s6d")
}
}, {
$unwind: '$items'
}, {
$match: {
'items.type': { '$nin': ["TYPE_2", "TYPE_3"] }
}
},
{ $limit: 2 }
])
It gives :
{ "_id" : "s4dcsd5s4d6c54s6d", "items" : { "type" : "TYPE_1", "text" : "blablabla" } }
{ "_id" : "s4dcsd5s4d6c54s6d", "items" : { "type" : "TYPE_1", "text" : "blablabla" } }
You will need to use aggregation for restricting the array in the form you have. Use $filter to apply the condition and $slice to limit the array elements.
db.history.aggregate([{
$match: {
_id: ObjectId("586309d6772c68234445f2a5")
}
}, {
"$project": {
"items": {
"$slice": [{
"$filter": {
"input": "$items",
"as": "item",
"cond": {
"$and": [{
$ne: ["$$item.type", "TYPE_2"]
}, {
$ne: ["$$item.type", "TYPE_3"]
}]
}
}
},
2
]
}
}
}])
Sample Output:
{ "_id" : ObjectId("586309d6772c68234445f2a5"), "items" : [ { "type" : "TYPE_1", "text" : "blablabla" }, { "type" : "TYPE_1", "text" : "blablabla" } ] }

How to check if nested arrays are ALL empty in mongodb?

I have something like below:
{
"_id" : "1",
"firstArray" : [
{
"_id" : "11",
"secondArray" : [ ]
},
{
"_id" : "12",
"secondArray" : [ ]
},
{
"_id" : "13",
"secondArray" : [ { "type" : "somthing" } ]
}
]
},
{
"_id" : "2",
"firstArray" : [
{
"_id" : "21",
"secondArray" : [ ]
},
{
"_id" : "22",
"secondArray" : [ ]
}
]
}
I need a mongodb query to find documents which ALL of the nested secondArrays are empty? the query should return second document and not the first one.
to solve that, we need to check size of arr2, but to enable that we need first to unwind arr1.
Please find below aggregation framework snippet which solves this problem,
db.pmoubed.aggregate([{
$unwind : "$firstArray"
}, {
$project : {
_id : 1,
firstArray : 1,
isNotEmpty : {
$size : "$firstArray.secondArray"
}
}
}, {
$group : {
_id : "$_id",
isNotEmpty : {
$sum : "$isNotEmpty"
},
firstArray : {
$push : "$firstArray"
}
}
}, {
$match : {
"isNotEmpty" : 0
}
}
])
Any comments welcome

Retrieve the second element of array for each document - MongoDB

I have a MongoDB collection, called bios, that contains documents similar to these:
{
"_id" : ObjectId("51df07b094c6acd67e492f41"),
"name" : {
"first" : "John",
"last" : "McCarthy"
},
"birth" : ISODate("1927-09-04T04:00:00Z"),
"death" : ISODate("2011-12-24T05:00:00Z"),
"contribs" : [
"Lisp",
"Artificial Intelligence",
"ALGOL"
]
},
{
"_id" : 3,
"name" : {
"first" : "Grace",
"last" : "Hopper"
},
"title" : "Rear Admiral",
"birth" : ISODate("1906-12-09T05:00:00Z"),
"death" : ISODate("1992-01-01T05:00:00Z"),
"contribs" : [
"UNIVAC",
"compiler",
"FLOW-MATIC",
"COBOL"
]
}
My target is to retrieve the second element of the array contribs for each document in bios collection.
Using the new aggregation pipeline operator $filter I run the following query:
> db.bios.aggregate([
{
$match: {"contribs.2":{"$exists":1}}},
{
$project:{contribs:
{
$filter:{input:"$contribs", as: "contribs", cond:{}}},_id:0}}])
With my query, the output is:
{ "contribs" : [ "Lisp", "Artificial Intelligence", "ALGOL" ] }
{ "contribs" : [ "UNIVAC", "compiler", "FLOW-MATIC", "COBOL" ] }
that is not just the second element of the array contribs but a projection on contribs array when its second element exists.
did you try $elementAt ?
db.bios.aggregate([
{ $match: {"contribs.1": { "$exists": 1 } }},
{ $project: { contribs: { $arrayElemAt: [ "$contribs", 1 ] } } }
]);