MongoDB query in java for array fields using ProjectionOperation - mongodb

Below is the JSON structure for a Store document:
{
{
"_id":"87348378",
"name": "ABC store",
"type": "Books",
"books": [
{
"name": "love",
"id": "1",
"types":{
"type":"love",
"number":"1"
}
},
{
"name": "coreman",
"id": "2",
"types":{
"type":"love",
"number":"1"
}
}
]
},
{
"_id":"87348",
"name": "Some store",
"type": "Books",
"books": [
{
"name": "JAVA",
"id": "1",
"types":{
"type":"Programming",
"number":"2"
}
},
{
"name": "coreman",
"id": "2",
"types":{
"type":"Programming",
"number":"3"
}
}
]
}
}
I need to get the all the stores, which are of a Bookstore type. But, I just want to return certain fields from the Books array(Name & type from types)
Using the following query to get the result:
db.getCollection('store').aggregate([{
"$project" : { "name" : 1 , "type" : 1 ,
"books":{"name":1,"types":{"type":1}}}
},
{ "$group" : { "_id" : "$type",
"books" : { "$push" : "$$ROOT"}}}])
Could anyone help me out to generate the same query with Spring/Java.
Note: I have seen addInclude method has like below in spring docs.
reference: https://docs.spring.io/spring-data/data-mongo/docs/current/api/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.html
**org.springframework.data.mongodb.core.aggregation**
andInclude
public ProjectionOperation andInclude(String... fieldNames)
Includes the given fields into the projection.
Parameters:
fieldNames - must not be null.
Returns:
andInclude
public ProjectionOperation andInclude(Fields fields)
Includes the given fields into the projection.
Parameters:
fields - must not be null.
Returns:

Related

MongoDb regex search in array objects

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

pymongo query to fetch the documents and its child-documents

I have following documents in collection.
{
"_id": Object('sjaflj'),
"employee_id": "23849823432",
"name": "employee-1",
"department": "department-1",
"type": "manager",
"associations" : [
{
"name" : "associated_components",
"values" : [
"45c10396cefa7351b1ddadf6d6a29c37"
]
}
]
}
# Document-2
{
"_id": Object('sjafljwerq'),
"employee_id": "45c10396cefa7351b1ddadf6d6a29c37",
"name": "employee-2",
"department": "department-1",
"type": "employee",
"associations" : [
{
"name" : "associated_components",
"values" : []
}
]
}
I am fetching documents from db based on name, department, type. i would like to fetch all child-document if the type of record is "manager".
The child-records's key attached to manager is in the associations-->values.
How i can get all the records + child-records from collection using single query. ?
Please suggest some ideas, it will be very helpful. thanks.

Return null default value if no result found

I have a collection that looks like this:
{
"value" : "20",
"type" : "square",
"name" : "form1"
}
{
"value" : "24",
"type" : "circle",
"name" : "form2"
}
{
"value" : "12",
"type" : "square",
"name" : "form3"
}
I want to extract a document with name = form2 so I type:
db.myCollec.find({"name":"form2"} , {"name":1, "type":1, "_id":0})
The result is:
{ "name" : "form2", "type" : "circle" }
Now if I want to find a document with name = form4 I type:
db.myCollec.find({"name":"form4"} , {"name":1, "type":1, "_id":0})
But this returns nothing because there is no document with this name.
However I want the return value to look like this:
{ "name" : "form4", "type" : null }
How would I do this?
You can use below aggregation
Mongodb doesn't produce the result if there is not $matched data found with the query.
But you can use $facet aggregation which processes multiple aggregation pipeline within single stage.
So first use $facet to get the $matched documents and use $projection if no ($ifNull) data found.
let searchTerm = "form2"
db.myCollec.aggregate([
{ "$facet": {
"data": [
{ "$match": { "name": searchTerm }},
{ "$project": { "name": 1, "type": 1, "_id": 0 }}
]
}},
{ "$project": {
"name": {
"$ifNull": [{ "$arrayElemAt": ["$data.name", 0] }, searchTerm ]
},
"type": {
"$ifNull": [{ "$arrayElemAt": ["$data.type", 0] }, null]
}
}}
])
Why you dont check in callback if result==null and create your own empty object?
let name = "form4";
db.myCollec.find({"name":name} , {"name":1, "type":1, "_id":0}, function(err, result){
if(err) {
// Error handling
return;
}
if (result==null){
result = {"name":name, "type":null};
}
});

MongoDb $lookup query with multiple fields from objects array

