Search for array exist and empty - mongodb atlas search - mongodb-atlas

I want to search for a document in collcetion using atlas search index with following conditions for a given field someValue :
It must exist.
It should be empty.
NOTE : the size property i have written arbitrarily, please suggest how can i achieve this. Is there a way ?
"compound": {
"must": [
{
"compound": {
"should": [
{
"compound": {
"must": [
{
"exists": {
"path": "someValue"
}
},
{
'size' : 0
}
]
}
},
{
"compound": {
"mustNot": [
{
"exists": {
"path": "someValue"
}
}
]
}
}
]
}
}
]
}
}```

Related

Check for missing field or null value in mongoDB atlas

I am using mongodb atlas for full text search.
My sample collection looks like this :
{
"_id": "62fdfd7518da050007f035c5",
"expiryDate": "2022-08-18T23:59:59+05:30",
"arrayField" : ['abc', 'def', 'ghi', 'jkl']
},
{
"_id": "62fdfd7518da050007f035c6",
"expiryDate": null,
"arrayField" : ['abc','jkl']
},
{
"_id": "62fdfd7518da050007f035c7",
"arrayField" : []
},
{
"_id": "62fdfd7518da050007f035c8",
"expiryDate": null
}
expiryDate is a Date type field and arrayField is an Array type field.
My goal is to get all documents where either :
expiryDate doesn't exists OR
If expiryDate does exists, then it must be null OR
If expiryDate does exists, then it must greater than current time.
My current atlas aggregation looks like :
{
'compound' : {
'should' : [
{
'compound' : {
'mustNot' : [{
"exists": {
"path": "expiryDate",
}
}]
}
},
{
"range": {
"path": "expiryDate",
'gte': new Date()
}
}
],
'minimumShouldMatch' : 1
}
}
This is not returning all documents where the expiryDate field have null value and it is only matching one clause of should where expiryDate is greater than or equal to current time. I want it to return all those documents too where the expiryDate is null.
Please advise.
You can use the $exists operator (docs) to check if an element exists, and if it does, run a check on its value.
So I tried with multiple clauses and approaches and found that there are two solutions to this problem :
Use combination of $must, $should and $mustNot :
{
'compound' : {
'should' : [
{
'compound' : {
'mustNot' : [{
"exists": {
"path": "expiryDate",
}
}]
}
},
{
"compound": {
"must": [
{
"exists": {
"path": "expiryDate"
}
}
],
"mustNot": [
{
"range": {
"path": "expiryDate",
"lt": new Date()
}
}
]
}
}
{
"range": {
"path": "expiryDate",
'gte': new Date()
}
}
],
'minimumShouldMatch' : 1
}
}
And the second one is rather not optimized but works. Since at the end it's and aggregation; We can use $match operator just outside the $search pipeline like so :
db.exampleCollection.aggregate([
{
"$search": {
"index": "default",
"compound": {
"must": [
...some conditions
],
"filter": [
...some clauses
]
}
}
},
{
"$match": [...some other conditions]
},
{
"$project": {
...some fields
}
},
{
"$skip": 0
},
{
"$limit": 10
},
{
"$sort": {
"score": 1
}
}
])
Hope it helps someone 🙂
#PawanSaxena I'm trying to do something similar, i.e., finding documents whose date field exists (and) with null value. Did you test out just the 2nd goal in your original post. Somehow it didn't work for me. Here is my testing code:
{
"compound": {
"must": [
{
"exists": {
"path": "expiryDate"
}
}
],
"mustNot": [ /// should it be "must"?
{
"range": {
"path": "expiryDate",
"lt": new Date() /// see below _MONGOSH, new Date() will output current date
// "lt": 1 /// this doesn't work, either
}
}
]
}
}

How can I remove an element that is contained in the third level of an array?

my database has a structure like this:
{
"universe": "comics",
"saga": [
{
"name": "x-men",
"characters": [
{
"character": "wolverine",
"powers": [
{
"power": "self-recovery"
},
{
"power": "Super strength"
},
{
"power": "steels claws"
}
]
},
{
"character": "cyclops",
"powers": [
{
"power": "rays"
},
{
"power": "Super strength"
}
]
}
]
}
]
},
{
"universe": "comics",
"saga": [
{
"name": "spiderman",
"characters": [
{
"character": "venom",
"powers": [
{
"power": "Super strength"
}
]
}
]
}
]
}
I basically want to learn how to do operations with complex arrays, this time I think I will learn too much if I get the answer to this question.
I want to delete the objects where "power:"self-recovery"
something like $.saga.characters.$.powers
I don't know how to do it because of the number of levels this property is under the main root.
normally i would use something like that:
db.mydb.update(
{
saga: {
$elemMatch: { saga.characters.$ },
},
},
{ $pull: { powers: { power: "Super strength"
} }},
{
new: true,
multi:true
},
for this example it should delete as many objects where {" power ":" self-recovery"} (in this case, only one object is deleted, which is where the character is wolverine)
but i don't know how to do what i need.
Try the positional all $[] operator in MongoDb 3.6+
var query = {
universe: 'comics'
};
var update = {
$pull: {
'saga.$[].characters.$[].powers': {
power: 'self-recovery'
}
}
};
var options = {
multi: true
};
db.collection.update(query, update, options);
db.test.update(
{
"saga.characters.powers": {
"$elemMatch": {
"power": "self-recovery"
}
}
},
{
"$pull": {
"saga.$[].characters.$.powers": {
"power":"self-recovery"
}
}
}
)
It removes the respective power object.It results
/* 1 */
{
"_id" : ObjectId("5f11e25a9e001b53e39985fd"),
"universe" : "comics",
"saga" : [
{
"name" : "x-men",
"characters" : [
{
"character" : "wolverine",
"powers" : [
{
"power" : "Super strength"
},
{
"power" : "steels claws"
}
]
},
{
"character" : "cyclops",
"powers" : [
{
"power" : "rays"
},
{
"power" : "Super strength"
}
]
}
]
}
]
}
Kindly check and let me know missing things

Mongo query multiple levels of nested document

I have a mongodb collection that has 'nested' documents. For example, a document can have the following structure:
{
"condition": {
"parameter": {
"type": "person"
}
}
}
as well as the next one:
{
"condition": {
"conditions": [
{
"conditions": [
{
"parameter": {
"type": "A"
}
},
{
"parameter": {
"type": "B"
}
}
]
},
{
"parameter": {
"type": "C"
}
}
]
}
}
Meaning, each condition sub-document can have multiple conditions within itself.
Now, I'd want to make a 'recursive' query on the type field of each condition, something like ('..' representing the recursion):
{
"$or": [
{"condition.type": "person"},
{"condition..conditions.type": "person"}
]
}
Is there any way to do this in Mongo?

How do I get the current validator of a mongo collection?

How do I get the current validator of a mongo collection?
Or is there no way, and I just need to overwrite it?
You can get it with the db.getCollectionInfos() method.
Example:
Create a collection in an empty database:
db.createCollection( "contacts",
{
validator: { $or:
[
{ phone: { $type: "string" } },
{ email: { $regex: /#mongodb\.com$/ } },
{ status: { $in: [ "Unknown", "Incomplete" ] } }
]
},
validationAction: "warn"
}
)
{ "ok" : 1 }
Run the command:
db.getCollectionInfos()[0].options.validator
Result:
{
"$or" : [
{
"phone": {
"$type": "string"
}
},
{
"email": {
"$regex": /#mongodb\.com$/
}
},
{
"status": {
"$in": [
"Unknown",
"Incomplete"
]
}
}
]
}
The validator is found as an object in the options object.

Find a single field from a nested Array in MongoDB

Here I have a sample Nested Array. I have a problem with writing proper queries on this collection which is deeply nested.
{
"productUUID" : "craft001",
"providers": [
{
"providerUUID": "prov001",
"orgs": [
{
"orgUUID": "org001",
"location": {
"buildings": [
{
"buildingUUID": "sit001",
"floors": [
{
"floorUUID": "GrndFlr",
"assets": [ ],
"agents": [ ],
"users": [ ]
},
{
"floorUUID": "1stFlr",
"assets": [ ],
"agents": [ ],
"users": [ ]
}
]
},
{
"buildingUUID": "ist001",
"floors": [ ]
}
]
}
},
{
"orgUUID": "org002",
"location": {
"buildings": [ ]
}
}
]
},
{
"providerUUID": "prov002",
"orgs": [ ]
}
]
}
Question in simple words, "1. Get all orgUUIDs which fall under providerUUID: "prov001"".
Similarly, "2. Get all floorUUIDs where "buildingUUID": "sit001"".
If someone can help me with the 1st ques, I hope I can solve the 2nd ques myself.
Mongo aggregation use to finding to nested documents. First unwind all providers array then use match to match providerUUID as given prov001 then used project to get all orgUUID and aggregation query as :
db.collectionName.aggregate({"$unwind":"$providers"},
{"$match":{"providers.providerUUID":"prov001"}},
{"$project":{"orgUUID":"$providers.orgs.orgUUID"}},
{"$unwind":"$orgUUID"},
{"$project":{"_id":0,"orgUUID":1}}
).pretty()
this will returns all orgUUID in an array.
If you use $elemMacth then this operator having it's own limitation as
The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.
elemMatch query as :
db.collectionName.find({"providers":{"$elemMatch":{"providerUUID":"prov001"}}},
{"providers.$.providerUUID.orgs.orgUUID":1}).pretty()
it returns whole matching providers array.
I hope you will find out "2" question query yourself, If you having any trouble with finding with "2" query I will post "2" query also. Try to yourself to find out second query answer yourself :)
For some reason, I had to change the data in collection as following.
{
"productUUID": "prod001",
"providers": [
{
"providerUUID": "prov001",
"orgs": [
{
"orgUUID": "org001",
"floors": [
{ "floorUUID": "SIT_GrndFlr" },
{ "floorUUID": "SIT_1stFlr" }
],
"assets": [{},{}],
"agents": [{},{}],
"users": [{},{}]
},
{
"orgUUID": "org002",
"floors": [
{ "floorUUID": "IST_1stFlr" },
{ "floorUUID": "IST_2ndFlr" }
],
"assets": [{},{}],
"agents": [{},{}],
"users": [{},{}]
}
]
},
{
"providerUUID": "prov002",
"orgs": [
{
"orgUUID": "org001",
"floors": [{},{}],
"assets": [{},{}],
"agents": [{},{}],
"users": [{},{}]
},
{
"orgUUID": "org002",
"floors": [{},{}],
"assets": [{},{}],
"agents": [{},{}],
"users": [{},{}]
}
]
}
]
}
so, now with the help of #yogesh, I was introduced to aggregate and was able to write queries for my questions.
1. Get all `orgUUID`s under `providerUUID: "prov001"`.
db.collectionName.aggregate({"$unwind":"$providers"},
{"$match":{"providers.providerUUID":"prov001"}},
{"$project":{"orgUUID":"$providers.orgs.orgUUID"}},
{"$unwind":"$orgUUID"},
{"$project":{"_id":0,"orgUUID":1}}
)
2. Get all `floorUUID`s under `orgUUID : "org001"`.
db.collectionName.aggregate(
{ "$unwind" : "$providers" },
{ "$match" : { "providers.providerUUID" : "prov001" } },
{ "$unwind" : "$providers.orgs" },
{ "$match" : { "providers.orgs.orgUUID" : "org001" } },
{ "$project" : { "floorUUID" : "$providers.orgs.floors.floorUUID" } },
{ "$unwind" : "$floorUUID" },
{ "$project" : { "_id":0 , "floorUUID" : 1 } }
)