field within the object of array in Mongodb - mongodb

The sample structure is as follows:
{
"_id": 1,
"College_name" : "abcdef"
"Students": [
{
"name": "a",
"Grade": "First"
},
{
"name": "b",
"Grade": "Second"
},
{
"name": "c",
"Grade": "First"
},
{
"name": "d",
"Grade": "First"
}
]
}
What I am not getting is - I want to get all ("Grade" : "First") objects in the array and insert only ("Grade" : "First") objects to other collection where ("College_name" : "abcdef"). What is the optimal solution for this?
Is this possible through aggregation??
Please help me out to solve this.

You can fetch the data with {"Grade": "First"} either using aggregation or mongo query -
Aggregation query -
db.coll.aggregate([{'$match':{"Students.Grade":"First"}}, {$project: {"_id":1, "College_name":1}}])
Mongo Query -
db.coll.find({"Students.Grade":"First"}, {"College_name":1})
After that you can insert the data to other collection.

Answer to this question is in the link:
query to retrieve multiple objects in an array in mongodb
i.e.,
Aggregation query will have 4 pipeline operators:
[match, unwind, match, group]
and then can be inserted to the other collection.

Related

how to use $project return nest array after $lookup in mongodb

I have two collections in MongoDB and I like to use $lookup to mapping two collections and return specific values.
Job collection
{
"_id": ObjectId("5b0d2b2c7ac4792df69a9942"),
"title": "software engineer",
"categories" : [
ObjectId("5b0d16ee7ac4792df69a9924"),
ObjectId("5b0d47667ac4792df69a9994")
],
"deadline": 2021-05-03T06:29:54.634+00:00
}
job_categories collection:
{
"_id": ObjectId(5b0d16ee7ac4792df69a9924),
"name": "front-end"
}
{
"_id": ObjectId(5b0d47667ac4792df69a9994),
"name": "full-stack"
}
The objectidin job collection categories array matches the _id of job_categories.
how to use $lookup and $project to return the following result.
Expected result:
{
"_id": ObjectId("5b0d2b2c7ac4792df69a9942"),
"title": "software engineer",
"categories" : [
ObjectId("5b0d16ee7ac4792df69a9924"),
ObjectId("5b0d47667ac4792df69a9994")
],
"deadline": 2021-05-03T06:29:54.634+00:00,
"categories_list": [
"front-end",
"full-stack"
]
}
The expected result adds a new filed categories list and the array value reference job_categories collection's namekey value.
Just perform the $lookup directly. Then $project the field $categories_list.name as categories_list.
Here is the Mongo playground for your reference.

Trying to fetch data from Nested MongoDB Database?

I am beginner in MongoDB and struck at a place I am trying to fetch data from nested array but is it taking so long time as data is around 50K data, also it is not much accurate data, below is schema structure please see once -
{
"_id": {
"$oid": "6001df3312ac8b33c9d26b86"
},
"City": "Los Angeles",
"State":"California",
"Details": [
{
"Name": "Shawn",
"age": "55",
"Gender": "Male",
"profession": " A science teacher with STEM",
"inDate": "2021-01-15 23:12:17",
"Cars": [
"BMW","Ford","Opel"
],
"language": "English"
},
{
"Name": "Nicole",
"age": "21",
"Gender": "Female",
"profession": "Law student",
"inDate": "2021-01-16 13:45:00",
"Cars": [
"Opel"
],
"language": "English"
}
],
"date": "2021-01-16"
}
Here I am trying to filter date with date and Details.Cars like
db.getCollection('news').find({"Details.Cars":"BMW","date":"2021-01-16"}
it is returning details of other persons too which do not have cars- BMW , Only trying to display details of person like - Shawn which have BMW or special array value and date too not - Nicole, rest should not appear but is it not happening.
Any help is appreciated. :)
A combination of $match on the top-level fields and $filter on the array elements will do what you seek.
db.foo.aggregate([
{$match: {"date":"2021-01-16"}}
,{$addFields: {"Details": {$filter: {
input: "$Details",
as: "zz",
cond: { $in: ['BMW','$$zz.Cars'] }
}}
}}
,{$match: {$expr: { $gt:[{$size:"$Details"},0] } }}
]);
Notes:
$unwind is overly expensive for what is needed here and it likely means "reassembling" the data shape later.
We use $addFields where the new field to add (Details) already exists. This effectively means "overwrite in place" and is a common idiom when filtering an array.
The second $match will eliminate docs where the date matches but not a single entry in Details.Cars is a BMW i.e. the array has been filtered down to zero length. Sometimes you want to know this info so if this is the case, do not add the final $match.
I recommend you look into using real dates i.e. ISODate instead of strings so that you can easily take advantage of MongoDB date math and date formatting functions.
Is a common mistake think that find({nested.array:value}) will return only the nested object but actually, this query return the whole object which has a nested object with desired value.
The query is returning the whole document where value BMW exists in the array Details.Cars. So, Nicole is returned too.
To solve this problem:
To get multiple elements that match the criteria you can do an aggregation stage using $unwind to separate the different objects into array and match by the criteria you want.
db.collection.aggregate([
{
"$match": { "Details.Cars": "BMW", "date": "2021-01-26" }
},
{
"$unwind": "$Details"
},
{
"$match": { "Details.Cars": "BMW" }
}
])
This query first match by the criteria to avoid $unwind over all collection.
Then $unwind to get every document and $match again to get only the documents you want.
Example here
To get only one element (for example, if you match by _id and its unique) you can use $elemMatch in this way:
db.collection.find({
"Details.Cars": "BMW",
"date": "2021-01-16"
},
{
"Details": {
"$elemMatch": {
"Cars": "BMW"
}
}
})
Example here
You can use $elemenMatch into query or projection stage. Docs here and here
Using $elemMatch into query the way is this:
db.collection.find({
"Details": {
"$elemMatch": {
"Cars": "BMW"
}
},
"date": "2021-01-16"
},
{
"Details.$": 1
})
Example here
The result is the same. In the second case you are using positional operator to return, as docs says:
The first element that matches the query condition on the array.
That is, the first element where "Cars": "BMW".
You can choose the way you want.

