Find all matching elements in the array - database

Can someone please help me with this query ??
Query >>> Find all warehouses that keep item "Planner" and having in-stock quantity less than 20
This is the sample document in the items collection of the Inventory database :
{
"_id" : ObjectId("6067640da9a907175caaca34"),
"id" : 101,
"name" : "Planner",
"status" : "A",
"height" : 12,
"tags" : [
"mens",
"womens"
],
"warehouses" : [
{
"name" : "Phoenix",
"quantity" : 25
},
{
"name" : "Quickshift",
"quantity" : 15
},
{
"name" : "Poona",
"quantity" : 10
}
]
}
This is what I have tried doing :
db.items.find({"name":"Planner","warehouses.quantity":{"$lt":20}},{"warehouses":1,"_id":0}).pretty()
But it gives me the result as
{
"warehouses" : [
{
"name" : "Phoenix",
"quantity" : 25
},
{
"name" : "Quickshift",
"quantity" : 15
},
{
"name" : "Poona",
"quantity" : 10
}
]
}

Demo - https://mongoplayground.net/p/IpD5ypWSZyt
Use aggregation query
db.collection.aggregate([
{ $match: { "name": "Planner" } },
{ $unwind: "$warehouses" }, // break into individual documents
{ $match: { "warehouses.quantity": { $lt: 20 } } }, // query the data
{ $group: { _id: "_id", warehouses: { $push: "$warehouses" } } } // join them back
])
Demo - https://mongoplayground.net/p/pdTY0IkIqgF
Use $elemMatch only if you think there will be only 1 array element matching per document
The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.
The $elemMatch operator limits the contents of an field from the query results to contain only the first element matching the $elemMatch condition.
db.collection.find({
"name":"Planner",
"warehouses": { "$elemMatch": { "quantity": { $gt: 20 } } }
},
{ "warehouses.$": 1})
https://docs.mongodb.com/manual/reference/method/db.collection.find/#find-projection

Related

How update my objects in a document's array in mongoDB

i have the following collection, I have one question about:
{
"_id" : ObjectId("123456789"),
"user_id" : 123456,
"total" : 100,
"items" : [
{
"item_name" : "my_item_one",
"price" : 30
},
{
"item_name" : "my_item_two",
"price" : 30
},
{
"item_name" : "my_item_three",
"price" : 30
}
]
}
how i can update all price concurrently, (price=50 in all objects)
thanks for help
You can use positional all operator to update all elements in the array
db.collectionName.update(
{ },
{ $set: { "items.$[].price" : 50 } },
{ multi: true }
)

How to update an array and pull a nested element from same array

