Find documents matching multiple fields in an object array in MongoDB - arrays

Let's say I have an array of objects (let us call that array A) and I need a query to find a collection in MongoDB for all documents matching one of it's fields to one of the properties of object 1 in array A and another field to some other property in the same object in array A.
The documents do not have all the properties that the objects in array A have.
To make things clear...
Array A would look something like this...
[{
id_bus:1,
id_bus_variation:13,
....
},{
id_bus:2,
id_bus_variation:184,
....
},{
id_bus:3,
id_bus_variation:13,
....
}]
The documents in my database include those two properties and I need to match those two at the same time. For example, I need to find in my database the docs that have id_bus == 1 and id_bus_variation == 13, and also the ones that have id_bus == 2 and id_bus_variation == 184 but not the ones that id_bus == 4 and id_bus_variation == 13.
I really don't have any idea of how to do this using a single query, the only way around it I found is to go through array A and execute a query for each of it's elements, matching all the fields I need, but that doesn't seem efficient.

It sounds like you want to match the structure of a subdocument in an array to one of many possible structures specified by an array. I'll give an example of how to do this in the mongo shell:
> db.test.insert({
"_id" : 0,
bus : [
{ "id_bus" : 1, "id_bus_variation" : 1 },
{ "id_bus" : 2, "id_bus_variation" : 2 },
{ "id_bus" : 3, "id_bus_variation" : 3 }
]
})
> db.test.insert({
"_id" : 1,
bus : [
{ "id_bus" : 1, "id_bus_variation" : 3 },
{ "id_bus" : 2, "id_bus_variation" : 2 },
{ "id_bus" : 3, "id_bus_variation" : 1 }
]
})
> db.test.insert({
"_id" : 2,
bus : [
{ "id_bus" : 1, "id_bus_variation" : 1 },
{ "id_bus" : 2, "id_bus_variation" : 3 },
{ "id_bus" : 3, "id_bus_variation" : 2 }
]
})
If we want to return all documents where (id_bus = 2 and id_bus_variation = 3) or (id_bus = 3 and id_bus_variation = 3), as specified in an array
> var match = [{ "id_bus" : 2, "id_bus_variation" : 3 }, { "id_bus" : 3, "id_bus_variation" : 3 }];
We can construct the query programmatically:
> var query = { "$or" : [] }
> for (var i = 0; i < match.length; i++) {
query["$or"].push({ "bus" : { "$elemMatch" : match[i] } });
}
> db.test.find(query, { "_id" : 1 }) // just get _id's for easy reading
{ "_id" : 0 }
{ "_id" : 2 }
We get the expected results.

I don't know if I understand your Question.
Your collection could be like
{
"_id" : ObjectId("53de54c1560b7815e123792f"),
"bus" : [
{
"id_bus" : 1,
"id_bus_variation" : 13
},
{
"id_bus" : 2,
"id_bus_variation" : 184
},
{
"id_bus" : 3,
"id_bus_variation" : 13
}
]
}
And you want retrieve the document only if id_bus and id_bus_variation are "true"
You can try it
db.stack.find({$and:[{ "bus.id_bus" : 1,"bus.id_bus_variation" : 13},{"bus.id_bus" : 2,"bus.id_bus_variation" : 184}]})
and retrieve the Document only if bus.id_bus and bus.id_bus_variation are in Document.
For Example
db.stack.find({$and:[{ "bus.id_bus" : 1,"bus.id_bus_variation" : 13},{"bus.id_bus" : 2,"bus.id_bus_variation" : 184},{"bus.id_bus":4}]})
you haven't any result.
If you want exactly the element inside Object
db.stack.find ( { bus: { "$elemMatch" : { id_bus:1, id_bus_variation : 13} } } )
The document return only if both value are "true"

Related

Insert numbers into mongo-db collection

I am currently learning Mongo DB and trying to insert numbers into my "numbers" collection (in mongo command shell).
//This works :
db.numbers.insertMany([{"number":1},{"number":2}]);
//This doesn't
db.numbers.insertMany([1,2,3,4,5,6]);
(1) Does that mean that number is not a valid document or I am missing a very basic concept ?
(2) Why Mongo-db is not assigning Object-ID to numbers automatically in this case ?
//actual output from Mongoshell version 4.2.6 command line
> db.numbers.insertMany([{"number":1},{"number":2}]);
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("5f79fa89d04cd9e2b3acbf03"),
ObjectId("5f79fa89d04cd9e2b3acbf04")
]
}
> db.numbers.find();
{ "_id" : ObjectId("5f79fa89d04cd9e2b3acbf03"), "number" : 1 }
{ "_id" : ObjectId("5f79fa89d04cd9e2b3acbf04"), "number" : 2 }
> db.numbers.insertOne({number:[1,2,3,4,5,6]});
{
"acknowledged" : true,
"insertedId" : ObjectId("5f79fa9ed04cd9e2b3acbf05")
}
> db.numbers.find();
{ "_id" : ObjectId("5f79fa89d04cd9e2b3acbf03"), "number" : 1 }
{ "_id" : ObjectId("5f79fa89d04cd9e2b3acbf04"), "number" : 2 }
{ "_id" : ObjectId("5f79fa9ed04cd9e2b3acbf05"), "number" : [ 1, 2, 3, 4, 5, 6 ] }
>

