MongoDb regex search in array objects - mongodb

I have the following collection:
{
"invoice": {
"data": [{
"name": "VOUCHERNUMBER",
"value": "59302311"
}, {
"name": "VOUCHERDATE",
"value": "2020-02-20"
}
]
}
},
{
"invoice": {
"data": [{
"name": "VOUCHERNUMBER",
"value": "59112389"
}, {
"name": "VOUCHERDATE",
"value": "2020-02-20"
}
]
}
},
{
"invoice": {
"data": [{
"name": "VOUCHERNUMBER",
"value": "59302378"
}, {
"name": "VOUCHERDATE",
"value": "2020-02-11"
}
]
}
}
My task is to build a query that find all invoices which invoicenumbers includes "11" (or any other substring).
So I built the following statement:
{"invoice.data.name": "VOUCHERNUMBER", "invoice.data.value": {$regex : "11"} }
I'm expecting a result of the first two objects, but because of the second value in the third object, mongodb returns me all three objects. Then I tried
{$and : [{"invoice.data.name": "VOUCHERNUMBER"}, {"invoice.data.value": {$regex : "11"}}]}
with the same result ...
So I'm running out of ideas. Is there a solution to search for the string only in the value field where the corresponding "name" field contains "VOUCHERNUMBER"?

You need $elemMatch.
The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.
db.collection.find({
"invoice.data": {
"$elemMatch": {
"name": "VOUCHERNUMBER",
"value": {
$regex: "11"
}
}
}
})
Sample Mongo Playground

Related

How to use an existing field value with $push in MongoDb?

