Retrieve all documents where _ids are in another collection - mongodb

I want to return all documents from the "usersessions" collection where _ids are in my "users" collection.
I tried the following:
db.usersessions.find( { "userId": { $in: (db.getCollection('users').find({},{"_id":1})) } } )
which returns an error:
Error: error: { "waitedMS" : NumberLong(0), "ok" : 0, "errmsg" : "$in needs an array", "code" : 2 }

As mentioned in the error message $in needs an array. You can use the distinct to return an array of _id from the "users" collection. The reason is that _id are unique within the collection.
var ids = db.getCollection('users').distinct('_id');
db.usersessions.find( { "userId": { "$in": ids } })

Related

How to remove an element from inner array of nested array pymongo using $ pull

Here is my news document structure
{
"_id" : ObjectId("5bff0903bd9a221229c7c9b2"),
"title" : "Test Page",
"desc" : "efdfr",
"mediaset_list" : [
{
"_id" : ObjectId("5bfeff94bd9a221229c7c9ae"),
"medias" : [
{
"_id" : ObjectId("5bfeff83bd9a221229c7c9ac"),
"file_type" : "png",
"file" : "https://aws.com/gg.jpg",
"file_name" : "edf.jpg"
},
{
"_id" : ObjectId("5bfeff83bd9a221229c7c9ad"),
"file_type" : "mov",
"file" : "https://aws.com/gg.mov",
"file_name" : "abcd.mov"
}
]
}
]}
The queries that i've tried are given below
Approach 1
db.news.find_and_modify({},{'$pull': {"mediaset_list": {"medias": {"$elemMatch" : {"_id": ObjectId('5bfeff83bd9a221229c7c9ac')}} }}})
Approach 2
db.news.update({},{'$pull': {"mediaset_list.$.medias": {"_id": ObjectId('5bfeff83bd9a221229c7c9ac')}} })
Issue we are facing
The above queries are removing entire elements inside 'mediaset_list' . But i only want to remove the element inside 'medias' matching object ID.
Since you have two nested arrays you have to use arrayFilters to indicate which element of outer array should be modified, try:
db.news.update({ _id: ObjectId("5bff0903bd9a221229c7c9b2") },
{ $pull: { "mediaset_list.$[item].medias": { _id: ObjectId("5bfeff83bd9a221229c7c9ad") } } },
{ arrayFilters: [ { "item._id": ObjectId("5bfeff94bd9a221229c7c9ae") } ] })
So item is used here as a placeholder which will be used by MongoDB to determine which element of mediaset_list needs to be modified and the condition for this placeholder is defined inside arrayFilters. Then you can use $pull and specify another condition for inner array to determine which element should be removed.
From #micki's mongo shell query (Answer above) , This is the pymongo syntax which will update all news document with that media id .
db.news.update_many({},
{
"$pull":
{ "mediaset_list.$[item].medias": { "_id": ObjectId("5bfeff83bd9a221229c7c9ad") } } ,
},
array_filters=[{ "item._id": ObjectId("5bfeff94bd9a221229c7c9ae")}],
upsert=True)

mongodb getting on simple $push The positional operator did not find the match needed from the query

I have this simple update api invocation :
this is my document :
{
"_id" : ObjectId("577a5b9a89xxx32a1"),
"oid" : {
"a" : 0,
"b" : 0,
"c" : NumberLong("1260351143035")
},
"sessions" : [
{
}
]
}
Then i try to insert 1 element into sessions array :
db.getCollection('CustomerInfo').update({"oid.c":1260351143035},{$push:{"sessions.$.asessionID":"test123"}})
but im getting this error:
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 16837,
"errmsg" : "The positional operator did not find the match needed from the query. Unexpanded update: sessions.$.asessionID"
}
})
using $set im getting the same error
As the error implies,
"The positional operator did not find the match needed from the query.
Unexpanded update: sessions.$.asessionID",
the positional operator will work if the array to be updated is also part of the query. In your case, the query only involves the embedded document oid. The best update operator to use in your case is the $set instead.
You can include the sessions array in the query, for example:
db.getCollection('CustomerInfo').update(
{
"oid.c": 1260351143035,
"sessions.0": {} // query where sessions array first element is an empty document
/* "sessions.0": { "$exists": true } // query where sessions array first element exists */
},
{
"$set": { "sessions.$.asessionID": "test123" }
}
)
As the documentation says, you can do as the follow:
db.getCollection('CustomerInfo').update(
{ "oid.c": 1260351143035 },
{ $push: {
"sessions": {
"asessionID":"test123"
}
}
}
)