This question has previously been marked as a duplicate of this question I can with certainty confirm that it is not.
This is not a duplicate of the linked question because the elements in question are not an array but embedded in individual objects of an array as fields. I am fully aware of how the query in the linked question should work, however that scenario is different from mine.
I have a question regarding the $lookup query of MongoDb. My data structure looks as follows:
My "Event" collection contains this single document:
{
"_id": ObjectId("mongodbobjectid..."),
"name": "Some Event",
"attendees": [
{
"type": 1,
"status": 2,
"contact": ObjectId("mongodbobjectidHEX1")
},
{
"type": 7,
"status": 4,
"contact": ObjectId("mongodbobjectidHEX2")
}
]
}
My "Contact" collection contains these documents:
{
"_id": ObjectId("mongodbobjectidHEX1"),
"name": "John Doe",
"age": 35
},
{
"_id": ObjectId("mongodbobjectidHEX2"),
"name": "Peter Pan",
"age": 60
}
What I want to do is perform an aggregate query with the $lookup operator on the "Event" collection and get the following result with full "contact" data:
{
"_id": ObjectId("mongodbobjectid..."),
"name": "Some Event",
"attendees": [
{
"type": 1,
"status": 2,
"contact": {
"_id": ObjectId("mongodbobjectidHEX1"),
"name": "John Doe",
"age": 35
}
},
{
"type": 7,
"status": 4,
"contact": {
"_id": ObjectId("mongodbobjectidHEX2"),
"name": "Peter Pan",
"age": 60
}
}
]
}
I have done the same with single elements of "Contact" referenced in another document but never when embedded in an array. I am unsure of which pipeline arguments to pass to get the above shown result?
I also want to add a $match query to the pipeline to filter the data, but that is not really part of my question.
Try this one
db.getCollection('Event').aggregate([{ "$unwind": "$attendees" },
{ "$lookup" : { "from" : "Contact", "localField" : "attendees.contact", "foreignField": "_id", "as" : "contactlist" } },
{ "$unwind": "$contactlist" },
{ "$project" :{
"attendees.type" : 1,
"attendees.status" : 1,
"attendees.contact" : "$contactlist",
"name": 1, "_id": 1
}
},
{
"$group" : {
_id : "$_id" ,
"name" : { $first : "$name" },
"attendees" : { $push : "$attendees" }
}
}
])

Combining multiple sub-documents into a new doc in mongo

I am trying to query multiple sub-documents in MongoDB and return as a single doc.
I think the aggregation framework is the way to go, but, can't see to get it exactly right.
Take the following docs:
{
"board_id": "1",
"hosts":
[{
"name": "bob",
"ip": "10.1.2.3"
},
{
"name": "tom",
"ip": "10.1.2.4"
}]
}
{
"board_id": "2",
"hosts":
[{
"name": "mickey",
"ip": "10.2.2.3"
},
{
"name": "mouse",
"ip": "10.2.2.4"
}]
}
{
"board_id": "3",
"hosts":
[{
"name": "pavel",
"ip": "10.3.2.3"
},
{
"name": "kenrick",
"ip": "10.3.2.4"
}]
}
Trying to get a query result like this:
{
"hosts":
[{
"name": "bob",
"ip": "10.1.2.3"
},
{
"name": "tom",
"ip": "10.1.2.4"
},
{
"name": "mickey",
"ip": "10.2.2.3"
},
{
"name": "mouse",
"ip": "10.2.2.4"
},
{
"name": "pavel",
"ip": "10.3.2.3"
},
{
"name": "kenrick",
"ip": "10.3.2.4"
}]
}
I've tried this:
db.collection.aggregate([ { $unwind: '$hosts' }, { $project : { name: 1, hosts: 1, _id: 0 }} ])
But it's not quite what I want.
You can definitely do this with aggregate. Let's assume your data is in collection named board, so please replace it with whatever your collection name is.
db.board.aggregate([
{$unwind:"$hosts"},
{$group:{_id:null, hosts:{$addToSet:"$hosts"}}},
{$project:{_id:0, hosts:1}}
]).pretty()
it will return
{
"hosts" : [
{
"name" : "kenrick",
"ip" : "10.3.2.4"
},
{
"name" : "pavel",
"ip" : "10.3.2.3"
},
{
"name" : "mouse",
"ip" : "10.2.2.4"
},
{
"name" : "mickey",
"ip" : "10.2.2.3"
},
{
"name" : "tom",
"ip" : "10.1.2.4"
},
{
"name" : "bob",
"ip" : "10.1.2.3"
}
]
}
So your basic problem here is that the arrays are contained in separate documents. So while you are correct to $unwind the array for processing, in order to bring the content into a single array you would need to $group the result across documents, and $push the content to the result array:
db.collection.aggregate([
{ "$unwind": "$hosts" },
{ "$group": {
"_id": null,
"hosts": { "$push": "$hosts" }
}}
])
So just as $unwind will "deconstruct" the array elements, the $push accumulator in $group brings "reconstructs" the array. And since there is no other key to "group" on, this brings all the elements into a single array.
Note that a null grouping key is only really practical when the resulting document would not exceed the BSON limit. Otherwise you are better off leaving the individual elements as documents in themselves.
Optionally remove the _id with an additional $project if required.