MongoDB query to find a value from multiple nested json files - arrays

I have 500 json files of the following format (Same format. Just different data). I just need the city names from all these files. Can anyone help me with the query.
Example: From the following file, I need the output as San Diego, Newnan, Louisville.
{
"clinical_study":{
"location": [
{
"facility": {
"address": {
"city": "San Diego",
"country": "United States"
}
}
},
{
"facility": {
"address": {
"city": "Newnan",
"country": "United States"
}
}
},
{
"facility": {
"address": {
"city": "Louisville",
"country": "United States"
}
}
}
]
}
}

I'm somewhat new to MongoDB, but it sounds like you don't need a query for the city names. It sounds like you already query for the list of locations, so all you need to do is right a loop to obtain the city from each returned location.
Something like this:
// Assume here that clinical is the object in your question
var locations = clinical.clinical_study.locations;
var cityList = [];
for (var i = 0; i < locations.length ; i++ ) {
cityList[i] = locations.facility.address.city;
}
This is a naive approach at the very least. I'm not sure how you can do it in a db query itself (I'm familiar with the express module Mongoose, which I don't think allows this. Please correct me if I'm wrong.)

It is pretty unclear what you want. But Whatever I understand you wants only city names from given sample collection structure. You can simply use Mongo Aggregation
Here is a query for getting cities:
db.collectionName.aggregate({
$unwind: "$clinical_study.location"
}, {
$project: {
"_id": 0,
city: '$clinical_study.location.facility.address.city'
}
})

Related

Can mongo group by array element fully utilize index?

I have some data in mongo looks like
{ "name": "John", "city":"X", "location": ["A", "B"] }
{ "name": "Dude", "city":"X", "location": ["B"] }
{ "name": "Alan", "city":"Y", "location": ["A", "B"] }
{ "name": "Wang", "city":"Y", "location": ["X", "B"] }
There is a compound index
{
"city": 1,
"location": 1
}
This index is use to speed up query for names in specific city and location like
db.collection.find( { "city":"X", location: "A" } )
db.collection.find( { "city":"Y", location: { $in: ["A", "B"] } } )
What I want to do is to know how many rows exists for each location in a specific city, say X. With above data the result should be:
{ "_id": { "location":"A" }, amount: 1 }
{ "_id": { "location":"B" }, amount: 2 }
Here assumes that I do not know how many type of location it is. I mean I can not list all possible location elements before I query it.
If I know all possible locations and it is not too much, a simple thought is to issue multiple query and each of the query can fully utilize index without fetch any data from disk
db.collection.find( { "city":"X", location: "A" } ).count()
db.collection.find( { "city":"X", location: "B" } ).count()
db.collection.find( { "city":"X", location: "C" } ).count()
...
BUT, since I do no know all possible elements in advance, what comes to my mind is to use group by, like
db.collection.aggregate([
{ $match: { "city":"X" } },
{ $unwind: "$location" } },
{ $group: { _id: "$location", "amount": { "$sum": 1 } } }
])
However, when explain() this query, only the $match part can utilize the index, after $unwind, fetch data from disk is required (correct me if I am wrong).
Technically, the index contains all information required for the query. So I wonder is there any query that can fully utilize the index.
BTW, my mongo version is 3.6. If it can only be done in newer version, still please let me know. Thanks.

How to convert json objects to json array in flutter

This is my json response data. I would like to display this data in listview using map but i dont know how to convert the objects of objects to array of data.
{
"success": true,
"data": {
"addresses": {
"abc": {
"address_id": "121",
"firstname": "Demo",
"lastname": "User",
"company": "Demo Company name",
"telephone": "1-541-754-3011",
"address_1": "Demo",
"address_2": "test address",
"postcode": "3333",
"city": "Berlin",
"zone_id": "1256",
"zone": "Berlin",
"zone_code": "BER",
"country_id": "81",
"country": "Germany",
"longitude": "",
"lattitude": "",
"iso_code_2": "DE",
"iso_code_3": "DEU",
"address_format": "{company}\r\n{firstname} {lastname}\r\n{address_1}\r\n{address_2}\r\n{postcode} {city}\r\n{country}",
"custom_field": null
}
}
}
}
I assume you want to display dynamic keys (eg: abc) inside addresses object. For this, you need to iterate through addresses object for keys and values.
//json you provide
addresses = json["data"]["addresses"];
addresses.forEach((final String key, final value) {
//here key will be abc & value will be json object
//you can add key or value to a different list and use for list rendering later
});
You can use JSON to dart tool, which is available online for free.
Paste your JSON to left panel and select dart language from upper right corner,
You will get your dart class code, in which you can use methods like .toMap() and .toJson(),
This can be very helpful for huge JSON data.

