I have data stored in following format in mongodb .. help me in knowing how can I write a query to find if the database has a particular id stored in one of the sheet or not
the structure of data is like the following :-
{
"name": "abc",
"linked_data": {
"sheet 1": [
"7d387e05d3f8180a",
"8534sfjog904395"
],
"sheet 2": [
"7d387e05d3f8180a",
"54647sgdsevey67r34"
]
}
}
for example if id "8534sfjog904395" is mapped with "sheet 1".. then it should return me this data where id is mapped with the sheet.
I am passing id in the query and want to find inside linked_data and then all the sheets
Using dynamic value as field name is considered as an anti-pattern and introduce unnecessary complexity to queries. Nevertheless, you can use $objectToArray to convert the linked_data into array of k-v tuples. $filter to get only the sheet you want. Finally, revert back to original structure using $arrayToObject
db.collection.aggregate([
{
"$set": {
"linked_data": {
"$objectToArray": "$linked_data"
}
}
},
{
$set: {
linked_data: {
"$filter": {
"input": "$linked_data",
"as": "ld",
"cond": {
"$in": [
"8534sfjog904395",
"$$ld.v"
]
}
}
}
}
},
{
"$set": {
"linked_data": {
"$arrayToObject": "$linked_data"
}
}
},
{
$match: {
linked_data: {
$ne: {}
}
}
}
])
Mongo Playground
Related
I have a Mongo collection of states, where each state contains an array of cities:
{
"_id":"636d1137cf1e57408486f795",
"state":"new york",
"cities":[
{
"cityid":"62bd8fa5396ba8aef4ad1041",
"name":"Yonkers"
},
{
"cityid":"62bd8fa5396ba8aef4ad1043",
"name":"Syracuse"
}
]
}
I need an update query that will lowercase every cities.name in the collection.
I can do an update with a literal value e.g.
db.states.updateMany(
{},
{ $set: { "cities.$[].name" : "some_value" } }
)
... , but I need the value to be based on the existing value. The closest I can get is something like this (but that doesn't work -- FieldPath field names may not start with '$')
db.states.updateMany(
{},
{ $set: { "cities.$[].name" : { $toLower: "cities.$[].name"} } }
)
You can chain up $map and $mergeObjects to perform the update. Put it in an aggregation pipeline in update.
db.collection.update({},
[
{
$set: {
cities: {
"$map": {
"input": "$cities",
"as": "c",
"in": {
"$mergeObjects": [
"$$c",
{
"name": {
"$toLower": "$$c.name"
}
}
]
}
}
}
}
}
])
Mongo Playground
I have the below 2 documents from a post collection. How to get only key value pairs from "post" object. The match condition will be using "post_id".
{
"_id":"1001",
"post":{
"country_name":"India",
"state_name":"Maharashtra",
"city_name":"Mumbai",
"duration":"10",
"country":[
{
"name":"india"
}
],
"site":[
{
"site_no":"101",
"code":"Taj",
"name":"santacruz"
}
]
},
"post_id":"abcd123"
}
{
"_id":"1002",
"post":{
"country_name":"India",
"state_name":"Karnataka",
"city_name":"Bangalore",
"duration":"20",
"country":[
{
"name":"india"
}
],
"site":[
{
"site_no":"201",
"code":"COLES",
"name":"Coles Park"
}
]
},
"post_id":"abcd234"
}
The expected result is:
"abcd123":{
"country_name":"India",
"state_name":"Maharashtra",
"city_name":"Mumbai",
"duration":"10"
}
"abcd234" : {
"country_name":"India",
"state_name":"Karnataka",
"city_name":"Bangalore",
"duration":"20"
}
I'm able to filter for one object, but for the bulk and with good performance, can you help me to solve this.
You can try this aggregate query:
The trick here is to create an object with keys k and v to define the key and value for the next stage.
The key will be post_id and the value the object with desired values.
In this case values has to be dinamically created so you can:
Parse $post object to an array, which allows you to filter values inside.
$filter that values to not get arrays or objects using $type.
Then convert again the array to object using $arrayToObject.
And the last step is to use $replaceRoot so you can get your desired output.
db.collection.aggregate([
{
"$project": {
"k": "$post_id",
"v": {
"$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$post" },
"cond": {
"$not": [
{
"$in": [
{ "$type": "$$this.v" },
[ "object", "array" ]
]
}
]
}
}
}
}
}
},
{
"$replaceRoot": {
"newRoot": { "$arrayToObject": [ [ { k: "$k", v: "$v" } ] ] }
}
}
])
Example here
I have a collection containing documents of the following form:
{
tokens: {
name: {
value: "...",
},
...
}
}
name can be anything, and there can be multiple embedded documents assigned to different keys, all of which have a value field.
How do I $unset all embedded documents that have a certain value? { $unset: { "tokens.value": "test" } } doesn't work, and nor does "tokens.$[].value" or "tokens.$.value".
You can use pipelined form of update, like this:
db.collection.update({},
[
{
"$set": {
"tokens": {
$arrayToObject: {
"$filter": {
"input": {
"$objectToArray": "$$ROOT.tokens"
},
"as": "item",
"cond": {
"$ne": [
"$$item.v.value",
"test"
]
}
}
}
}
}
}
],
{
multi: true
})
Playground link.
In this query, we do the following:
We convert tokens object to an array, using $objectToArray.
We filter out the array elements which have a value key present in embedded documents and have a value of test using $filter.
Finally we convert the filtered array back again to object, and assign it to tokens key.
I have a mongo collection mimicking this java class. A student can be taught a number of subjects across campus.
class Students {
String studentName;
Map<String,List<String>> subjectsByCampus;
}
So a structure will look like this
{
_id: ObjectId("someId"),
studentName:'student1',
subjectByCampusName:{
campus1:['subject1','subject2'],
campus2: ['subject3']
},
_class: 'fqnOfTheEntity'
}
I want to find the count of subjects offered by each campus or be able to query the count of subjects offered by a specific campus. Is there a way to get it through query?
Mentioned in the comments, but the schema here does not appear to be particularly well-suited toward gathering the data requested in the question. For an individual student, this is doable via $size and processing the object (as an array) via $map. For example, if we want your desired out put of campus1:2, campus2:1 for the sample document provided, a pipeline to produce that in a countsByCampus field might look as follows:
[
{
"$addFields": {
"countsByCampus": {
"$arrayToObject": {
"$map": {
"input": {
"$objectToArray": "$subjectByCampusName"
},
"in": {
"$mergeObjects": [
"$$this",
{
v: {
$size: "$$this.v"
}
}
]
}
}
}
}
}
}
]
Playground demonstration here with an output document of:
{
"_class": "fqnOfTheEntity",
"_id": "someId",
"countsByCampus": {
"campus1": 2,
"campus2": 1
},
"studentName": "student1",
"subjectByCampusName": {
"campus1": [
"subject1",
"subject2"
],
"campus2": [
"subject3"
]
}
}
Doing that across the entire collection would involve $grouping the results together. This can be done but would be an extremely resource-intensive and likely slow operation.
I am fairly new to MongoDB and I came across the $replaceRoot(aggregation) which I need to output the document the way I need it.
Document Schema
{
name: "Apple",
image: "",
is_fruit: true,
other: [
{
url: "",
quotes: { // saved as ObjectId and is then lookedup or populated
text: "An apple a day keeps the doctor away"
}
}
]
}
Wanted Output
{
name: "Apple",
image: "",
is_fruit: true,
other: [
{
text: "An apple a day keeps the doctor away"
},
...
]
}
i.e: To make quotes field as the root of the other array and not the root of entire document.
Thank you.
There is no $replaceRoot like for embedded array but you can achieve similar effect using $map to transform the array into new format.
Something like
db.col.aggregate([
{
"$addFields": {
"other": {
"$map": {
"input": "$other",
"as": "res",
"in": {
"text": "$$res.text"
}
}
}
}
}
])
You can easily do something similar in client side code.
By using $replaceRoot(aggregation) here is the output.
P.S: The collection name I used is 'demoStack' replace it with your specific collections.
Query is :
db.demoStack.aggregate([
{ $replaceRoot: { newRoot: { $mergeObjects: [
{name:"$name"},
{ image: "$image" },
{is_fruit:"$is_fruit"},
{other:"$other.quotes"}
] } } } ])