Need help in querying mongodb

I have a a few documents that have the following structure. See attached image.
document structure
Each document includes an array of 'FileMeta' objects and each FileMeta object includes an array of 'StatusHistory' objects. I'm trying to get only the FileMetas that contain StatusCode equal to 4 and that the TimeStamp is greater than a certain datetime.
Tried the following query but it only returns the first FileMeta element of each document.
db.getCollection('Collection').find({'ExternalParams.RequestingApplication':'aaa.bbb'},
{ "FileMeta": { $elemMatch: { "StatusHistory":{ $elemMatch:{ "StatusCode": 4, "TimeStamp": { $gt: ISODate("2020-06-28T11:02:26.542Z")} } } } }} )
What am I doing wrong?
here is the document structure:
{
"_id" : ObjectId("5ef84e2ec08abf38b0043ab4"),
"FileMeta" : [
{
"StatusHistory" : [
{
"StatusCode" : 0,
"StatusDesc" : "New File",
"TimeStamp" : ISODate("2020-06-28T11:00:46.286Z")
},
{
"StatusCode" : 2,
"StatusDesc" : "stby",
"TimeStamp" : ISODate("2020-06-28T11:02:20.400Z")
},
{
"StatusCode" : 4,
"StatusDesc" : "success",
"TimeStamp" : ISODate("2020-06-28T11:02:26.937Z")
}
]
},
{
"StatusHistory" : [
{
"StatusCode" : 0,
"StatusDesc" : "New File",
"TimeStamp" : ISODate("2020-06-28T11:00:46.286Z")
},
{
"StatusCode" : 2,
"StatusDesc" : "stby",
"TimeStamp" : ISODate("2020-06-28T11:02:20.617Z")
},
{
"StatusCode" : 4,
"StatusDesc" : "success",
"TimeStamp" : ISODate("2020-06-28T11:02:26.542Z")
}
]
}
],
}
I want to return only the FileMeta objects that include a StatusHistory that match the following conditions: StatusCode = 4 and TimeStamp > SomeDateTime
Sorry for the delay, mate, I've been quite busy lately. Hope you already solved your problem. Anyway, I think that I found the solution.
As you can see on this link, the example shows that by default the $elemMatch operator returns the whole array in case of match on any element.
For instance, consider the following collection:
{ _id: 1, results: [ { product: "abc", score: 10 }, { product: "xyz", score: 5 } ] }
{ _id: 2, results: [ { product: "abc", score: 8 }, { product: "xyz", score: 7 } ] }
{ _id: 3, results: [ { product: "abc", score: 7 }, { product: "xyz", score: 8 } ] }
If you do the following query, for example:
db.survey.find(
{ results: { $elemMatch: { product: "xyz", score: { $gte: 8 } } } }
)
The output will be:
{ "_id" : 3, "results" : [ { "product" : "abc", "score" : 7 }, { "product" : "xyz", "score" : 8 } ] }
Not:
{ "_id" : 3, "results" : [{ "product" : "xyz", "score" : 8 }]}
That said, if you want to return only the document in the array that matches the specified query, you must use the db.collection.aggregate() function with the $unwind and $match operator.
The query below shall give you what you want.
Query:
db.collection.aggregate([
{"$unwind" : "$FileMeta"},
{"$unwind" : "$FileMeta.StatusHistory"},
{
"$match" : {
"FileMeta.StatusHistory.StatusCode" : 4,
"FileMeta.StatusHistory.TimeStamp" : {"$gte" : ISODate("2020-06-28T11:02:26.937Z")}
}
}
]).pretty()
Result:
{
"_id" : ObjectId("5ef84e2ec08abf38b0043ab4"),
"FileMeta" : {
"StatusHistory" : {
"StatusCode" : 4,
"StatusDesc" : "success",
"TimeStamp" : ISODate("2020-06-28T11:02:26.937Z")
}
}
}
One last tip. Consider changing your modeling to something that looks like the unwinded document, and remember that one document should be equivalent to one row in a normal relational database. So avoid storing information that should be on "several rows" on a single document.
Useful links:
The $elemMatch operator.
The $unwind operator.