Unwind array for documents that have a specified field

I have an application that simply takes and converts XML files to JSON and inserts them into a Mongo database. Inside each document of the collection I have an array VehicleEntry. Some of these VehicleEntries will have a tag called Pre-FlashDTC-IPCand I would like to pull all of those entries. Since I want the individual entries from the array, I used the unwind operator:
db.Vehicles.aggregate( [
{ $unwind : { path : "$VehicleEntry" } },
{ $match : { "$VehicleEntry.Pre-FlashDTC-IPC" : { $exists: true } } }
] );
I have tried this both with unwind first and match first, but neither work. I get an error:
{ "serverUsed": "localhost:27017", "ok": 0.0, "errmsg": "bad query: BadValue unknown top level operator: $VehicleEntry.Pre-FlashDTC-IPC", "code": 16810}
I thought the $exists operator would work to ensure I only returned elements that do have that value, but that doesn't appear to be the case. How can I correct this query?
Consider the following sample document:
{
"VehicleEntry" : [
{
"BatteryStatus" : "GOOD",
"Pre-FlashDTC-IPC" : "U100",
"VehicleStatus" : "PASSED"
},
{
"BatteryStatus" : "GOOD",
"VehicleStatus" : "PASSED"
}
],
"project_id" : "1234"
}
There is some consistent information in the array, such as Battery and Vehicle status, but some have extra information like the first array item. I want to get the individual array items (hence the need for unwind) where this value exists. Therefore my expected results are:
{
"BatteryStatus" : "GOOD",
"Pre-FlashDTC-IPC" : "U100",
"VehicleStatus" : "PASSED"
},
Omit the "$" in your match-clause
{ $match : { "VehicleEntry.Pre-FlashDTC-IPC" : { $exists: true } } }
The $ in front of a field-name is only needed when you want to access the value of a field in an aggregation operation.

Getting only Sub Elements [duplicate]

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 8 years ago.
If I have the following document
{
"_id" : ObjectId("54986d5531a011bb5fb8e0ee"),
"owner" : "54948a5d85f7a9527a002917",
"type" : "group",
"deleted" : false,
"participants" : [
{ "_id": "54948a5d85f7a9527a002917", "name": "user1" },
{ "_id": "5491234568f7a9527a002918", "name": "user2" },
{ "_id": "5491234568f7a9527a002918", "name": "user3" },
{ "_id": "1234567aaaa7a9527a002917", "name": "user2" }
]
}
How would I get all records where name = 'user2'?
I'm trying the followoing:
db.users.find({ _id: ObjectId('54a7103b1a57eee00bc0a9e4') },
{ 'participants.$.name': 'user2') }).pretty()
...and I get the following:
error: {
"$err" : "Can't canonicalize query: BadValue Positional projection 'participants.$.name' does not match the query document.",
"code" : 17287
}
Though the positional operator($) would give you the first matching element from the participant array. If you need all the participants in with the name user2, you need to aggregate the results.
Match the document with the required _id.
Use the redact operator to only keep all the sub documents that have
participants, who have their name as user2.
Code:
var search = "user2";
db.users.aggregate([
{$match:{"_id":ObjectId("54986d5531a011bb5fb8e0ee")}},
{$redact:{$cond:[{$eq:[{$ifNull:["$name",search]},search]},
"$$DESCEND",
"$$PRUNE"]}},
{$project:{"participants":1,"_id":0}} // set _id:1, if you need the _id.
])
o/p:
{
"participants" : [
{
"_id" : "5491234568f7a9527a002918",
"name" : "user2"
},
{
"_id" : "1234567aaaa7a9527a002917",
"name" : "user2"
}
]
}
Coming to your query,
db.users.find({ _id: ObjectId('54a7103b1a57eee00bc0a9e4') },
{ 'participants.$.name': 'user2'}).pretty()
The positional operator can be applied only on the array, that is referred in the query document of the find function. The above query document doesn't have a reference to the array named participants and only refers to the _id field to match a document. Hence you get the error.
From the docs,
The field being limited must appear in the query document
So, changing the query to include the participants array in the query document would fix the error.
db.users.find({ "_id":ObjectId('54a7103b1a57eee00bc0a9e4'),
"participants.name": "user2"
},
{"participants.$.name":"user2"}).pretty()
But it would return you only the first participant that has matched the criteria in the query document.
From the docs,
Use $ in the projection document of the find() method or the findOne()
method when you only need one particular array element in selected
documents.
o/p:
{
"_id" : ObjectId("54986d5531a011bb5fb8e0ee"),
"participants" : [
{
"_id" : "5491234568f7a9527a002918",
"name" : "user2"
}
]
}