How to search for child objects inside parent objects in MongoDB?

I'm trying to search any value that match with a "name" param, inside any object with any level in a MongoDB collection.
My BSON looks like this:
{
"name": "a",
"sub": {
"name": "b",
"sub": {
"name": "c",
"sub": [{
"name": "d"
},{
"name": "e",
"sub": {
"name": "f"
}
}]
}
}
}
I've created an index with db.collection.createIndex({"name": "text"}); and it seems to work, because it has created more than one.
{
"numIndexesBefore" : 1,
"numIndexesAfter" : 6,
"note" : "all indexes already exist",
"ok" : 1
}
But, when I use this db.collection.find({$text: {$search : "b"}}); to search, it does not work. It just searches at the first level.
I cannot do a search with precision, because the dimensions of the objects/arrays is dynamic and can grow or shrink at any time.
I appreciate your answers.
MongoDB cannot build an index on arbitrarily-nested objects. The index only occurs for the depth specified. In your case, the $text search will only check the top-level name field, but not the name field for any of the nested sub-documents. This is an inherent limitation for indexing.
To my knowledge, MongoDB has no support for handling these kinds of deeply-nested data structures. You really need to break your data out into separate documents in order to handle it correctly. For example, you could break it out into the following:
[
{
"_id": 0,
"name": "a",
"root_id": null,
"parent_id": null
},
{
"_id": 1,
"name": "b",
"root_id": 0,
"parent_id": 0
},
{
"_id": 2,
"name": "c",
"root_id": 0,
"parent_id": 1
},
{
"_id": 3,
"name": "d",
"root_id": 0,
"parent_id": 2
},
{
"_id": 4,
"name": "e",
"root_id": 0,
"parent_id": 2
},
{
"_id": 5,
"name": "f",
"root_id": 0,
"parent_id": 4
}
]
In the above structure, our original query db.collection.find({$text: {$search : "b"}}); will now return the following document:
{
"_id": 1,
"name": "b",
"root_id": 0,
"parent_id": 0
}
From here we can retrieve all related documents by retrieving the root_id value and finding all documents with an _id or root_id matching this value:
db.collection.find({
$or: [
{_id: 0},
{root_id: 0}
]
});
Finding all root-level documents is a simple matter of matching on root_id: null.
The drawback, of course, is that now you need to assemble these documents manually after retrieval by matching a document's parent_id with another document's _id because the hierarchical information has been abstracted away. Using a $graphLookup could help alleviate this somewhat by matching each subdocument with a list of ancestors, but you would still need to determine the nesting order manually.
Regardless of how you choose to structure your documents moving forward, this sort of restructure is going to be needed if you're going to query on arbitrarily-nested content. I would encourage you to consider different possibilities and determine which is most suited for your specific application needs.

How do I add new fields to mongodb in production and query?

Structural question about mongo db. I have a node/express/mongoose app querying my mongo database for my rest api and have it running in production.
I am wanting to add new fields to query, but the issue I am having is that a lot of old documents will not have these new fields. Whats the best way to add a new field and query? I have used field :{ exists: true } and that works, but how do I query fields if these new fields are true or false?
I want to find all documents and not just if that particular fields exists or not. Not even sure if this is possible, I may need to add the new fields to every single document?
if the field that you are querying on does not exist in any document, that document will be discarded. To demo it, here are the two documents I have in a collection, and of these documents does not have a field I am querying on. Mongo simply returned the document that matched the query requirements
The sample documents
{
"_id": 2,
"description": "QUESTION",
"names": [
"A",
"B",
"C",
"D",
"E"
]
},
{
"_id": 3,
"description": "ANSWER",
"newField1": "I have some value", <=== new field only for this doc
"names": [
"A2",
"B2",
"C2",
"D2",
"E2"
]
}
My query
db.collection.find({
"newField1": "I have some value"
})
the result
{
"_id": 3,
"description": "ANSWER",
"names": [
"A2",
"B2",
"C2",
"D2",
"E2"
],
"newField1": "I have some value"
}

How to connect to collections by nested fields in MongoDB

I am struggling with some query in MongoDB. Let's say I have standings collection which looks like
{
"competitions: {id: "1", name:"someLeague"},
"standings": [
{
"type": "TOTAL",
"table": [
{
"position": "1",
"team": {
"id": "123",
"name": "XYZ"
},
won: "1",
draw: "2",
lost: "3",
points: "4",
},
{
"position": "2",
"team": {
"id": "321",
"name": "ABC"
}
...
And the fixtures collection which looks like
{
matchDay: "YYYY-MM-DD",
homeTeam: {id: "123", name:"ABC"},
awayTeam: {id: "321", name:"XYZ"},
}
Is it possible to connect this two collection this way that field "homeTeam" in fixtures collection will contain all information including points, won games etc. from standings where type would be total? And same thing with the field awayTeam, with the proviso that information of team would be from array where standings type is away.
There is no means in MongoDB to reference a document of collection A in collection B so that find queries on collection B automatically provide attributes of the referenced document. However, as of MongoDB 3.2 it is possible to use $lookup command as part of an aggregation (see https://stackoverflow.com/a/33511166/3976662) to JOIN (similar to standard SQL) over multiple collections during the query. In your case, you can consider using $lookup in conjunction with $unwind - similar to the example in the MongoDB docs. Spring Data Mongo supports $lookup since 1.10.