Remove duplicates from MongoDB 4.2 data base

I am trying to remove duplicates from MongoDB but all solutions find fail.
My JSON structure:
{
"_id" : ObjectId("5d94ad15667591cf569e6aa4"),
"a" : "aaa",
"b" : "bbb",
"c" : "ccc",
"d" : "ddd",
"key" : "057cea2fc37aabd4a59462d3fd28c93b"
}
Key value is md5(a+b+c+d).
I already have a database with over 1 billion records and I want to remove all the duplicates according to key and after use unique index so if the key is already in data base the record wont insert again.
I already tried
db.data.ensureIndex( { key:1 }, { unique:true, dropDups:true } )
But for what I understand dropDups were removed in MongoDB > 3.0.
I tried also several of java script codes like:
var duplicates = [];
db.data.aggregate([
{ $match: {
key: { "$ne": '' } // discard selection criteria
}},
{ $group: {
_id: { key: "$key"}, // can be grouped on multiple properties
dups: { "$addToSet": "$_id" },
count: { "$sum": 1 }
}},
{ $match: {
count: { "$gt": 1 } // Duplicates considered as count greater than one
}}
],
{allowDiskUse: true} // For faster processing if set is larger
).forEach(function(doc) {
doc.dups.shift(); // First element skipped for deleting
doc.dups.forEach( function(dupId){
duplicates.push(dupId); // Getting all duplicate ids
}
)
})
and it fails with:
QUERY [Js] uncaught exception: Error: command failed: {
“ok“: 0,
“errmsg“ : “assertion src/mongo/db/pipeline/value.cpp:1365“.
“code“ : 8,
“codeName" : “UnknownError“
} : aggregate failed
I haven't change MongoDB settings, working with the default settings.
This is my input collection dups, with some duplicate data (k with values 11 and 22):
{ "_id" : 1, "k" : 11 }
{ "_id" : 2, "k" : 22 }
{ "_id" : 3, "k" : 11 }
{ "_id" : 4, "k" : 44 }
{ "_id" : 5, "k" : 55 }
{ "_id" : 6, "k" : 66 }
{ "_id" : 7, "k" : 22 }
{ "_id" : 8, "k" : 88 }
{ "_id" : 9, "k" : 11 }
The query removes the duplicates:
db.dups.aggregate([
{ $group: {
_id: "$k",
dups: { "$addToSet": "$_id" },
count: { "$sum": 1 }
}},
{ $project: { k: "$_id", _id: { $arrayElemAt: [ "$dups", 0 ] } } }
] )
=>
{ "k" : 88, "_id" : 8 }
{ "k" : 22, "_id" : 7 }
{ "k" : 44, "_id" : 4 }
{ "k" : 55, "_id" : 5 }
{ "k" : 66, "_id" : 6 }
{ "k" : 11, "_id" : 9 }
As you see the following duplicate data is removed:
{ "_id" : 1, "k" : 11 }
{ "_id" : 2, "k" : 22 }
{ "_id" : 3, "k" : 11 }
Get the results in an array:
var arr = db.dups.aggregate([ ...] ).toArray()
The arr has the array of the documents:
[
{
"k" : 88,
"_id" : 8
},
{
"k" : 22,
"_id" : 7
},
{
"k" : 44,
"_id" : 4
},
{
"k" : 55,
"_id" : 5
},
{
"k" : 66,
"_id" : 6
},
{
"k" : 11,
"_id" : 9
}
]

Update element in array if exists else insert new element in that array in MongoDb