How to projection element in array field of MongoDb collection?

MongoDb Collection Example (Person):
{
"id": "12345",
"schools": [
{
"name": "A",
"zipcode": "12345"
},
{
"name": "B",
"zipcode": "67890"
}
]
}
Desired output:
{
"id": "12345",
"schools": [
{
"zipcode": "12345"
},
{
"zipcode": "67890"
}
]
}
My current partial code for retrieving all:
collection.find({}, {id: true, schools: true})
I am querying the entire collection. But I only want to return zipcode part of school element, not other fields (because the actual school object might contain much more data which I do not need). I could retrieve all and remove those un-needed fields (like "name" of school) in code, but that's not what I am looking for. I want to do a MongoDb query.
You can use the dot notation to project specific fields inside documents embedded in an array.
db.collection.find({},{id:true, "schools.zipcode":1}).pretty()

Updating state of deep object value in React/Redux using immutable.js

I have this pretty deeply nested state array and need to update the Hours field by Employee, Task, and Week. I've attempted numerous ways to do this with Immutable but not having success, any help?
Here is an example of my data :
[{
"Employee": "John Doe",
"Other Data": "test",
"Tasks": [{
"AccessType": "Confidential",
"DueDate": "2016-02-26 23:59:59",
"taskId": "3",
"TaskTitle": "testTitle",
"Weeks": {
"2016-10-10": {
"Hours": "3"
}
}
}]
}, {
"Employee": "Bill Der",
"Other Data": "test",
"Tasks": [{
"AccessType": "Confidential",
"DueDate": "2016-02-26 23:59:59",
"taskId": "3",
"TaskTitle": "testTitle",
"Weeks": {
"2016-10-10": {
"Hours": "3"
}
}
}]
}]
You are missing a bunch of information for me to fully answer this for you, but I can show you how I would do something like this otherwise.
You can take advantage of all the functions immutable js provides you with. So lets say you have an object with the information needed to mutate your immutable object, something like this :
var changeHours = {
"Employee": "John Doe",
"TaskTitle": "testTitle",
"Week": '2016-10-10',
"Hours": "5"
}
And we have a basic state like you have there set up like this :
var myState = Immutable.fromJS([{
"Employee": "John Doe",
"Tasks": [{
"AccessType": "Confidential",
"DueDate": "2016-02-26 23:59:59",
"taskId": "3",
"TaskTitle": "testTitle",
"Weeks": {
"2016-10-10": {
"Hours": "3"
}
}
}]
}]);
Note: I did not add more the the arrays, but we will map over them so they will be taken into account.
You can use immutables map to iterate over and find the items you are looking for, something like this :
var newstate = myState.map((k, v) => {
if (k.get('Employee') === changeHours.Employee) {
return k.get('Tasks').map((k, v) => {
if (k.get('TaskTitle') === changeHours.TaskTitle) {
return k.setIn(['Weeks', changeHours.Week, 'Hours'], changeHours.Hours);
}
return k;
})
return k;
}
return k;
});
To see it in action - http://fiddle.jshell.net/djj6u8xL/63/ . I am iterating over each array level with map and finding the correct node by checking based on our changeHours object and immutables get, then once I am at the right level I just use setIn. You can use updateIn as well, it just depends on your scenario.
In the future, please provide all the information for your question, not just a picture of the data, it will be much easier to help you :) (and not have to type out the data structure manually).
Edit: Update based on comment - http://fiddle.jshell.net/qxbq1nq3/9/
the code :
function newTasks(k) {
return k.get('Tasks').map((k, v) => {
if (k.get('TaskTitle') === changeHours.TaskTitle) {
return k.setIn(['Weeks', changeHours.Week, 'Hours'], changeHours.Hours);
}
return k;
});
}
var newstate = myState.map((k, v) => {
if (k.get('Employee') === changeHours.Employee) {
return k.set('Tasks', newTasks(k));
}
return k;
});

Update array of subdocuments in MongoDB