With the following document:
{
"_id" : "123",
"firstArray" : [
{
"_id" : "456",
"status" : "open",
"nestedArray" : [
{
"_id" : "100",
"quantity" : 10
},
{
"_id" : "101",
"quantity" : 10
},
{
"_id" : "102",
"quantity" : 10
}
},
{
"_id" : "789",
"status" : "open",
"nestedArray" : [
{
"_id" : "200",
"quantity" : 10
},
{
"_id" : "201",
"quantity" : 10
},
{
"_id" : "202",
"quantity" : 10
}
}
]
}
How can I update the quantity by 20 of the nested ID 101 element and pull the one with the ID 201 from the same MongoDB query ?
I am trying to do that in Java with $set and $pull operator and I'm stuck with the following error:
[BulkWriteError{index=0, code=40, message='Update created a conflict
at 'firstArray.0.nestedArray'', details={}}]
MongoDB doesn’t allow multiple operations on the same property in the same update call. This means that the two operations must happen in two individual queries.
The first solution is you can write 2 seperate queries for both the operations.
The second solution is you can try update with aggregation pipeline, starting from MongoDB 4.2,
$map to iterate loop of firstArray
$filter to iterate loop of nestedArray and remove _id: "201" record
$map to iterate loop of above filtered nestedArray
$cond check condition if _id: "101" then return new quantity otherwise return current
$mergeObjects to merge current object with updated properties
db.collection.update(
{ "firstArray.nestedArray._id": "101" },
[{
$set: {
firstArray: {
$map: {
input: "$firstArray",
in: {
$mergeObjects: [
"$$this",
{
nestedArray: {
$map: {
input: {
$filter: {
input: "$$this.nestedArray",
cond: { $ne: ["$$this._id", "201"] }
}
},
in: {
_id: "$$this._id",
quantity: {
$cond: [
{ $eq: ["$$this._id", "101"] },
20,
"$$this.quantity"
]
}
}
}
}
}
]
}
}
}
}
}
])
Playground

Can $in and $or replace each other in MongoDB?

Can $in and $or replace each other in MongoDB?
db.restaurants.find(
{
"borough" :{$in :["Staten Island","Queens","Bronx","Brooklyn"]}},
{
"restaurant_id" : 1,
"name":1,"borough":1,
"cuisine" :1
}
);
db.restaurants.find(
{
"borough": "Bronx" ,
$or : [
{ "cuisine" : "American " },
{ "cuisine" : "Chinese" }
]
}
);
Here I observe that both these queries require us to choose from some options:
Does it make sense to replace $in in the first query with $or as follows:
db.restaurants.find(
{ $or: [{ borough: 'Staten Island',
borough: 'Queens',
borough: 'Bronx',
borough: 'Brooklyn' }],
{ _id : 1,
name: 1,
borough : 1,
cuisine : 1
}
})
Are $in and $or replaceable?
Update:
I tried to use two queries in a hope to get identical results:
Why is the second query selecting two rows of status 'D' only?
> db.inventory.find( {status : { $in: [ 'A', 'D'] }}, {item:1, status: 1})
{ "_id" : ObjectId("5eb67598bee5213484d45087"), "item" : "journal", "status" : "A" }
{ "_id" : ObjectId("5eb67598bee5213484d45088"), "item" : "notebook", "status" : "A" }
{ "_id" : ObjectId("5eb67598bee5213484d45089"), "item" : "paper", "status" : "D" }
{ "_id" : ObjectId("5eb67598bee5213484d4508a"), "item" : "planner", "status" : "D" }
{ "_id" : ObjectId("5eb67598bee5213484d4508b"), "item" : "postcard", "status" : "A" }
>
>
> db.inventory.find( {$or: [ {status: 'A', status: 'D'} ] }, {item:1, status: 1})
{ "_id" : ObjectId("5eb67598bee5213484d45089"), "item" : "paper", "status" : "D" }
{ "_id" : ObjectId("5eb67598bee5213484d4508a"), "item" : "planner", "status" : "D" }
>
From their official documentation itself or-versus-in :
When using $or with that are equality checks for the
value of the same field, use the $in operator instead of the $or
operator.
If you've docs like below :
[
{
"price": 100
},
{
"price": 200
},
{
"price": 300
},
{
"price": 400
},
{
"price": 500
}
]
If you wanted to get docs where price is equal to 100 or 500, query like :
db.collection.find({ price: { $in: [ 100, 500 ] } })
By doing like above, query is simple & clean. You can also use $or instead of $in but why would you loose shorthand notation and try to make your query look bulky by adding more objects of same field again and again ?
By default if you wanted to do logical OR on two different operators you would use $or, But when to use $or on same field :
db.collection.find({ $or: [ { price: { $lt: 200 } }, { price: { $gt: 400 } } ] })
As like above when you've multiple different conditions to match on same field you'll use it.
These two queries yield same result when executed but when you use $in - if input values are straight numbers or can be strings or other types where input values will exactly match with values of price field in docs, but when you use $or you're checking for different conditions on same field.
Test : mongoplayground

Conditionally remove Subdocument nested inside array of document MongoDB

I have a collection with document like this:
{
"_id" : "ABC",
"Name" : "Rajesh",
"createstmp" : ISODate("2015-06-22T17:09:16.705Z"),
"updstmp" : ISODate("2015-06-22T19:31:53.527Z"),
"AvgValue" : "65",
"PreValues" : [
{
"Date" : 20150709,
"Rate" : [
{
"Time" : 1566,
"value" : 60
},
{
"Time" : 1500,
"value" : 400
},
{
"Time" : 1400,
"value" : 100
},
{
"Time" : 1500,
"value" : 103
}
]
}
]
}
I want to remove the duplicate doc for a particular Date value
eg If Time value is 1500, I need to pull the document and push it the new value for (Value) in single bulk operation.
Here is my query
bulk.find({ "_id":"ABC" })
.update(
{
"_id": "ABC",
"PreValues": { "Date": 20150709 }
},
{
$pu‌​ll: { "PreValues": { "Rate": { "Time": 1000 } } }
}
);
bulk.find({ "_id":"ABC" })
.update(
{ "_id": "ABC","PreValues": { "Date": 20150709 }},
{ $pu‌​sh : {
"PreValues": { "Rate": { "Time": 1000,"Rating": 100 }}
}}
);
bulk.execute();
It's not a great idea to have nested arrays since the only thing you will ever be able to do atomically is $push or $pull. See the positional $ operator for details on why "nested arrays" are not good here, but basically you can only ever match the position of the "outer" array element.
And that is basically what you are missing here, and of course the proper "dot notation" for accessing the elements:
var bulk = db.ABA.initializeOrderedBulkOp();
bulk.find({ "_id": "ABC", "PreValues.Date": 20150709 })
.updateOne({ "$pull": { "PreValues.$.Rate": { "Time": 1500 } } })
bulk.find({ "_id": "ABC", "PreValues.Date": 20150709 })
.updateOne({ "$push": { "PreValues.$.Rate": { "Time": 1500, "Rating": 100 } } })
bulk.execute();
Which alters the document like so:
{
"_id" : "ABC",
"Name" : "Rajesh",
"createstmp" : ISODate("2015-06-22T17:09:16.705Z"),
"updstmp" : ISODate("2015-06-22T19:31:53.527Z"),
"AvgValue" : "65",
"PreValues" : [
{
"Date" : 20150709,
"Rate" : [
{
"Time" : 1566,
"value" : 60
},
{
"Time" : 1400,
"value" : 100
},
{
"Time" : 1500,
"Rating" : 100
}
]
}
]
}
That is the correct syntax for both statements there and sends both requests to the server at the same time with a single response.
Note that you need to inclide in the .find() query a field from the outer array to match. This is so the positional $ operator is populated with the matched index of that element and the operator knows which array element to act upon.

MongoDB: Find all matched array element from single document

I have a mongodb document like this,
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"eId": 101,
"about": "test",
"tags" : [
{"name": "jana"},
{"name":"bala"},
{"name":"jk"},
{"name":"charles"}
]
}
I need to find all matched array elements, where the name matched with given array.
db.coll.find({"tags": {"$elemMatch": {"name": {"$in": [/^jana/i, /^charles/i] }}}})
for this query i got the following result
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"tags" : [
{
"name" : "jana"
}
]
}
$elemMatch query only return the first matched element, but i want all the matched array element like this,
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"tags" : [
{
"name" : "jana"
},
{
"name" : "charles"
}
]
}
Is it possible to get the result like this?
note: i don't want any others fields, I want only the matched array elements along with _id
You can use MongoDB Aggregation Pipeline:
db.coll.aggregate([
{'$unwind': '$tags'},
{'$match':
{"tags.name":
{"$in": [/^jana/, /^charles/i] }
}
},
{'$group':
{
'_id': '$_id',
'tags':
{'$push': '$tags'}
}
}
])
Result : -
{
"result" : [
{
"_id" : ObjectId("5538b214706a90c718f75a41"),
"tags" : [
{
"name" : "jana"
},
{
"name" : "charles"
}
]
}
],
"ok" : 1
}

Resources