I'm having group of array of element in MongoDB as given below :
{
"_id" : 5,
"quizzes" : [
{
"wk" : 1,
"score" : 10
},
{
"wk" : 2,
"score" : 8
},
{
"wk" : 3,
"score" : 5
}
],
"play" : [
{
"wk" : 2,
"score" : 8
},
{
"wk" : 3,
"score" : 5
}
]
}
I am trying insert new record in array if not present and if record present in that array then update that array record.
Below is my MongoDB query.
db.push.update(
{ _id: 5 },
{ $push: { "quizzes": {"wk" : 6.0,"score" : 8.0},"play": {"wk" : 6.0,"score" : 8.0} } }
)
Every time when i execute this query it inserts new record in array but i want if record present then update that array.
Use $addToSet instead of $push.
db.push.update(
{ _id: 5 },
{ $addToSet: { "quizzes": {"wk": 6.0, "score": 8.0}, "play": {"wk": 6.0, "score": 8.0} } }
)
EDIT:
There is no simple built-in approach for conditional sub-document update in an array field, by specific property. However, a small trick can do the job by executing two commands in sequence.
For example: If we want to update the quizzes field with the object { "wk": 7.0, "score": 8.0 }, we can do it in two steps:
Step-1: $pull out sub-documents from the quizzes array where "wk": 7.0. (Nothing happens if matching sub-document not found).
db.push.update(
{ _id: 5 },
{ $pull: { "quizzes": { "wk": 7.0 } } }
)
Step-2: $addToSet the sub-document.
db.push.update(
{ _id: 5 },
{ $addToSet: { "quizzes": {"wk": 7.0, "score": 8.0} } }
)
You can combine the above two update commands using the bulk.find().update()
I assume what you want to do is,
add a property to an element of the array based on the same element's different property's value.
You can use the updateOne() function with the $(update) operator, please refer the document mongodb documentation for $(update).
Let's say you want to add a property description to the array element where the value of wk is 1 (assuming its some kind of key based on what you have provided)
Input: Your data collection given in the question.
Mongo Command:
db.<your_collection_name>.updateOne(
{
"_id":5,
"quizzes.wk":1
},
{
$set:{
"quizzes.$.description":"I am a new property"
}
});
Output:
{
"_id" : 5,
"quizzes" : [
{
"wk" : 1,
"score" : 10,
"description" : "I am a new property"
},
{
"wk" : 2,
"score" : 8
},
{
"wk" : 3,
"score" : 5
}
],
"play" : [
{
"wk" : 2,
"score" : 8
},
{
"wk" : 3,
"score" : 5
}
]
}
Note: It will only change one entry of an array, the one which is matched first.

Adding item to an array in Typescript

I am working in TypeScript with MongoDB and I am trying to add an item to an array.
registeredUsers.update({ guid: ObjectId(req.cookies._id) }, {
$addToSet: {
favorites: [comicID]
}
});
This is the code I have currently, and I am trying to add the comicID to an array called favorites that is in registeredUsers. At the moment it does not seem to be adding to the array at all, so when I try to look at the array it is empty.
Your code seems fine. I suspect the reason you are not getting updates is because your search condition is not matching any documents.
Have you tried the following statement:
db.registeredUsers.find({ guid: ObjectId(req.cookies._id) })
If your search condition is valid, you should get the "desired" document back.
Here are my examples:
First create a doc.
> db.registeredUsers.insert({ "mycustom_id":1, "favorites":[1,2,3]})
WriteResult({ "nInserted" : 1 })
Check to see if the doc looks right.
> db.registeredUsers.find()
{ "_id" : ObjectId("56f88aa1d14e4832a027f625"), "mycustom_id" : 1, "favorites" : [ 1, 2, 3 ] }
Do an update based on one of the columns that I defined.
> db.registeredUsers.update({"mycustom_id" : 1}, { $addToSet: { favorites: 5}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
Check the update results:
> db.registeredUsers.find()
{ "_id" : ObjectId("56f88aa1d14e4832a027f625"), "mycustom_id" : 1, "favorites" : [ 1, 2, 3, 5 ] }
Now, do an update based on the "_id" field.
> db.registeredUsers.update({"_id": ObjectId("56f88aa1d14e4832a027f625") }, { $addToSet: { favorites: 6}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
Checking the result:
> db.registeredUsers.find()
{ "_id" : ObjectId("56f88aa1d14e4832a027f625"), "mycustom_id" : 1, "favorites" : [ 1, 2, 3, 5, 6 ] }
Another example where the update is expected to create the array:
> db.registeredUsers.insert({"a": 1})
WriteResult({ "nInserted" : 1 })
> db.registeredUsers.find()
{ "_id" : ObjectId("56f88aa1d14e4832a027f625"), "mycustom_id" : 1, "favorites" : [ 1, 2, 3, 5, 6 ] }
{ "_id" : ObjectId("56f88be6d14e4832a027f626"), "a" : 1 }
Updating based on "_id" again in a situation where the array favorites does not exist prior to the update
> db.registeredUsers.update({"_id" : ObjectId("56f88be6d14e4832a027f626")}, { $addToSet: { favorites: 6}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
Check the results:
> db.registeredUsers.find()
{ "_id" : ObjectId("56f88aa1d14e4832a027f625"), "mycustom_id" : 1, "favorites" : [ 1, 2, 3, 5, 6 ] }
{ "_id" : ObjectId("56f88be6d14e4832a027f626"), "a" : 1, "favorites" : [ 6 ] }
As you can see my queries are identical to yours. You are comparing "guid" and I am comparing "_id". I am suspicious that "guid" does not contain what you expect it to contain, which leads to your "search condition" to the update to return 0 documents, consequently

Resources