Mongodb find wrong document - mongodb

You can see my document as bellow. How can I write a find for my uniq fields?
I need search a document where KEYMAP is (SNUM="3151" and "SKEY"="THR" and "SID"="ID_HUT")
{
"CID":"2",
"DESCRIPTION":"test",
"SECKEY":"test",
"API":{
"SMS":"http://api.php",
"DR":"http://api2.php"
},
"LOGS":{
"IN":"log_cid_in_1",
"OUT": "log_cid_out_1"
},
"KEYMAP":[
{"SNUM":"3151","SKEY":"THR", "SID":"ID_HUT"},
{"SNUM":"3152","SKEY":"ONE", "SID":"ID_XL"},
{"SNUM":"3153","SKEY":"TWO", "SID":"ID_INDO"}
]
}
db.content_provider_map.ensureIndex({"KEYMAP.SNUM":1,"KEYMAP.SKEY":1,"KEYMAP.SID":1},{unique:true});
db.mycollection.find({"KEYMAP.SNUM":"3151","KEYMAP.SKEY":"TWO","KEYMAP.SID":"ID_XL"});# not work. it find the document

I believe you want to use $elemMatch ( http://docs.mongodb.org/manual/reference/operators/#_S_elemMatch ) here like:
find({KEYMAP: {$elemMatch: {SNUM: "3151", SKEY: "TWO", SID: "ID_XL"}}})
Also unique indexes on subdocuments do not work the way you probably think they do. They create uniqueness across all documents not just that one document. If you want a unique index on that one document then you will need to use something like $addToSet or an upsert function on the subdocument.

Related

Delete documents from collection based on condition

I want to delete the documents from a collection(Collection1) which are not matching with other collection(Collection2).
collection1 document - {_id: <autogen>, items: [{key:'key', ...}}]
collection2 document - {_id: 'key', action: {key1:'key1', ...}}, {_id: 'non_matching_key', action: {key1:'key1', ...}}
Delete all the documents from collection2, where items.key in collection1 is not matching with _id in collection2. with reference to the above example, a document with key-value 'non_matching_key' should be delete from collection2. There would be similar documents in collection2 like the one with _id value 'non_matching_key'.
The approach I thought was for mark and sweep.
I will add the column in collection2 documents for matching ids(in collection2 with items.key in collection1). This is mark step
Delete all the documents from collection2 where newly added column do not exists. This is sweep step.
Could you please advise if there is a better way of doing this?
Thanks,
Not fully clear how your documents look like and what are the exact conditions, but you could do it like this:
var ids = db.collection1.find({}, { "item.key": 1 }).toArray();
db.collection2.deleteMany({ _id: { $nin: ids } });
So basically, you want to iterate over your collection 2 and get all the ids that are not in collection 1 and delete those or vice versa.
db.collection2.find({}).forEach((doc) => db.collection1.deleteOne({_id: {$ne: doc._id}}))
or
let idsToDelete = db.collection2.find({}).distinct('_id')
let deleteResponse = db.collection1.deleteMany({_id: {$nin: idsToDelete}})
SWAP the collection name in the case of the other way.
NOTE: The code is just to give an overview and is not tested.

Use $not or $ne in update query

Should I use $not or $ne in the query:
Mytable.update({ TheThing: Thing,
'UpdatedInfo.NewInfo': {$ne: ThisNewestInfo} }, {
$push: {
UpdatedInfo: {
TheDate: ThisDate,
NewInfo: ThisNewestInfo,
Past: OriginalInfo
}
}
},
function (err, result) {
if (err){
throw new Error(err.message);
}
}
If I only want to update the document when ThisNewestInfo is not already present in UpdatedInfo array, in NewInfo object element. Trying to understand the difference between $not and $ne.
And also:
If the document does not contain UpdatedInfofield in the beginning? How should I change the update query above? Meaning that if UpdatedInfodoes not exists it adds UpdatedInfo, and later on, say next day, checks if ThisNewestInfois not already present when updating document again.
It depends on your collection actually.
The main different between $ne and $not in this scenario is that, $not performs a logical disjunction. That is if your document didn't had an UpdatedInfo field, using $not would have pushed the document while using $ne nothing would have happened to that document.
So if all your document of collection has UpdatedInfo field, its better to go with $ne.
Edit
Based on your edit you mentioned UpdatedInfo might not be present in document. In such cases you should use $not. $ne wont be able to update docs that doesn't have UpdatedInfo field.
Remember like this: $not checks for presence of key as well as value, while $ne checks only for value and ignores document that doesn't have the particular key in query.

how to keep all field also do a $elemMatch projection in mongodb

Below is the document:
{
name:"james",
files:[
{name:"file1.txt", content:"..."},
{name:"file2.txt", content:"..."},
{name:"file3.txt", content:"..."}
],
status:4
}
Want to make a query and return:
all root fields include files
files only contain file2.txt info, coze content is very big, only interested in files2.txt
I know using $elemMatch in find query, as below:
db.collection.find(
{'files':{$elemMatch:{name:'file2.txt'}} },
{name:1, status:1, 'files.$':1 }
)
Is there any way to avoid list all the fields in projection and get all the key?
Requirement: ask for all field but not to list the key (don't list name:1, status:1)
Is this what you asking for namely, return all the fields, but for the files field return only "files2.txt" value ?
db.collection.find({ 'files':{$elemMatch:{name:'file2.txt'}} }, { 'files.$':1, name:1, status:1 } )

How to force MongoDB pullAll to disregard document order

I have a mongoDB document that has the following structure:
{
user:user_name,
streams:[
{user:user_a, name:name_a},
{user:user_b, name:name_b},
{user:user_c, name:name_c}
]
}
I want to use $pullAll to remove from the streams array, passing it an array of streams (the size of the array varies from 1 to N):
var streamsA = [{user:"user_a", name:"name_a"},{user:"user_b", name:"name_b"}]
var streamsB = [{name:"name_a", user:"user_a"},{name:"name_b", user:"user_b"}]
I use the following mongoDB command to perform the update operation:
db.streams.update({name:"user_name", {"$pullAll:{streams:streamsA}})
db.streams.update({name:"user_name", {"$pullAll:{streams:streamsB}})
Removing streamsA succeeds, whereas removing streamsB fails. After digging through the mongoDB manuals, I saw that the order of fields in streamsA and streamsB records has to match the order of fields in the database. For streamsB the order does not match, that's why it was not removed.
I can reorder the streams to the database document order prior to performing an update operation, but is there an easier and cleaner way to do this? Is there some flag that can be set to update and/or pullAll to ignore the order?
Thank You,
Gary
The $pullAll operator is really a "special case" that was mostly intended for single "scalar" array elements and not for sub-documents in the way you are using it.
Instead use $pull which will inspect each element and use an $or condition for the document lists:
db.streams.update(
{ "user": "user_name" },
{ "$pull": { "streams": { "$or": streamsB } }}
)
That way it does not matter which order the fields are in or indeed look for an "exact match" as the current $pullAll operation is actually doing.

How can I get all the doc ids in MongoDB?

How can I get an array of all the doc ids in MongoDB? I only need a set of ids but not the doc contents.
You can do this in the Mongo shell by calling map on the cursor like this:
var a = db.c.find({}, {_id:1}).map(function(item){ return item._id; })
The result is that a is an array of just the _id values.
The way it works in Node is similar.
(This is MongoDB Node driver v2.2, and Node v6.7.0)
db.collection('...')
.find(...)
.project( {_id: 1} )
.map(x => x._id)
.toArray();
Remember to put map before toArray as this map is NOT the JavaScript map function, but it is the one provided by MongoDB and it runs within the database before the cursor is returned.
One way is to simply use the runCommand API.
db.runCommand ( { distinct: "distinct", key: "_id" } )
which gives you something like this:
{
"values" : [
ObjectId("54cfcf93e2b8994c25077924"),
ObjectId("54d672d819f899c704b21ef4"),
ObjectId("54d6732319f899c704b21ef5"),
ObjectId("54d6732319f899c704b21ef6"),
ObjectId("54d6732319f899c704b21ef7"),
ObjectId("54d6732319f899c704b21ef8"),
ObjectId("54d6732319f899c704b21ef9")
],
"stats" : {
"n" : 7,
"nscanned" : 7,
"nscannedObjects" : 0,
"timems" : 2,
"cursor" : "DistinctCursor"
},
"ok" : 1
}
However, there's an even nicer way using the actual distinct API:
var ids = db.distinct.distinct('_id', {}, {});
which just gives you an array of ids:
[
ObjectId("54cfcf93e2b8994c25077924"),
ObjectId("54d672d819f899c704b21ef4"),
ObjectId("54d6732319f899c704b21ef5"),
ObjectId("54d6732319f899c704b21ef6"),
ObjectId("54d6732319f899c704b21ef7"),
ObjectId("54d6732319f899c704b21ef8"),
ObjectId("54d6732319f899c704b21ef9")
]
Not sure about the first version, but the latter is definitely supported in the Node.js driver (which I saw you mention you wanted to use). That would look something like this:
db.collection('c').distinct('_id', {}, {}, function (err, result) {
// result is your array of ids
})
I also was wondering how to do this with the MongoDB Node.JS driver, like #user2793120. Someone else said he should iterate through the results with .each which seemed highly inefficient to me. I used MongoDB's aggregation instead:
myCollection.aggregate([
{$match: {ANY SEARCHING CRITERIA FOLLOWING $match'S RULES} },
{$sort: {ANY SORTING CRITERIA, FOLLOWING $sort'S RULES}},
{$group: {_id:null, ids: {$addToSet: "$_id"}}}
]).exec()
The sorting phase is optional. The match one as well if you want all the collection's _ids. If you console.log the result, you'd see something like:
[ { _id: null, ids: [ '56e05a832f3caaf218b57a90', '56e05a832f3caaf218b57a91', '56e05a832f3caaf218b57a92' ] } ]
Then just use the contents of result[0].ids somewhere else.
The key part here is the $group section. You must define a value of null for _id (otherwise, the aggregation will crash), and create a new array field with all the _ids. If you don't mind having duplicated ids (according to your search criteria used in the $match phase, and assuming you are grouping a field other than _id which also has another document _id), you can use $push instead of $addToSet.
Another way to do this on mongo console could be:
var arr=[]
db.c.find({},{_id:1}).forEach(function(doc){arr.push(doc._id)})
printjson(arr)
Hope that helps!!!
Thanks!!!
I struggled with this for a long time, and I'm answering this because I've got an important hint. It seemed obvious that:
db.c.find({},{_id:1});
would be the answer.
It worked, sort of. It would find the first 101 documents and then the application would pause. I didn't let it keep going. This was both in Java using MongoOperations and also on the Mongo command line.
I looked at the mongo logs and saw it's doing a colscan, on a big collection of big documents. I thought, crazy, I'm projecting the _id which is always indexed so why would it attempt a colscan?
I have no idea why it would do that, but the solution is simple:
db.c.find({},{_id:1}).hint({_id:1});
or in Java:
query.withHint("{_id:1}");
Then it was able to proceed along as normal, using stream style:
createStreamFromIterator(mongoOperations.stream(query, MortgageDocument.class)).
map(MortgageDocument::getId).forEach(transformer);
Mongo can do some good things and it can also get stuck in really confusing ways. At least that's my experience so far.
Try with an agregation pipeline, like this:
db.collection.aggregate([
{ $match: { deletedAt: null }},
{ $group: { _id: "$_id"}}
])
this gona return a documents array with this structure
_id: ObjectId("5fc98977fda32e3458c97edd")
i had a similar requirement to get ids for a collection with 50+ million rows. I tried many ways. Fastest way to get the ids turned out to be to do mongoexport with just the ids.
One of the above examples worked for me, with a minor tweak. I left out the second object, as I tried using with my Mongoose schema.
const idArray = await Model.distinct('_id', {}, function (err, result) {
// result is your array of ids
return result;
});