Nested Mongodb Query needing multi seareshes - mongodb

Hi I'm new with mongodb and I have to write a query to check if each field in database has a parent, if yes change the hasChild field to "true" else leave it be the default value of false.
Here is sample of one Document.
{
"_id":"5e19ef611052250ba51abb7b",
"name":"shampoo",
"price":12.99,
"hasChild":true
}
{
"_id":"5e1b268d25fe046b3518dcb9",
"name":"conditioner",
"price":"13.99",
"parent":"5e19ef611052250ba51abb7b",
"hasChild":false
}
I though a query which could ,while updating a filed search in other documents, check whether they have a parent id same as current documents id and set true/false would work out. but I couldn't find a way to write it. I would appreciate any ideas.
note that "_id" and "parent" are ObjectId.

You can do it with aggregation framework.
Something like this with $addFields and $cond:
db.getCollection("yourcollection").aggregate(
[
{
$addFields: {
"hasChild": {
$cond: { if: { $eq: [ "$parent", undefined ] }, then: false, else: true }
}
}
},
]
);

Related

Appending a string to all values in an array field in MongoDB

I have a collection filled with docs that contain an "ip_addresses" field, which is an array of strings (IPs). I want to append '/32' to all of these values in all of my docs that don't already have a CIDR range suffix.
Here's my issue:
I don't know how to use the current value of the IP which is being iterated on.
Even if I did, $concat doesn't seem to work and throws an error even with placeholder values (as in the query below) - The dollar ($) prefixed field '$concat' in 'ip_addresses.0.$concat' is not valid for storage.
Here is my current query which throws the error:
db.getCollection('docs').update(
{},
{ $set: { "ip_addresses.$[ip]": { "$concat": [ "1.1.1.1", "/32" ] } } },
{
arrayFilters: [ { "ip": { $not: /.+\/\d{1,2}/ } } ],
multi: true
}
)
I'd appreciate help using the current values in the array in the $concat command and resolving the error.
Let's tackle each of the problems.
2 - $concat is an aggregation operator, hence it cannot be used for an update. you can view the list of available operators for an update here. You might notice that none of these "work" on actual document values which brings me back to the first question you had.
1 - In order you use a current document values within an update you have to use pipelined updates which is only available for Mongo v4.2+, if you're using a lesser Mongo version you have to fetch the documents into memory and do the update one by one in code. If you are on Mongo v4.2+ then the pipeline update syntax goes like this:
db.collection.updateMany(
{ip_addresses: {$exists: true}},
[
{
$set: {
ip_addresses: {
$map: {
input: '$ip_addresses',
as: 'ipAddress',
in: {
$cond: [
{
$regexMatch: {input: "$$ipAddress", regex: /.+\/\d{1,2}/}
},
'$$ipAddress',
{
$concat: [
'$$ipAddress',
'/32'
]
}
]
}
}
}
}
}
]
);

Find whether embedded document exists in array and get the parent document's a property value

I want to find whether embedded document exists in array property of parent document and get the parent document's a property value.
Think, I have a document like this
{
_id:1,
persons:[{name: "Jack", earning: 1000},{name: "Monica", earning: 2000}]
totalDebt:500
}
I want to find whether name=Jamal exists in person array's embedded document and with this the totalDebt of _id=1
The result will be like this:
{
totalDebt:500,
exists:false // as Jamal does not exists
}
How execute this in a single query ?
Yes, you can easily solve this by using one query. I solved this by one query using $cond, Look below code to get a brief understanding.
db.getCollection('collectionName').aggregate([
{
$project: {
_id: 0,
totalDebt: 1,
"exists": {
$cond: {
if: {
$in: [
"Jack",
"$persons.name"
]
},
then: true,
else: false
}
}
}
}
])
Initially, I have used $cond with $eq operator, it always returns false. When I use $in, it solved my problem.

MongoDb - add index on 'calculated' fields

I have a query that includes an $expr-operator with a $cond in it.
Basically, I want to have objects with a timestamp from a certain year. If the timestamp is not set, I'll use the creation date instead.
{
$expr: {
$eq: [
{
$cond: {
'if': {
TimeStamp: {
$type: 'null'
}
},
then: {
$year: '$Created'
},
'else': {
$year: '$TimeStamp'
}
}
},
<wanted-year>
]
}
}
It would be nice to have this query using a index. But is it possible to do so? Should I just add index to both TimeStamp and Created-fields? Or is it possible to create an index for a Year-field that doesn't really exist on the document itself...?
Not possible
Indexes are stored on disk before executing the query.
Workaround: On-Demand Materialized Views
You store in separate collection your calculated data (with indexes)
This can't be done today without precomputing that information and storing it in a field on the document. The closest alternative would probably be to use MongoDB 4.2's aggregation pipeline-powered updates to precompute and store a createdOrTimestamp field whenever your documents are updated. You could then create an index on createdOrTimestamp that would be used when querying for documents that match a certain year.
What this would look like when updating or after inserting your document:
db.collection.update({ _id: ObjectId("5e8523e7ea740b14fb16b5c3") }, [
{
$set: {
createdOrTimestamp: {
$cond: {
if: {$gt: ['$TimeStamp', null]},
then: '$TimeStamp',
else: '$Created'
}
}
}
}
])
If documents already exist, you could also send off an updateMany operation with that aggregation to get that computed field into all your existing documents.
It would be really nice to be able to define computed fields declaratively on a collection just like indexes, so that they take care of keeping themselves up to date!

How to add ObjectId property to each object of an array

I am have manually created an object in a Mongo collection:
{
"messages": [
{
"url":"http://test.test.com",
"message":"test message"
}
],
....other properties
}
I would like to add an _id:ObjectId() to each item of my messages array and for each document in the collection.
I tried:
collection.update({}, {
$set: {
'messages.$._id': ObjectId(),
},
}, { multi: true }
but this is not working. The Id is getting added when I add new ones going through Mongoose, but these were manually entered into mongo. Any help is appreciated.
Your syntax is correct, but in order to use the $ positional operator the array field must appear as part of the query document, check in documentation.
Try this:
db.collection.update({messages: {$exists: true }},
{$set: { 'messages.$._id': ObjectId() } },
{multi: true}
)

Mongodb query - apply condition to subfield only when field exists

I need to find a document either when a field doesn't exist or when a subfield of this field meets some condition.
A similar question was asked here: Mongodb query - apply condition only if field exists. I'll use the code from this answer to illustrate:
db.stackoverflow.find({
$or: [
{ howmuch: { $exists:false } },
{ 'howmuch.chocolate':5 }
]})
Of course when I do that I get an error when howmuch is undefined. I know I could test if 'howmuch.chocolate' exists but that wouldn't change anything. Is there a way to do that?
This should work:
db.stackoverflow.find({
$or: [
{ howmuch: { $exists:false } },
{ $and: [{ 'howmuch.chocolate': { $exists: true } } ,{ 'howmuch.chocolate':5 }]}
]})
According to the documentation, if the first expression in the $and array evaluates to false, the remaining expressions are not evaluated.