I am trying to return returned properties, much like in Mysql's AS. But with renamed object properties.
Query
Games.find({leagueID:leagueID, result:{$ne: null}}).populate('home_id away_id').sort({date: -1}).execAsync()
Output
{
home_id: {
...some details
},
away_id: {
...some details
}
}
Desired Output
{
home: {
...some details
},
away: {
...some details
}
}
So how can I get the desired outcome?
My solution is to use the transform function.
GamesSchema.set('toJSON', {
transform: function(doc, ret, options) {
if (mongoose.Types.ObjectId.isValid(ret.home)) {
ret.homeId = ret.home;
delete ret.home;
}
if (mongoose.Types.ObjectId.isValid(ret.away)) {
ret.awayId = ret.away;
delete ret.away;
}
}
});
Without populate:
Input
{
"_id": "sD95OhsGrWVIqmTLVeuQdkna",
"leagueID": 1000,
"home": "404d1d9f68c3bb386b50f440" // ObjectId
"away": "504d1d9f68c3bb386b50f450" // ObjectId
}
Output
{
"_id": "sD95OhsGrWVIqmTLVeuQdkna",
"leagueID": 1000,
"homeId": "404d1d9f68c3bb386b50f440"
"awayId": "504d1d9f68c3bb386b50f450"
}
With populate:
Input
{
"_id": "sD95OhsGrWVIqmTLVeuQdkna",
"leagueID": 1000,
"home": "404d1d9f68c3bb386b50f440" // ObjectId
"away": "504d1d9f68c3bb386b50f450" // ObjectId
}
Output
{
"_id": "sD95OhsGrWVIqmTLVeuQdkna",
"leagueID": 1000,
"home": {
"_id": "404d1d9f68c3bb386b50f440",
"name": "Home"
}
"away": {
"_id": "504d1d9f68c3bb386b50f450",
"name": "Away"
}
}
You can use aggregation and manipulate the output field like this
db.collection.aggregate([{ $project:{_id:0, home:"$home_id", away:"$away_id"} }])
Try lodash's _.mapKeys, like this:
const newObject = _.mapKeys(oldObject.toJSON(), (value, key) => {
if (key === 'oldKey') return 'newKey';
return key;
});
Related
If there is an object with unknown keys:
{
data: {
someObjectIdStringThatCantBePutInProjection: {
dontReturn: 123,
return: 321
},
someOtherObjectIdStringThatCantBePutInProjection: {
dontReturn: 1234,
return: 4321
}
}
}
And I want MongoDB to return only return property of the objects of the objects, what would the projection look like?
For example a projection
{
data: { **allProperties**: { return: 1 } }
}
should return:
{
data: {
someObjectIdStringThatCantBePutInProjection: {
return: 321
},
someOtherObjectIdStringThatCantBePutInProjection: {
return: 4321
}
}
}
Using dynamic values as field names is considered an anti-pattern and introduces unnecessary complexity to queries. Nevertheless, you can convert the data object to an array of k-v tuples by $objectToArray. Use $map to get only the return field you need. Finally, use $arrayToObject to revert back to original form.
db.collection.aggregate([
{
"$set": {
"data": {
"$map": {
"input": {
"$objectToArray": "$data"
},
"as": "d",
"in": {
k: "$$d.k",
v: {
return: "$$d.v.return"
}
}
}
}
}
},
{
"$set": {
"data": {
"$arrayToObject": "$data"
}
}
}
])
Mongo Playground
So I am working on a generic Elastic search method. I can generate queries well but I need to find a way to check if a document has property x, it must match a value y
something like;
{
index: 'any-index',
query {
bool: { must: [...queries] },
// if has property companyId, only return objects with companyId == 4
}
}
I believe that a filter with exists-query can be a solution.
{
"query": {
"bool": {
"filter": [
{
"exists": {
"field": "field_name"
}
}
],
"must": [
{
"match": {
"field_name": "xpto"
}
}
]
}
}
}
I have following Json which i need to insert into a table.
I want to convert each student detail into a row.
Because if i loop through the rows as per the existing structure i am reading one column as a row.
var json {
"Students":[
{
"name":{
"value":"Allan"
},
"number":{
"value":"123"
}
},
{
"name":{
"value":"Frank"
},
"number":{
"value":"456"
}
}
]
}
Ideally i want to the above as
{ "name": "Allan", "number": 123};
{ "name": "Frank", "number": 456};
I am looping through the Json as below
var objectKeys = Object.keys(json);
for (var key in objectKeys)
{
var student = json.Students;
for (var i = 0; i < student .length; i++) {
for (var column in json.Students[i]) {
window.print(column);
window.print(json.Students[i][column].value);
}
}
}
NOTE: No JQuery, want to achieve the above through normal Javascript.
If you want to transform the data, you can use Array.map
var json = {"Students":[{"name":{"value":"Allan"},"number":{"value":"123"}},{"name":{"value":"Frank"},"number":{"value":"456"}}]};
let result = json.Students.map(o => ({
name: o.name.value,
number: o.number.value
}));
console.log(result);
If you want to access the data, you can use Array.forEach
var json = {"Students":[{"name":{"value":"Allan"},"number":{"value":"123"}},{"name":{"value":"Frank"},"number":{"value":"456"}}]};
json.Students.forEach(o => console.log({name: o.name.value, number: o.number.value}));
var json = {
"Students":[
{
"name":{
"value":"Allan"
},
"number":{
"value":"123"
}
},
{
"name":{
"value":"Frank"
},
"number":{
"value":"456"
}
}
]
}
var studentData = JSON.stringify(json.Students);
var convertedData = JSON.parse(studentData.replace(/\{\"value\"\:/g,"").replace(/\}\,\"number/g,',"number').replace(/\"\}\}/g,'"}'));
Try this :)
No map or reduce. Just classic Javascript.
var json = {
"Students": [{
"name": {
"value": "Allan"
},
"number": {
"value": "123"
}
},
{
"name": {
"value": "Frank"
},
"number": {
"value": "456"
}
}
]
};
for (var student of json["Students"]) {
console.log(student); //your logic goes here.
}
I am using the following code to call an API and return results:
api.jobs.all(function(response) {
const obj = response.data.map(function(item) {
return [item.id, item.billed.amountString];
});
});
With the following JSON:
{
"data": [
{
"id": 2090170,
"deadline": null,
"jobId": {
"id": 1644
},
"billed": {
"amountString": 200,
"currencyType": "CAD"
}
},
{
"id": 2090171,
"deadline": null,
"jobId": {
"id": 1645
},
"billed": {
"amountString": 400,
"currencyType": "USD"
}
}]}
The code is working fine, for the most part I am getting back good results, with the exception of: billed.amountString
I keep getting the following error:
TypeError: Cannot read property 'amountString' of null
Can anyone see why this would be returning null?
Also, is there a way in which I could loop through the API call and force it to do the following:
If .amountString === null, .amountString = "";
var response = {
"data": [
{
"id": 2090170,
"deadline": null,
"jobId": {
"id": 1644
},
"billed": {
"amountString": 200,
"currencyType": "CAD"
}
},
{
"id": 2090171,
"deadline": null,
"jobId": {
"id": 1645
},
"billed": {
"amountString": 400,
"currencyType": "USD"
}
}]};
const obj = (response.data).map(function(item) {
return [item.id, item.billed.amountString];
});
console.log(obj);
You could use the library lodash. The lodash method get can be used to try and access an object field. If it does not exist you can specify a default return value. See https://lodash.com/ .
// This will try to access item.billed.amountString
// If an item does not exist anywhere along the way
// it will return the default.
// _.get( OBJECT, PATH, DEFAULT )
_.get(item, ['billed', 'amountString'], '')
I have following object records:
{
"notes":[
{
"id":1,
"description":"hey",
"userId":2,
"replyToId":null,
"postId":2,
"parentId":null
},
{
"id":5,
"description":"hey test",
"userId":3,
"replyToId":null,
"postId":2,
"parentId":null
},
{
"id":2,
"description":"how are you",
"userId":null,
"replyToId":2,
"postId":2,
"parentId":null,
"user":null
}
]
}
I want to output it as:
2
object with id 1
object with id 2 (because replyToId value is same as userId
3
object with id 5
So basically I want to consider UserId and replyToId value under the same group.
I have build my own mixin under lodash, wrapping groupBy method as:
mixin({
splitGroupBy: function(list, groupByIter){
if (_.isArray(groupByIter)) {
function groupBy(obj) {
return _.forEach(groupByIter, function (key){
if ( !!obj[key] ) return obj[key]
});
}
} else {
var groupBy = groupByIter;
}
debugger;
var groups = _.groupBy(list, groupBy);
return groups;
}
});
Call looks like this:
_.splitGroupBy(data.notes,['userId', 'replyToId']);
The output is coming without group. Even when I have tried with _.map instead _.forEach the split is not happening correctly.
A solution using underscore:
var props = ['userId', 'replyToId'];
var notNull = _.negate(_.isNull);
var groups = _.groupBy(record.notes, function(note){
return _.find(_.pick(note, props), notNull);
});
This can probably done much prettier, but it should work:
lodash.mixin({
splitGroupBy: function(list, groupByIter) {
var _ = this, groupBy;
if (lodash.isArray(groupByIter)) {
groupBy = function(obj) {
return _(obj) .pick(groupByIter)
.values()
.without(null, undefined)
.first();
};
} else {
groupBy = groupByIter;
}
var groups = _.groupBy(list, groupBy);
return groups;
}
});
You can use stringify object as key.
_.groupBy(notes, ({ userId, replyToId }) => JSON.stringify({ userId, replyToId }));
output:
{
"{\"userId\":2,\"replyToId\":null}": [
{
"id": 1,
"description": "hey",
"userId": 2,
"replyToId": null,
"postId": 2,
"parentId": null
}
],
"{\"userId\":3,\"replyToId\":null}": [
{
"id": 5,
"description": "hey test",
"userId": 3,
"replyToId": null,
"postId": 2,
"parentId": null
}
],
"{\"userId\":null,\"replyToId\":2}": [
{
"id": 2,
"description": "how are you",
"userId": null,
"replyToId": 2,
"postId": 2,
"parentId": null,
"user": null
}
]
}
You could map your list of attributes to their respective values and pick the first non falsy value as your group key:
_.mixin({
splitGroupBy: function(list, groupByIter){
if (!_.isArray(groupByIter))
return _.groupBy(list, groupByIter);
return _.groupBy(list, function(o) {
var values = _.map(groupByIter, function(k) {
return o[k];
});
return _.find(values);
});
}
});
var data = {
"notes":[
{
"id":1,
"userId":2,
"replyToId":null
},
{
"id":5,
"userId":3,
"replyToId":null
},
{
"id":2,
"userId":null,
"replyToId":2
}
]
};
_.mixin({
splitGroupBy: function(list, groupByIter){
if (!_.isArray(groupByIter))
return _.groupBy(list, groupByIter);
return _.groupBy(list, function(o) {
var values = _.map(groupByIter, function(k) {
return o[k];
});
return _.find(values);
});
}
});
snippet.log(JSON.stringify(_.splitGroupBy(data.notes,['userId', 'replyToId'])));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Assuming userId and replyToId are mutually exclusive (i.e. you either have a userId or a replyToId, but never both) as they are in the sample data, then specifying a custom grouping function works:
_.groupBy(data.notes, function(note) {
return note.userId || note.replyToId;
});