In a Mongo collection, how do you query for a specific object in an array?

I'm trying to retrieve an object from an array in mongodb. Below is my document:
{
"_id" : ObjectId("53e9b43968425b29ecc87ffd"),
"firstname" : "john",
"lastname" : "smith",
"trips" : [
{
"submitted" : 1407824585356,
"tripCategory" : "staff",
"tripID" : "1"
},
{
"tripID" : "2",
"tripCategory" : "volunteer"
},
{
"tripID" : "3",
"tripCategory" : "individual"
}
]
}
My ultimate goal is to update only when trips.submitted is absent so I thought I could query and determine what the mongo find behavior would look like
if I used the $and query operator. So I try this:
db.users.find({
$and: [
{ "trips.tripID": "1" },
{ "trips": { $elemMatch: { submitted: { $exists: true } } } }
]
},
{ "trips.$" : 1 } //projection limits to the FIRST matching element
)
and I get this back:
{
"_id" : ObjectId("53e9b43968425b29ecc87ffd"),
"trips" : [
{
"submitted" : 1407824585356,
"tripCategory" : "staff",
"tripID" : "1"
}
]
}
Great. This is what I want. However, when I run this query:
db.users.find({
$and: [
{ "trips.tripID": "2" },
{ "trips": { $elemMatch: { submitted: { $exists: true } } } }
]
},
{ "trips.$" : 1 } //projection limits to the FIRST matching element
)
I get the same result as the first! So I know there's something odd about my query that isn't correct. But I dont know what. The only thing I've changed between the queries is "trips.tripID" : "2", which in my head, should have prompted mongo to return no results. What is wrong with my query?
If you know the array is in a specific order you can refer to a specific index in the array like this:-
db.trips.find({"trips.0.submitted" : {$exists:true}})
Or you could simply element match on both values:
db.trips.find({"trips" : {$elemMatch : {"tripID" : "1",
"submitted" : {$exists:true}
}}})
Your query, by contrast, is looking for a document where both are true, not an element within the trips field that holds for both.
The output for your query is correct. Your query asks mongo to return a document which has the given tripId and the field submitted within its trips array. The document you have provided in your question satisfies both conditions for both tripIds. You are getting the first element in the array trips because of your projection.
I have assumed you will be filtering records by the person's name and then retrieving the elements inside trips based on the field-exists criteria. The output you are expecting can be obtained using the following:
db.users.aggregate(
[
{$match:
{
"firstname" : "john",
"lastname" : "smith"
}
},
{$unwind: "$trips"},
{$match:
{
"trips.tripID": "1" ,
"trips.submitted": { $exists: true }
}
}
]
)
The aggregation pipeline works as follows. The first $match operator filters one document (in this case the document for john smith) The $unwind operator in mongodb aggregation unwinds the specified array (trips in this case), in effect denormalizing the sub-records associated with the parent records. The second $match operator filters the denormalized/unwound documents further to obtain the one required as per your query.