I have a collection of students that have a name and an array of email addresses. A student document looks something like this:
{
"_id": {"$oid": "56d06bb6d9f75035956fa7ba"},
"name": "John Doe",
"emails": [
{
"label": "private",
"value": "private#johndoe.com"
},
{
"label": "work",
"value": "work#johndoe.com"
}
]
}
The label in the email subdocument is set to be unique per document, so there can't be two entries with the same label.
My problems is, that when updating a student document, I want to achieve the following:
adding an email with a new label should simply add a new subdocument with the given label and value to the array
if adding an email with a label that already exists, the value of the existing should be set to the data of the update
For example when updating with the following data:
{
"_id": {"$oid": "56d06bb6d9f75035956fa7ba"},
"emails": [
{
"label": "private",
"value": "me#johndoe.com"
},
{
"label": "school",
"value": "school#johndoe.com"
}
]
}
I would like the result of the emails array to be:
"emails": [
{
"label": "private",
"value": "me#johndoe.com"
},
{
"label": "work",
"value": "work#johndoe.com"
},
{
"label": "school",
"value": "school#johndoe.com"
}
]
How can I achieve this in MongoDB (optionally using mongoose)? Is this at all possible or do I have to check the array myself in the application code?
You could try this update but only efficient for small datasets:
mongo shell:
var data = {
"_id": ObjectId("56d06bb6d9f75035956fa7ba"),
"emails": [
{
"label": "private",
"value": "me#johndoe.com"
},
{
"label": "school",
"value": "school#johndoe.com"
}
]
};
data.emails.forEach(function(email) {
var emails = db.students.findOne({_id: data._id}).emails,
query = { "_id": data._id },
update = {};
emails.forEach(function(e) {
if (e.label === email.label) {
query["emails.label"] = email.label;
update["$set"] = { "emails.$.value": email.value };
} else {
update["$addToSet"] = { "emails": email };
}
db.students.update(query, update)
});
});
Suggestion: refactor your data to use the "label" as an actual field name.
There is one straightforward way in which MongoDB can guarantee unique values for a given email label - by making the label a single separate field in itself, in an email sub-document. Your data needs to exist in this structure:
{
"_id": ObjectId("56d06bb6d9f75035956fa7ba"),
"name": "John Doe",
"emails": {
"private": "private#johndoe.com",
"work" : "work#johndoe.com"
}
}
Now, when you want to update a student's emails you can do an update like this:
db.students.update(
{"_id": ObjectId("56d06bb6d9f75035956fa7ba")},
{$set: {
"emails.private" : "me#johndoe.com",
"emails.school" : "school#johndoe.com"
}}
);
And that will change the data to this:
{
"_id": ObjectId("56d06bb6d9f75035956fa7ba"),
"name": "John Doe",
"emails": {
"private": "me#johndoe.com",
"work" : "work#johndoe.com",
"school" : "school#johndoe.com"
}
}
Admittedly there is a disadvantage to this approach: you will need to change the structure of the input data, from the emails being in an array of sub-documents to the emails being a single sub-document of single fields. But the advantage is that your data requirements are automatically met by the way that JSON objects work.
After investigating the different options posted, I decided to go with my own approach of doing the update manually in the code using lodash's unionBy() function. Using express and mongoose's findById() that basically looks like this:
Student.findById(req.params.id, function(err, student) {
if(req.body.name) student.name = req.body.name;
if(req.body.emails && req.body.emails.length > 0) {
student.emails = _.unionBy(req.body.emails, student.emails, 'label');
}
student.save(function(err, result) {
if(err) return next(err);
res.status(200).json(result);
});
});
This way I get the full flexibility of partial updates for all fields. Of course you could also use findByIdAndUpdate() or other options.
Alternate approach:
However the way of changing the schema like Vince Bowdren suggested, making label a single separate field in a email subdocument, is also a viable option. In the end it just depends on your personal preferences and if you need strict validation on your data or not.
If you are using mongoose like I do, you would have to define a separate schema like so:
var EmailSchema = new mongoose.Schema({
work: { type: String, validate: validateEmail },
private: { type: String, validate: validateEmail }
}, {
strict: false,
_id: false
});
In the schema you can define properties for the labels you already want to support and add validation. By setting the strict: false option, you would allow the user to also post emails with custom labels. Note however, that these would not be validated. You would have to apply the validation manually in your application similar to the way I did it in my approach above for the merging.

Resources