Delete documents where value is not an array - mongodb

It seems that mongodb allows the same syntax for deleting documents where the value is a single value, and whenever the value is present in a collection:
This
db.SomeCollection.deleteMany({UserId: 12345});
Can affect both {UserId: 12345} and {UserId: [12345, 67895, 87897]}.
I understand that this schema isn't ideal; we are using a singular UserId prop to denote both a single id int, and an array of ints. However, I've inherited a database that makes use of mongodb's dynamic nature, if you catch my drift.
What is the safest way to execute a deleteMany query, specifying I only want to delete documents where the value is a single integer?

You can use $type operator to check field type but it's not so obvious in this case. You can't check if UserId is of type double because MongoDB will indicate that array of doubles is a double as well (more here). So you need to invert your logic and check if documents you're trying to remove are not an array (type 4):
db.SomeCollection.deleteMany({ $and: [ { UserId: 12345 }, { UserId: { $not: { $type: 4 } } } ] })

Related

How to find in MongoDB by last 4 chars in ObjectID?

I don't want to expose the full object ID to the client, instead I want to show him only a short of the last 4 chars of the actual object ID of an entity in the collection.
For example: ObjectId("5fcca5d997239a74da0d67a9") will become just 67a9
So it will be much easier to "talk" with ids of documents instead of the full object it.
Then I need to find the document in the DB using only the 67a9.
Is this possible and how?
According to this issue in Jira the resolution is "Won't fix".
ObjectId is not a String, is another object, so $regex is no possible.
Check this example where $regex works ok when _id is an String but not an ObjectId.
So one possible option is duplicate every field _id in another field called id or whatever where the id is in String format.
Then, you can do this query:
db.collection.find({
"_id": {
"$regex": "67a9$"
}
})
Example here where I've added more _id fields that not match the pattern
As pointed out, regex won't work on an ObjectId. But there is an easy workaround. Just use aggregation to first convert your ObjectId into a string and then match it.
db.collection.aggregate([
{
$addFields: {
tempId: { $toString: '$_id' },
}
},
{
$match: {
tempId: { $regex: "67a9"}
}
}
])
Obviously not a great solution to use on very large collections.

MongoDB: concatinate multiple number values to string

I have a document (inside aggregation, after $group stage) which have an object (but I could form array, if I needed it to) with number values.
MongoPlayground example with data and my aggregate query available here.
And I want to make a new _id field during next $project stage, consisted of this three number values, like:
item_id | unix time | pointer
_id: 453435-41464556645#1829
The problem is, that when I am trying to use $concat, the query returns me an error like:
$concat only supports strings, not int
So here is my question: is it possible to achieve such results? I have seen the relevant question MongoDB concatenate strings from two fields into a third field, but it didn't cover my case.
The $concat only concatenate strings, these fields $_id.item_id contains int value and $_id.last_modified double value,
The $toString converts a value to a string,
_id: {
$concat: [
{
$toString: "$_id.item_id"
},
" - ",
{
$toString: "$_id.last_modified"
}
]
}
Playground: https://mongoplayground.net/p/SSlXW4gIs_X

How to update an integer value in Mongodb while preserving type

I want to include some values by a percent.
i.e. give a document with "A": 1700 I want to update it to A: (1700* 1.15)
{$mul: {"A": 1.15}}
but doing so with $mul I get weird results for some values, like 1954.99999998 and it changes the type to a double.
Does a valid syntax exist for an operation like this?
{
$set: {
"A" : {$trunc: ("A" * 1.15)}
}
}
this is not value either
{
$set: {
"A" : NumberInt("A" * 1.15)
}
}
If you take a look into documentation of $mul you will see that this operator can change the element data type and you can't controll it. Unfortunately you can't use any arithmetic operations directly in $set operator. In this case (when you want to apply the additional operations to multiplied value) you need read the element value, apply your custom operations and then save it into DB.

How to read a specific key-value pair from mongodb collection

If I have a mongodb collection users like this:
{
"_id": 1,
"name": {
"first" : "John",
"last" :"Backus"
},
}
How do I retrieve name.first from this without providing _id or any other reference. Also, is it possible that pulling just the `name^ can give me the array of embedded keys (first and last in this case)? How can that be done?
db.users.find({"name.first"}) didn't work for me, I got a:
SyntaxError "missing: after property id (shell):1
The first argument to find() is the query criteria whereas the second argument to the find() method is a projection, and it takes the form of a document with a list of fields for inclusion or exclusion from the result set. You can either specify the fields to include (e.g. { field: 1 }) or specify the fields to exclude (e.g. { field: 0 }). The _id field is implicitly included, unless explicitly excluded.
In your case, db.users.find({name.first}) will give an error as it is expected to be a search criteria.
To get the name json :
db.users.find({},{name:1})
If you want to fetch only name.first
db.users.find({},{"name.first":1})
Mongodb Documentation link here
To fetch all the record details:
db.users.find({"name.first":""})
To fetch just the name or specific field:
db.users.find({{},"name.X":""});
where X can be first, last .
dot(.) notation can be used if required to traverse inside the array for key value pair as
db.users.find({"name.first._id":"xyz"});
In 2022
const cursor = db
.collection('inventory')
.find({
status: 'A'
})
.project({ item: 1, status: 1 });
Source: https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results/

How to Create a nested index in MongoDB?

A. How do I index nested and all of it's values?
B. How do I index valuetwo?
{
id: 00000,
attrs: {
nested:{
value: value1,
valuetwo: value2,
}
}
}
I've looked here: http://www.mongodb.org/display/DOCS/Indexes, and the docs to my knowledge, aren't clear about indexing things that aren't nested.
You'd create them just as if you were creating an index on a top level field:
db.collection.createIndex({"attrs.nested.value": 1})
You do need to explicitly create indexes on each field.
MongoDB automatically creates a multikey index if any indexed field is an array; you do not need to explicitly specify the multikey type.
This will work for both the scenario's
db.coll.createIndex( { "addr.pin": 1 } )
Scenario 1 nested OBJECTS
{
userid: "1234",
addr: {
pin:"455522"
}
},
{
userid: "1234",
addr: {
pin:"777777"
}
}
Scenario 2 nested Arrays
{
userid: "1234",
addr: [
{ pin:"455522" },
{ pin:"777777" },
]
}
https://docs.mongodb.com/manual/core/index-multikey/
A. to index all the properties in "nested" you will have to index them separately:
db.collection.createIndex({"attrs.nested.value": 1});
db.collection.createIndex({"attrs.nested.valuetwo": 1});
This can be done in one command with:
db.collection.createIndexes([{"attrs.nested.value": 1}, {"attrs.nested.valuetwo": 1}]);
B. to index just "valuetwo":
db.collection.createIndex({"attrs.nested.valuetwo": 1})
Use createIndex over ensureIndex as ensureIndex is Deprecated since version 3.0.0
It looks like there may have been a new feature to address this since these answers have been written.
Mongodb has wildcard indexes that allow you to create an index for a field that contains varying keys.
From the documentation:
If the field is a nested document or array, the wildcard index recurses into the document/array and indexes all scalar fields in the document/array.
So the index in the question would be constructed like this:
db.collections.createIndex({ "attrs.nested.$**": 1 })
You can read more about this here!
The previous answers nearly gave me a heart attack.