I have the following Mongo collection
{
"_id": ObjectId("5524d12d2702a21830bdb8e5"),
"code": "Apple",
"name": "iPhone",
"parameters": [
{
"code": "xxx",
"name": "Andrew",
"value": "9",
},
{
"code": "yyy",
"name": "Joy",
"value": "7",
},
]
}
I am using the following query to push into the parameters array object
db.coll.update({
"parameters.name": "Andrew"
},
{
$push: {
"parameters": {
"code": "$code",
"name": "bar",
"value": "10",
}
}
},
{
multi: true
})
However, for the value of code, I want to use the value of the object that matched (i.e. the object with parameters.name == "Andrew", which here is xxx.
Here's a playground link to the problem https://mongoplayground.net/p/v-j1tCCjiWq
Also, I am using a really old version (3.2) of MongoDb. It would be preferable if the solution worked with that.
With MongoDB v4.4+, you can first $match with your criteria. Chain up $first with $filter to extract the array element you want. Use $concatArrays to append a new element(i.e. same as $push) to the array and $merge to update back into the collection.
db.coll.aggregate([
{
$match: {
"parameters.name": "Andrew"
}
},
{
$set: {
parameters: {
"$concatArrays": [
"$parameters",
[
{
"$mergeObjects": [
// get the object matched
{
"$first": {
"$filter": {
"input": "$parameters",
"as": "p",
"cond": {
$eq: [
"Andrew",
"$$p.name"
]
}
}
}
},
// update the object matched with other fields with constant value
{
"name": "bar",
"value": "10"
}
]
}
]
]
}
}
},
{
"$merge": {
"into": "coll1",
"on": "_id"
}
}
])
Mongo Playground

Delete objects that met a condition inside an array in mongodb

My collection has array "name" with objects inside. I need to remove only those objects inside array where "name.x" is blank.
"name": [
{
"name.x": [
{
"_id": "607e7fcca57aa56e2a06b57b",
"name": "abc",
"type": "123"
}
],
"_id": {
"$oid": "62232cd70ce38c5007de31e6"
},
"qty": "1.0",
"Unit": "pound,lbs"
},
{
"name.x": [
{
"_id": "607e7fcca57aa56e2a06b430",
"name": "xyz",
"type": "123"
}
],
"_id": {
"$oid": "62232cd70ce38c5007de31e7"
},
"qty": "1.0",
"Unit": "pound,lbs"
},{
"name.x": []
,
"_id": {
"$oid": "62232cd70ce38c5007de31e7"
},
"qty": "1.0",
"Unit": "pound,lbs"
}
I tried to get all the ids where name.x is blank using python and used $pull to remove objects base on those ids.But the complete array got deleted.How can I remove the objects that meet the condition.
Think MongoDB update with aggregation pipeline meets your requirement especially to deal with the field name with ..
$set - Update the name array field by $filter name.x field is not an empty array.
db.collection.update({},
[
{
$set: {
name: {
$filter: {
input: "$name",
cond: {
$ne: [
{
$getField: {
field: "name.x",
input: "$$this"
}
},
[]
]
}
}
}
}
}
],
{
multi: true
})
Sample Mongo Playground

Upsert update of large amount of documents in mongoDB

I have mongoDB collection items with following document structure:
{ name: string, values: string[] }
Then I have large amount of documents outside of database, which I want add to the db.
If document with same name already exists in database, push its value to values of db item,
If document with same name doesn't exist, create new document.
For example, let's have these records in the database:
[
{ "name": "A", "values": ["Alaska"] },
{ "name": "B", "values": [] }
]
Now add these records:
[
{ "name": "A", "value": "Australia" },
{ "name": "C", "value": "Canada" }
]
Result should be:
[
{ "name": "A", "values": ["Alaska", "Australia"] },
{ "name": "B", "values": [] },
{ "name": "C", "values": ["Canada"] }
]
However the number of documents can be hundreds of thousands. Is there any better way to upsert array of records than one by one?
db.items.update({ "name": "A" }, { "$set": { "name": "A" }, "$push": { "values": "Australia" } }, { "upsert": true })
db.items.update({ "name": "C" }, { "$set": { "name": "C" }, "$push": { "values": "Canada" } }, { "upsert": true })
...
You can use bulk writes to aggregate multiple updates into a single network request, but if the changes are individually determined per document and cannot be expressed as a query applying to multiple documents, each change must be its own insert/update command.

Get Distinct Document by Max Value of a Field

I have a requirement where i should query on two fields out of which one is unique field and one is maximum field.
Here is my sample collection
{
"_id": ObjectId('59537b7fe08062b9ee8dfdf6'),
"admin": {
"model": "abc",
"version": "00",
"name":"john",
"age":"30"
}
}
{
"_id": ObjectId('59537b7fe08062b9ee8dfdf7'),
"admin": {
"model": "abc",
"version": "01" ,
"name":"john",
"age":"30"
}
}
{
"_id": ObjectId('59537b7fe08062b9ee8dfdf8'),
"admin": {
"model": "def",
"version": "00" ,
"name":"cena",
"age":"30"
}
}
I have two same models with different versions.I want to query for model with maximum version. I tried by simply sorting the version it does not work for me.
I am expecting output like this
{
"_id": ObjectId('59537b7fe08062b9ee8dfdf7'),
"admin": {
"model": "abc",
"version": "01" ,
"name":"john",
"age":"30"
}
}
{
"_id": ObjectId('59537b7fe08062b9ee8dfdf8'),
"admin": {
"model": "def",
"version": "00" ,
"name":"cena",
"age":"30"
}
}
Any suggestions will be really helpful.
As Neil said, it is $sort, $group, and $replaceRoot, but with correct values in the query:
db.collection.aggregate([
{ "$sort": { "admin.version": -1 } },
{ "$group": {
"_id": "$admin.model" ,
"admin": { "$first": "$$ROOT" }
}},
{ "$replaceRoot": { "newRoot": "$admin" } }
])

Match a specific item in a collection using MongoDB aggregation query

In the following document, I'd like to find the table object which has the column matching the criteria
(label is 'abc' or (name is 'abc' and label is empty))
{
"tables": [
{
"name": "table1",
"columns": [
{
"name": "abc",
"label": "xyz"
},
{
"name": "def",
"label": ""
}
]
},
{
"name": "table2",
"columns": [
{
"name": "xxx",
"label": "yyy"
},
{
"name": "zzz",
"label": "abc"
}
]
}
]
}
The expected answer is table2 object.
The following query returns both table1 and table2. I think the 'match' clause is applied to the entire column array not column by column. I'd like to get the table object as the final result So unwinding at column level is not an option.
db.getCollection('test').aggregate(
{$unwind : "$tables"},
{"$match":
{"$or":
[
{"tables.columns.label": "abc"},
{"tables.columns.name":"abc", "tables.columns.label": ""}
]
}
}
)
How do I match column by column?
You should use the $elemMatch operator:
db.getCollection('test').aggregate(
{
$unwind: "$tables"
},
{
$match:
{
"$or":
[
{
"tables.columns.label": "abc"
},
{
"tables.columns":
{
$elemMatch:
{
"name": "abc",
"label": ""
}
}
}
]
}
}
)