I have the following User object:
{
"_id" : ObjectId("someId"),
"name" : "Bob",
"password" : "fakePassword",
"follower" : [...],
"following" : [..]
}
I need to paginate over the follower list, so I use the slice projection operator, but I just need the paginated followers list to be returned. And I don't know if I am doing it the wrong way, or this can't be done, but limit fields doesn't work with slice projection.
Following are a couple of queries I tried:
collection.findOne(
{
_id: new ObjectId(userId)
},
{
follower: {$slice:[skip, parseInt(pageSize)]},
follower: 1
},..
and
collection.findOne(
{
_id: new ObjectId(userId)
},
{
follower: 1,
follower: {$slice:[skip, parseInt(pageSize)]}
},
But these return all the values in the object, and does not limit the fields, although, the slice works fine in both the cases.
Also when I do something like _id:0,following:0 , this part works, but I don't want to mention each and every field in the query like this, it may create problems once I decide to change the schema.
How do I get this to work, what could be the syntax for the query to get this working..??
Not sure I'm getting your usage pattern here. Perhaps we can simplify the example a little. So considering the document:
{
"_id" : ObjectId("537dd763f95ddda3208798c5"),
"name" : "Bob",
"password" : "fakePassword",
"follower" : [
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K"
]
}
So the simple query like this:
db.paging.find(
{ "name": "Bob" },
{
"_id": 0,
"name": 0,
"password": 0,
"follower": { "$slice": [0,3] }
}).pretty()
Gives results:
{
"follower" : [
"A",
"B",
"C"
]
}
And similarly from the following page:
db.paging.find(
{ "name": "Bob" },
{
"_id": 0,
"name": 0,
"password": 0,
"follower": { "$slice": [3,3] }
}).pretty()
Gives the results:
{
"follower" : [
"D",
"E",
"F"
]
}
So for me personally I am not sure whether you were asking about the field exclusion or whether you were asking about "paging" the array results, but either way, both of those examples are shown here.
One way is to actually use _id here by saying {_id:1}:
{ "_id" : ObjectId("537de1bc08eb9d89a7d3a1b2"), "f" : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20 ], "d" : 1 }
> db.test.findOne({ "_id" : ObjectId("537de1bc08eb9d89a7d3a1b2")},{f:{$slice:[0,2]}})
{
"_id" : ObjectId("537de1bc08eb9d89a7d3a1b2"),
"f" : [
1,
2
],
"d" : 1
}
> db.test.findOne({ "_id" : ObjectId("537de1bc08eb9d89a7d3a1b2")},{_id:0, f:{$slice:[0,2]}})
{ "f" : [ 1, 2 ], "d" : 1 }
> db.test.findOne({ "_id" : ObjectId("537de1bc08eb9d89a7d3a1b2")},{_id:1, f:{$slice:[0,2]}})
{ "_id" : ObjectId("537de1bc08eb9d89a7d3a1b2"), "f" : [ 1, 2 ] }
Related
JSON Object:
{
"students_detail": [
{
"student_id": 1,
"name": "abc",
"roll_number": 10
},
{
"student_id": 2,
"name": "pqr",
"roll_number": 12
}
],
"subject_details": [
{
"subject_id": 1,
"subject_name": "math"
},
{
"subject_id": 2,
"subject_name": "english"
}
],
"exam_details": [
{
"exam_id": 1,
"exam_name": "Prelim"
}
],
"mark_details": [
{
"id": 1,
"exam_id": 1,
"subject_id": 1,
"student_id": 1,
"mark": 51
},
{
"id": 2,
"exam_id": 1,
"subject_id": 2,
"student_id": 2,
"mark": 61
}
]
}
Ouptut:
{
"student_mark_details": [
{
"abc": {
"roll_number": 10,
"Prelim": [
{
"subject_name": "math",
"mark": 51
}
]
},
"pqr": {
"roll_number": 12,
"Prelim": [
{
"subject_name": "english",
"mark": 61
}
]
}
}
]
}
i tried using loops and accesing student_id in both object and comparing them but code gets too messy and complex,is there any way i can use map() or filter() in this or any other method.
i have no idea where to start,my brain is fried i know im asking lot but help will be appreciated (any link/source where i can learn this is fine too)
Your output object really has a weird format: student_mark_details is an array of size 1 that contains an object that has all your students in it. Anyway, this should give you what you need. It is a format that you find often because it is a system with primary key and secondary key used a lot in databases.
The key to manage that is to start with what is at the core of what you are looking for (here, you want to describe students, so you should start from there), and then navigate the informations you need by using the primary/secondary keys. In JS, you can use the find() function in the case where one secondary key can be linked only to one primary key (ex: one mark is linked to one exam), and the filter() function when a secondary key can be linked to multiple secondary keys (ex: a student is linked to many grades).
I am not sure if this is 100% what you need because there are maybe some rules that are not shown in your example, but it solves the problem you submitted here. You might have to test it and change it depending of those rules. I don't know what your level is so I commented a lot
const data = {
"students_detail": [
{
"student_id": 1,
"name": "abc",
"roll_number": 10
},
{
"student_id": 2,
"name": "pqr",
"roll_number": 12
}
],
"subject_details": [
{
"subject_id": 1,
"subject_name": "math"
},
{
"subject_id": 2,
"subject_name": "english"
}
],
"exam_details": [
{
"exam_id": 1,
"exam_name": "Prelim"
}
],
"mark_details": [
{
"id": 1,
"exam_id": 1,
"subject_id": 1,
"student_id": 1,
"mark": 51
},
{
"id": 2,
"exam_id": 1,
"subject_id": 2,
"student_id": 2,
"mark": 61
}
]
}
function format(data) {
const output = {
"student_mark_details": [{}]
};
//I start by looping over the students_detail because in the output we want a sumary by student
data.students_detail.forEach(student => {
//Initialization of an object for a particular student
const individualStudentOutput = {}
const studentId = student.student_id;
const studentName = student.name;
//The rollNumber is easy to get
individualStudentOutput.roll_number = student.roll_number;
//We then want to find the exams that are linked to our student. We do not have that link directly, but we know that our student is linked to some marks
//Finds all the marks that correspond to the student
const studentMarkDetails = data.mark_details.filter(mark => mark.id === studentId);
studentMarkDetails.forEach(individualMark => {
//Finds the exam that corresponds to our mark
const examDetail = data.exam_details.find(exam => individualMark.exam_id === exam.exam_id);
//Finds the subject that corresponds to our mark
const subjectDetail = data.subject_details.find(subject => individualMark.subject_id === subject.subject_id);
//We then create a grade that we will add to our exam
const grade = {
subject_name: subjectDetail.subject_name,
mark: individualMark.mark
}
//We then want to add our grade to our exam, but we don't know if our output has already have an array to represent our exam
//So in the case where it does not exist, we create one
if (!individualStudentOutput[examDetail.exam_name]) {
individualStudentOutput[examDetail.exam_name] = [];
}
//We then add our grade to the exam
individualStudentOutput[examDetail.exam_name].push(grade);
});
//Now that we have finished our individual output for a student, we add it to our object
output.student_mark_details[0][studentName] = individualStudentOutput;
})
return output;
}
console.log(JSON.stringify(format(data)))
I have an object that is structured similar as follows (simplified version):
{
"time": 100,
"complete" : true,
"results" : {
"total": 10,
"score": 3,
"results": [
{
"id" : 123,
"name": "test123"
},
{
"id" : 123,
"name": "test4554"
}
]
}
}
How do I use lodash ._uniqBy to deduplicate the results, based on results.results.id being the unique key?
To clarify, I would like the deduplicated resultset to be returned within the original object structure, e.g.
{
"time": 100,
"complete" : true,
"results" : {
"total": 10,
"score": 3,
"results": [
{
"id" : 123,
"name": "test123"
}
]
}
}
thanks
You can achieve your goal by simply passing the right part of your object into _.uniqBy(array, [iteratee=_.identity]) function.
Next thing you want to do is to 'concat' lodash uniqBy result and your object. This is a little bit tricky. I suggest you to use ES6 Object.assign() method and spread operator.
Check out my solution. Hope this helps.
const myObj = {
"time": 100,
"complete" : true,
"results" : {
"total": 10,
"score": 3,
"results": [
{"id" : 123, "name": "test123"},
{"id" : 123, "name": "test4554"}
]
}
};
const uniq = _.uniqBy(myObj.results.results, 'id');
const resultWrapper = Object.assign({}, myObj.results, { results: [...uniq] });
const resultObj = Object.assign({}, myObj, { results: resultWrapper });
console.log( resultObj );
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.5/lodash.min.js"></script>
You can use something like this
const myObj = {
time: 100,
complete : true,
results : {
total: 10,
score: 3,
results: [
{id : 123, name: "test123"},
{id : 123, name: "test4554"}
]
}
};
_.set(myObj, 'results.results', _.uniqBy(_.get(myObj, 'results.results'), 'id'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>
How do I get a nested object from a selected document?
If this is my document, which I get with Collection.findOne({ _id: 'dZXr2Pg7Ak4M5aYWF'})...
{
"_id" : "dZXr2Pg7Ak4M5aYWF",
"points" : [
{
"id" : "Gwf5BzorXyYBZSEzK",
"coordinates" : [
433,
215
],
"content" : "anything"
},
{
"id" : "iwSM98W5PD87BtcLa",
"coordinates" : [
666,
186
]
}
]
}
... I need to get the complete data of the point with the id Gwf5BzorXyYBZSEzK. So my result should look like:
result = {
"id" : "Gwf5BzorXyYBZSEzK",
"coordinates" : [
433,
215
],
"content" : "anything"
}
You can simply filter the points array, with Array.prototype.filter, like this
console.log(data.points.filter(function(obj) {
return obj.id === "Gwf5BzorXyYBZSEzK";
})[0]);
// { id: 'Gwf5BzorXyYBZSEzK' coordinates: [ 433, 215 ], content: 'anything' }
We just take the first element from the filtered result array. If you want to get all the elements, which have the specific id, then just remove the subscript.
If your environment supports ECMAScript 2015's Arrow functions, then you can write the same as
console.log(data.points.filter((obj) => obj.id === "Gwf5BzorXyYBZSEzK")[0]);
var data = {
"_id": "dZXr2Pg7Ak4M5aYWF",
"points": [{
"id": "Gwf5BzorXyYBZSEzK",
"coordinates": [433, 215],
"content": "anything"
}, {
"id": "iwSM98W5PD87BtcLa",
"coordinates": [666, 186]
}]
};
function finder(id){
for(i=0;i<data.points.length;i++){
if(data.points[i].id==id){
return data.points[i];
}
}
}
var result = finder("Gwf5BzorXyYBZSEzK");
document.write(JSON.stringify(result));
I have the following query that works fine without $max and $min, however, when $max and $min are included nothing happens?
Summary.update({
productId: _product._id,
},{
$addToSet: {
attrs: _variant.attrs,
vars: {
variantId: _variant._id,
title: _variant.title,
imgs: _variant.imgs,
}
},
$max: { 'price.highest': _price.highest },
$min: { 'price.lowest': _price.lowest },
$setOnInsert: self.summary
},{
upsert: true
},function(err,update){
...
});
Any ideas would be greatly appreciated.
Thanks.
Your query does come with come caveats, so it really only remains to explain what would be going wrong.
First and foremost is that your MongoDB server version must be at least version 2.6.x or greater in order to have the $min and $max update operators available.
Now consider the basic test conditions:
> db.test.update(
{ "a": 1 },
{
"$setOnInsert": { "b": 2 },
"$min": { "c": 1 },
"$max": { "d": 1 }
},
{ "upsert": true }
)
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("559f114dbe78f212535e2f5f")
})
On a first execution since there is no data for a matching value of "a" an upsert is performed creating the new object in the collection:
{
"_id" : ObjectId("559f114dbe78f212535e2f5f"),
"a" : 1,
"d" : 1,
"c" : 1,
"b" : 2,
}
Now if you change the values of $min or $max the expected behaviour is to modify those fields where the value falls within the constraints, like so:
> db.test.update(
{ "a": 1 },
{
"$setOnInsert": { "b": 3 },
"$min": { "c": 2 },
"$max": { "d": 2 }
},
{ "upsert": true }
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
Since the value to $min is larger than the stored value this field is not changed. However the value to $max is larger than the stored value and that is modified. There was a different value in $setOnInsert, but this does not affect the data since the operation is not an upsert this time:
{
"_id" : ObjectId("559f114dbe78f212535e2f5f"),
"a" : 1,
"d" : 2,
"c" : 1,
"b" : 2
}
If you then issue a statement with either the same values for $min and $max or that othewise fall out of the contraints of being respectively "lower" or "higher" values then nothing will be updated:
db.test.update(
{ "a": 1 },
{
"$setOnInsert": { "b": 3 },
"$min": { "c": 2 },
"$max": { "d": 2 }
},
{ "upsert": true }
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
That is the expected behavior of the operators in this context. Of course if you submit either $min or $max on a field that does not exist yet, then the field is added to the document with the specified value, just like with $set or $push or similar.
So either you are subitting values that do not meet the requirements for updating or your supported server and/or driver versions are not capable of handling the operators. These are all the things you need to check to see why you don't think you are getting the expected results.
As a side note, beware that you know what you are expecting with $addToSet as well. In a similar way, if the complete object exists already then nothing will be modified. If however you have an object with various keys as you do, then changing any one of those values makes the whole object "unique" and a new member will be added. If you mean to do something else like have only "one" of the keys to contain a unique vallue thene there is other logic you need to apply and cannot simply use $addToSet to handle it.
Also, here is a full listing you can run with node and mongoose in addition to the above test conditions. This is tested against MongoDB 3.x and mongoose 4.0.6:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var testSchema = new Schema({
"a": Number,
"b": Number,
"c": Number,
"d": Number
});
var Test = mongoose.model('Test',testSchema,"test");
async.series(
[
function(callback) {
Test.remove({},callback);
},
function(callback) {
Test.findOneAndUpdate(
{ "a": 1 },
{
"$setOnInsert": { "b": 2 },
"$min": { "c": 1 },
"$max": { "d": 1 }
},
{ "upsert": true, "new": true },
callback
);
},
function(callback) {
Test.findOneAndUpdate(
{ "a": 1 },
{
"$setOnInsert": { "b": 3 },
"$min": { "c": 2 },
"$max": { "d": 2 }
},
{ "upsert": true, "new": true },
callback
);
},
function(callback) {
Test.findOneAndUpdate(
{ "a": 1 },
{
"$setOnInsert": { "b": 3 },
"$min": { "c": 2 },
"$max": { "d": 2 }
},
{ "upsert": true, "new": true },
callback
);
}
],
function(err,results) {
if (err) throw err;
console.log( JSON.stringify( results, undefined, 2 ) );
process.exit();
}
);
Im new to JSON and I have to deal with a complex one.
Please see image below:
It has an error:
I don't know how to properly separate the 2 json arrays. I even tried to use : instead of , on line 18 but I still get errors. BTW, I use http://jsonlint.com/ to validate.
On line 2 you gave a key, but failed to do so on line 19. You have to keep the structure.
Remove the key on line 2, they shouldn't be used for arrays in that way.
Edit: In addition, you are trying to put arrays right in objects, switch the opening and ending object marks ({}) with ([]) for arrays on your first and last line.
[
[
{...},
{...},
...
{...}
],
[
{...},
{...},
...
{...}
],
...
[
{...},
{...},
...
{...}
]
]
I believe the correct way to build this JSON should be:
{
"glEntries": [
{
"generalLedgerId":1,
"accountId": 34,
"amount" : 32334.23,
"descripction": "desc1",
"debit" : "Yes"
},
{
"generalLedgerId":2,
"accountId": 35,
"amount" : 323.23,
"descripction": "desc",
"debit" : "Yes"
},
...
]
}
There are many ways to construct JSON data, but it depends on your data and the way you want to present it. Here are a couple examples - hope it helps:
{
"glEntries": [
{
"object1-prop1": "one"
},
{
"object2-prop1": 1,
"object2-prop2": "two"
},
{
"object3-prop1": [
"a",
"r",
"r",
"a",
"y"
],
"object3-prop1.1": "string"
}
],
"otherEntries": [
{
"objectx": "x"
},
{
"objecty": "y"
},
{
"objectz": [
1,
2,
3,
4
]
}
],
"oneEntry": "json"
}
Other Example:
[
{
"obj1-prop": 222
},
{
"obj2-prop": "object2"
},
{
"obj3-prop": "Object3"
},
[
"a",
"r",
"r",
"a",
"y",
777,
888
],
"string",
178,
{
"objectProp": "testing123"
}
]
You have more {} than needed and will make parsing your JSON more difficult:
Structure will work a lot better like this:
{"glentries":[
{ "property1":"value", "property2" : "value",..... "lastProperty": "value"},
{ "property1":"value", "property2" : "value",..... "lastProperty": "value"},
{ "property1":"value", "property2" : "value",..... "lastProperty": "value"}
]
}
Now glentries is an array of objects that have multiple properties to them.
alert( glentries[0].property2 )
The parent structure is an Object, so it is expecting a string Key for the second array. It it's supposed to be an array of arrays, you should be using an array and not an Object.