mongodb : search an array of hashes - arrays

db.test.insert({_id:1,communications:[{type:'sms'}]})
db.test.find()
{ "_id" : 1, "communications" : [ { "type" : "sms" } ] }
Ok, its inserted
db.test.find({'communications':{type:'sms'}})
{ "_id" : 1, "communications" : [ { "type" : "sms" } ] }
Ok, I can find it if its an exact match
db.test.update({_id:1}, {communications:[{type:'sms',call_id:9878}]}
Now I updated it such that the hash nested in the array has two keys
.test.update({_id:1}, {communications:[{type:'sms',call_id:9878}]})
But I can't find it bc the hash is not the exact match! NOOOOO!
db.test.find({'communications':{type:'sms'}}).count()
=>0
So, how can I do a search like that where I want to match on one of the keys in a hash in an array?

If I've understood correctly (no guarantees I have!), then I think what you are looking for is the dot notation.
db.test.find({'communications.type':'sms'}).count()
Here's the reference on MongoDb.org with all the examples.

Related

MongoDB Aggregate search with comma separated string list in array

I have a MongoDB question. I have a search in an aggregation with $match.
Search should check an array if one of the values matches a value of the array inside the documents.
As an example:
var stringList = 'general,online,offline'; //--> should check each value of this list
and two documents as an example
{
"_id" : ObjectId("5e8f3a64ec717a0013d2f1f9"),
"category" : [
"general",
"online",
"internal",
"miscellaneous"
]},
{
"_id" : ObjectId("5e8f3afeec717a0013d2f1fa"),
"category" : [
"offline"
]
}
I´ve tried a lot but I don´t found out how it is possible to check each value of the string list with each value in the category array. My example should show both documents, but if I use $in I don´t get any result.
What I tried is:
Split the list by comma and map
use of $elemMatch
use if $in
use combination of $elemMatch and $in
I hope I could explain my problem with my aggregation.
Thx everyone for his help.
You should be able to split the string on , with .split function, then pass this in to a $in query.
var stringList = 'general,online,offline';
db.documents.find( { "category" : { $in : stringList.split(",") } } );
{ "_id" : ObjectId("5e8f3a64ec717a0013d2f1f9"), "category" : [ "general", "online", "internal", "miscellaneous" ] }
{ "_id" : ObjectId("5e8f3afeec717a0013d2f1fa"), "category" : [ "offline" ] }
You can also do this in a $match in an aggregation query.
> db.documents.aggregate([
{ $match : { category: { $in : stringList.split(",") } }}
])
Here is an example, where we can search the array name from by using the regex method in the query.
var x = ["sai","test","jacob","justin"],
regex = x.join("|");
db.documents.find({
"firstName": {
"$regex": regex,
"$options": "i"
}
});

MongoDB: Check for missing documents using a model tree structures with an array of ancestors

I'm using a model tree structures with an array of ancestors and I need to check if any document is missing.
{
"_id" : "GbxvxMdQ9rv8p6b8M",
"type" : "article",
"ancestors" : [ ]
}
{
"_id" : "mtmTBW8nA4YoCevf4",
"parent" : "GbxvxMdQ9rv8p6b8M",
"ancestors" : [
"GbxvxMdQ9rv8p6b8M"
]
}
{
"_id" : "J5Dg4fB5Kmdbi8mwj",
"parent" : "mtmTBW8nA4YoCevf4",
"ancestors" : [
"GbxvxMdQ9rv8p6b8M",
"mtmTBW8nA4YoCevf4"
]
}
{
"_id" : "tYmH8fQeTLpe4wxi7",
"refType" : "reference",
"parent" : "J5Dg4fB5Kmdbi8mwj",
"ancestors" : [
"GbxvxMdQ9rv8p6b8M",
"mtmTBW8nA4YoCevf4",
"J5Dg4fB5Kmdbi8mwj"
]
}
My attempt would be to check each ancestors id if it is existing. If this fails, this document is missing and the data structure is corrupted.
let ancestors;
Collection.find().forEach(r => {
if (r.ancestors) {
r.ancestors.forEach(a => {
if (!Collection.findOne(a))
missing.push(r._id);
});
}
});
But doing it like this will need MANY db calls. Is it possible to optimize this?
Maybe I could get an array with all unique ancestor ids first and check if these documents are existing within one db call??
First take out all distinct ancesstors from your collections.
var allAncesstorIds = db.<collectionName>.distinct("ancestors");
Then check if any of the ancesstor IDs are not in the collection.
var cursor = db.<collectionName>.find({_id : {$nin : allAncesstorIds}}, {_id : 1})
Iterate the cursor and insert all missing docs in a collection.
cursor.forEach(function (missingDocId) {
db.missing.insert(missingDocId);
});

MongoDB remove an item from an array inside an array of objects

I have a document that looks like this:
{
"_id" : ObjectId("56fea43a571332cc97e06d9c"),
"sections" : [
{
"_id" : ObjectId("56fea43a571332cc97e06d9e"),
"registered" : [
"123",
"e3d65a4e-2552-4995-ac5a-3c5180258d87"
]
}
]
}
I'd like to remove the 'e3d65a4e-2552-4995-ac5a-3c5180258d87' in the registered array of only the specific section with the _id of '56fea43a571332cc97e06d9e'.
My current attempt is something like this, but it just returns the original document unmodified.
db.test.findOneAndUpdate(
{
$and: [
{'sections._id': ObjectId('56fea43a571332cc97e06d9e')},
{'sections.registered': 'e3d65a4e-2552-4995-ac5a-3c5180258d87'}
]
},
{
$pull: {
$and: [
{'sections._id': ObjectId('56fea43a571332cc97e06d9e')},
{'sections.registered': 'e3d65a4e-2552-4995-ac5a-3c5180258d87'}
]
}
})
I've looked in to $pull, but I can't seem to figure out how to make it work on an array of nested objects containing another array. The $pull examples all seem to deal with only one level of nesting. How do I remove the matching entry from the registered array of the item in the sections array with the _id that I supply?
You need to use the positional $ update operator to remove the element from your array. You need this is because "sections" is an array of sub-documents.
db.test.findOneAndUpdate(
{ "sections._id" : ObjectId("56fea43a571332cc97e06d9e") },
{ "$pull": { "sections.$.registered": "e3d65a4e-2552-4995-ac5a-3c5180258d87" } }
)

Getting subdocument element's count per index inside an array and updating the subdocument key - subdocument in array(IN MONGODB)

How to get subdocument element's count inside an array and how to update the subdocument's key in MongoDB
For eg, following is the whole doc stored in mongodb:
{
"CompanyCode" : "SNBN",
"EventCode" : "ET00008352",
"EventName" : "Sunburn Presents Avicii India Tour",
"TktDetail" : [
{
"Type" : "Category I",
"Qty" : {
"10-Dec" : {
"value" : 58
},
"11-Dec" : {
"value" : 83
},
"12-Dec" : {
"value" : 100
}
}
},
{
"Type" : "Category II",
"Qty" : {
"10-Dec" : {
"value" : 4
},
"11-Dec" : {
"value" : 7
},
"12-Dec" : {
"value" : 8
}
}
},
{
"Type" : "PRICE LEVEL 1",
"Qty" : {
"11-Dec" : {
"value" : 2
}
}
},
{
"Type" : "CatIV",
"Qty" : {
"18-Dec" : {
"value" : 20
}
}
}
],
"TransDate" : [
"10-Dec-2013",
"11-Dec-2013",
"12-Dec-2013",
],
"VenueCode" : "SNBN",
"VenueName" : "Sunburn",
"_id" : ObjectId("52452db273b92012c41ad612")
}
Here TktDetail is an array, inside which there is a Qty subdoc which contains multiple elements, I want to know how to get the elements count inside Qty per index?
For example, the 0th index of TktDetail array contains 1 Qty subdoc, which further has a element count of 3, whereas 3rd index has element count of 1 in Qty subdoc.
If I want to update the subdoc key, like, I want to update the date in Qty from "10-Dec" to "10-Dec-2013", how is it possible?
Thanks in advance, looking for a reply ASAP..
So the first thing here is that you actually asked two questions, being "how do I get a count of the items under Qty?" and "how can I change the names?". Now while normally unrelated I'm going to treat them as the same thing.
What you need to do is change your schema and in doing so I'm going to allow you to get the count of items and I'm going to encourage you to change those field names as well. Specifically you need a schema like this:
"TktDetail" : [
{
"Type" : "Category I",
"Qty" : [
{ "date": ISODate("2013-12-10T00:00:00.000Z") , "value" : 58 },
{ "date": ISODate("2013-12-11T00:00:00.000Z"), "value" : 83 },
{ "date": ISODate("2013-12-01T00:00:00.000Z"), "value" : 100 },
]
},
All the gory details are in my answer here to a similar question. But the problem basically is that when you use sub-documents in the way you have you are ruining your chances of doing any meaningful query operations on this, as to get at each element you must specify the full path to get there.
That answer has more detail, but the case is you really want an array. The trade-off, it's a little harder to update, especially considering you have nested arrays, but it's a lot easier to add and much easier to query.
Also, and related, change your dates to be dates and not strings. The strings, are no good for comparisons inside MongoDB. With them set as proper BSON dates (noting I clipped them to the start of day) you can compare, and query ranges and do useful things. Your application code will be happy to as the driver will return a real date object, rather than something you have to manipulate "both ways".
So once you have read through, understood and implemented this, on to counting:
db.collection.aggregate([
// Unwind the TktDetail array to de-normalize
{"$unwind": "$TktDetail"},
// Also Unwind the Qty array
{"$unwind": "$Qty" },
// Get some group information and count the entries
{"$group": {
"_id": {
"_id": "$_id,
"EventCode": "$EventCode",
"Type": "$TktDetail.Type"
},
"Qty": {"$sum": 1 }
}},
// Project nicely
{"$project": {
"_id": 0,
"EventCode": "$_id.EventCode",
"Type: "$_id.Type",
"Qty": 1,
}},
// Let's even sort it
{"$sort": { "EventCode": 1, "Qty" -1 }}
])
So that allowed us to get a count of the items in Qty for each EventCode by Type with the Qty ordered higest to lowest.
And that is not possible on your current schema without loading and traversing each document in code.
So there is the case. Now if you want to ignore this and just go about changing the sub-document key names, then you'll need to do remove the key and underlying document and replace with the new key name, using update:
db.collection.update(
{ EventCode: "ET00008352"},
{ $unset:{ "TktDetail.0.Qty.10-Dec": "" }}
)
db.collection.update(
{ EventCode: "ET00008352"},
{ $set:{ "TktDetail.0.Qty.10-Dec-2013": { value: 58 } }}
)
And you'll need to do that for every item that you have.
So you either work out that schema conversion or otherwise have a lot of work anyway in order to change the keys. For myself, I'd do it properly, and only do it once so I didn't run into the next problem later.

Removing the array element in mongoDB based on the position of element

Actually I need to remove an element from the array based on its position. Using $pop we can remove element from top or bottom (considering it as a stack. 0th element at top) as explained here.
We can also remove element from array based on the value of the elements in the array using $pull as explained here.
But I need to remove element from the array based on position. So is there any way I can do this.
From documentation:
{ $pull : { field : {$gt: 3} } } removes array elements greater than 3
So i suppose that you can do somethig like this for now:
{ $pull : { field : {$gt: 3, $lt: 5} } } // shoud remove elemet in 4 position
Or try update using position operator, i suppose shoud be something like this:
{ $pull : "field.4" }
{ $pull : {"field.$": 4}}
It is only a suggestion, because i can't test it right now.
Update:
Seems you cant do it right know in one step(there is such bug in jira)
But you can remove using unset element in position and that pull elemets with null value:
{$unset : {"array.4" : 1 }}
{$pull : {"array" : null}}
That is how possible to do that with latest mongodb v4.2, ugly but working
_id: ObjectId("5e4539cb6aebbeaa0a6b8fe7")
}, [
{ $set:
{ notes:
{
$concatArrays: [
{ $slice: ['$notes', 4] },
{ $slice: ['$notes', { $add: [1, 4] }, { $size: '$notes' }]}
]
}
}
}
])```
Here's your answer: MongoDB pull array element from a collection
To remove specific element from an array of some document first you need to identify this element. For instance I have a document with array and every element of this array is an object:
{
"_id" : ObjectId("5140f34888dd50971900002d"),
"_permissions" : {
"edit" : [
{
"profile_id" : NumberLong(123),
"comment" : "app/project owner"
},
{
"profile_id" : NumberLong("153579099841888257"),
"comment" : "project admin"
},
{
"profile_id" : NumberLong("153579095869882369"),
"comment" : "project admin"
}
],
"view" : [
{
"profile_id" : NumberLong(123),
"comment" : "app/project owner"
},
{
"profile_id" : NumberLong("153579099841888257"),
"comment" : "project admin"
},
{
"profile_id" : NumberLong("153579095869882369"),
"comment" : "project admin"
}
]
}
}
So let's remove profile_id with "153579099841888257" value from _permissions.view array.
Here we go
db.collection.update({_id: ObjectId("5140f34888dd50971900002d")}, {$pull:{"_permissions.view": {"profile_id": NumberLong("153579099841888257")}}});
I define scope of the object (to make sure id doesn't affect any other first found document)
Identify needed element to pull out: profile_id with "153579099841888257" value in the _permissions.view array

Resources