I need to achieve such collection structure:
{
"_id" : "36JaTS8N9uJDHabaq",
"pages" : {
1 : {
"field1" : "value1",
"field2" : "value2"
},
...
}
}
Everything is fine, when I'm doing the following:
Collection.insert({
"pages": {
1: {
"field1" : "value1",
"field2" : "value2"
}
}
})
But when I'm trying to use the variable containing the number instead of number itself, like:
var number = 1;
Collection.insert({
"pages": {
number: {
"field1" : "value1",
"field2" : "value2"
}
}
})
I receive not the result I want (there is a variable name where number should be):
{
"_id" : "SjMotZHrtXviuoyqv",
"pages" : {
"number" : {
"field1" : "value1",
"field2" : "value2"
}
}
}
What is the right way to do what I'm trying to achieve?
Thanks in advance!
You can try something like this :
var doc={
pages:{}
};
var number="1";
doc.pages[number]={
"field1":"value1",
"field2":"value2"
};
Collection.insert(doc);
You can't use variables as keys in object literals in javascript. See this answer.
var number = 1;
var pages = {};
pages[number] = {f1: 'v1', f2: 'v2'};
Collection.insert({pages: pages});
Related
I am currently working in a project that has insisted in explicitly defining over 1,700 questions into a JSON data schema for an API and its getting out of control. I have been suggesting a more generic structure to the schema and let the data do the talking to tell you what the context is.
Whilst there are debates happening around which schema to use, we have decided on our internal systems, to go ahead and use a more generic model even if the externally facing model is the explicit one. This means we need an adapter to convert from one to the other, until such time as we can just use the one we wanted in the first place.
The business is a Java shop, I don't know whether to advise to build the adapter in Java or whether we can incorporate some lightweight JavaScript to do the work, maybe in the form of a configuration.
My question is: How would you approach converting the first JSON example into the second JSON example? It maps from explicitly defined objects to generic objects in arrays. Thanks for considering my question.
Example One
{
"nested_object" : {
"department_one" : {
"floor" : "4",
"product_one" : {
"quantity" : 10,
"size" : "L"
},
"product_two" : {
"quantity" : 5,
"size" : "S"
}
},
"department_two" : {
"floor" : "2",
"product_thirteen" : {
"quantity" : 1,
"size" : "M"
},
"product_eleven" : {
"quantity" : 8,
"size" : "L"
}
}
}
}
Example Two
{
"departments" : [
{
"department_name" : "department_one",
"floor" : "4",
"products" : [
{
"product_name" : "product_one",
"quantity" : 10,
"size" : "L"
},
{
"product_name" : "product_two",
"quantity" : 5,
"size" : "S"
}
]
},
{
"department_name" : "department_two",
"floor" : "2",
"products" : [
{
"product_name" : "product_thirteen",
"quantity" : 1,
"size" : "M"
},
{
"product_name" : "product_eleven",
"quantity" : 8,
"size" : "L"
}
]
}
]
}
You could use a combination of Object.keys (to grab product and department names). Below is a quick implementation.
const obj1 = {
"nested_object" : {
"department_one" : {
"floor" : "4",
"product_one" : {
"quantity" : 10,
"size" : "L"
},
"product_two" : {
"quantity" : 5,
"size" : "S"
}
},
"department_two" : {
"floor" : "2",
"product_thirteen" : {
"quantity" : 1,
"size" : "M"
},
"product_eleven" : {
"quantity" : 8,
"size" : "L"
}
}
}
}
const transformedObj = {
departments: [ ],
};
//holds all department names
const departmentKeys = Object.keys(obj1.nested_object)
const departmentsArr = departmentKeys.map((key) => {
const floor = obj1.nested_object[key].floor
//remove floor reference, since we already stored the value above
delete obj1.nested_object[key].floor
//holds all product names
const productsKeysArr = Object.keys(obj1.nested_object[key])
//holds all product objects for respective department
const productsArr = productsKeysArr.map((product) => {
const quantity = obj1.nested_object[key][product].quantity
const size = obj1.nested_object[key][product].size
return {
product_name: product,
quantity: quantity,
size: size
}
})
return {
department_name: key,
floor: floor,
products: productsArr
}
})
//assign departments array to transformed object
transformedObj.departments = departmentsArr
console.log(transformedObj)
This would be my take on this. I like conciseness and expressiveness in implementations:
const data = { "nested_object": { ... }}
Object.entries(data.nested_object).map(([department_name, {floor, ...ps}]) => ({
department_name,
floor,
products: Object.entries(ps).map(([product_name, p]) => ({product_name, ...p}))
}))
Here is MongoDB scheme.
{
"_id" : ObjectId("222222"),
"active" : false,
"amount" : "15%",
"description" : "15% discount",
"name" : "20200628-test",
"policies" : {
"apply" : [
{
"name" : "expiryDate",
"params" : {
"date" : ISODate("2020-07-06T14:59:59.999Z")
}
},
{
"name" : "isApplyCategoryExist"
}
],
"discount" : [],
"conflict" : [
{
"name" : "exclusive"
}
],
"grant" : []
},
"target" : {
"sku" : "",
"products_ids" : [],
"category_ids" : [
ObjectId("11111111")
]
},
"title" : "15% coupon"
}
I want to access date.
For example, "policies.apply.params.date"...
I don't know how to access 'date' to Javascript.
Please let me know...
apply is an array, so you have to give it index which you want to get.
var num = 0; // pick up an array number you want
var date = policies.apply[num].params.date;
Your policies.apply is an array so if you want to access "2020-07-06T14:59:59.999Z", you should do this:
policies.apply[0].params.date
But the "policies.apply[1]" doesn't have params (params.date also) so you can write a function to get date like this:
function get_apply_date(index) {
if(policies.apply[index].params && policies.apply[index].params.date)
return policies.apply[index].params.date;
return undefined; // or null
}
I have multiple users in my collection and each user has an object of sessions shown below
{
user: 'adam',
sessions: {
key: some-value,
key: some-value
}
}
{
user: 'chris',
sessions: {
key: some-value,
key: some-value
}
}
I fetch the document on the basis of the user name. let us say i want the sessions of adam.
So I run db.collection.find({ user: 'Adam' }) which gives me full document of adam. Now I want the sessions of 'Adam' but the sessions object should be converted into an array of objects like this :
{
user: 'adam'
sessions : [ { key: value },{ key: value } ]
}
I want to convert the sessions of user into an object that means i want to do $objectToArray but on a specific username not the full collection.
Just the user I want.
Explanation:
I am running node as backend and i have a route called 'getuserdata' which gets data for a specific user based on the username. So when i will hit that route with username adam and it will get the data from the DB. The sessions of adam should come in an array of objects like shown above instead of an object which is in the db.
What i am doing
I am running a for-in loop on the back-end to convert them into array of objects but it is slow.
You can do it using $map in project part,
$map input sessions as array to convert using $objectToArray,
inside map convert array of object key and value to $arrayToObject
db.collection.find(
{ user: "adam" },
{
user: 1,
sessions: {
$map: {
input: { $objectToArray: "$sessions" },
in: { $arrayToObject: [["$$this"]] }
}
}
})
Playground
//run from mongodb client
//sample user data preparation
> db.users15.find();
{ "_id" : ObjectId("5f4fbfbc3372ab5da3a605ac"), "user" : "adam", "sessions" : { "key1" : 1123, "key2" : 1124 } }
{ "_id" : ObjectId("5f4fbfbc3372ab5da3a605ad"), "user" : "chris", "sessions" : { "key1" : 2122, "key2" : 2134 } }
// Mongo query, variable defined an user for dynamic passing in match condition
> var v_user = "adam"
> db.users15.aggregate([
... {$match:{user:v_user}},
... {$project:{user:1,
... sessionArray:{$objectToArray:"$sessions"}
... }
... }
... ]);
//output showing conversion from session object to array for key value pair
{ "_id" : ObjectId("5f4fbfbc3372ab5da3a605ac"), "user" : "adam", "sessionArray" : [ { "k" : "key1", "v" : 1123 }, { "k" : "key2", "v" : 1124 } ] }
>
//Further,the mongo query gives correct output for nested objects as well.
//Example below.
> db.users15.find().pretty();
{
"_id" : ObjectId("5f4fbfbc3372ab5da3a605ac"),
"user" : "adam",
"sessions" : {
"key1" : 1123,
"key2" : 1124
}
}
{
"_id" : ObjectId("5f4fbfbc3372ab5da3a605ad"),
"user" : "chris",
"sessions" : {
"key1" : 2122,
"key2" : 2134
}
}
{
"_id" : ObjectId("5f5069ed3372ab5da3a605ae"),
"user" : "sriny",
"sessions" : {
"key1" : 2122,
"key2" : 2134,
"key3" : {
"server1" : 2344,
"server2" : 9999
}
}
}
>
> var v_user = "sriny"
> db.users15.aggregate([
... {$match:{user:v_user}},
... {$project:{user:1,
... sessionArray:{$objectToArray:"$sessions"}
... }
... }
... ]).pretty();
{
"_id" : ObjectId("5f5069ed3372ab5da3a605ae"),
"user" : "sriny",
"sessionArray" : [
{
"k" : "key1",
"v" : 2122
},
{
"k" : "key2",
"v" : 2134
},
{
"k" : "key3",
"v" : {
"server1" : 2344,
"server2" : 9999
}
}
]
}
>
Take for example this object
const obj = {
key1: "value1",
key2: "value2"
}
We can map this to pattern [ { key1: value1 }, { key2: value2 } ] using a map on the object keys
const arrayFromObj = Object.keys(obj).map(value => {
return { value: obj[value] }
})
I have an object of the following kind
let obj = {
"p1" : "main",
"p2" : {
"p21" : [
{
"key1" : "val1",
"value1" : "val2",
},
{
"prop" : "test",
"value" : "dummy"
}
]
}
}
Need to find the "prop", and if its present set the "value" to empty string. Note there might be any number objects inside "p21" and in any order. Just need to find "prop" and then update "value" to empty string
Have tried the following
obj.p2.p21.map((item) => {
if (item.hasOwnProperty("prop")) {
item.value = "";
}
})
You need to create a function to check the key and then replace value.
let obj = {
"p1": "main",
"p2": {
"p21": [{
"key1": "val1",
"value1": "val2",
},
{
"prop": "test",
"value": "dummy"
}
]
}
}
function replaceValue(item) {
for (var i in item) {
if (i == "prop") {
item["value"] = "";
break;//break if you only want to place the first , otherwise remove it
}
}
}
obj.p2.p21.map(item => replaceValue(item));
console.log(obj);
.as-console {
height: 100% !important;
}
obj.p2.p21.map((elem) => {
if(Object.keys(elem)[0] === "prop")
elem.value = ""
})
try something like that?
I have data like this, and want to calculate how many i have true/false values for all fields
{
"key1" : true,
"key2" : false,
"key3" : true
},
{
"key1" : false,
"key2" : true,
"key3" : true
}
Expected result is
{
key1: { true: 1, false: 1 },
key2: { true: 1, false: 1 },
key3: { true: 2, false: 0 }
}
I can calc it for specific field with group by operator, but i don't know how to do this for all fields in collection
Closest you can get is this:
db.foo.aggregate([{
$group: {
_id: null,
key1 : {
$sum: {
$cond: {
if: "$key1",
then: 1,
else: 0
}
}
},
key2 : {
$sum: {
$cond: {
if: "$key2",
then: 1,
else: 0
}
}
},
key3 : {
$sum: {
$cond: {
if: "$key3",
then: 1,
else: 0
}
}
}
}
}])
Which will give you:
{
"_id" : null,
"key1" : 1,
"key2" : 1,
"key3" : 2
}
If you have no idea of the number of keys' name in you document, then you can't use the aggregation framework, instead what you need here is mapReduce and output the map reduce result into a new collection using the out option or display the result in the shell using inline: 1. Here we the option is used because we need extra processing step in order to get the expect result.
db.collection.mapReduce(
function() {
var keys = Object.keys(this);
for(var ind=0; ind<keys.length; ind++) {
if (keys[ind] !== "_id") {
var d = {};
d.name = keys[ind],
d.value= this[keys[ind]];
emit(d, 1);
}
}
},
function(key, values) { return Array.sum(values); },
{ "out": "mresult" }
)
which returns something like this:
{
"result" : "mresult",
"timeMillis" : 566,
"counts" : {
"input" : 2,
"emit" : 6,
"reduce" : 1,
"output" : 5
},
"ok" : 1
}
Five documents where saved in the newly created collection as shown by the mapReduce output. You can easily very this using the .find()
db.mresult.find()
which yields something like this:
{ "_id" : { "name" : "key1", "value" : false }, "value" : 1 }
{ "_id" : { "name" : "key1", "value" : true }, "value" : 1 }
{ "_id" : { "name" : "key2", "value" : false }, "value" : 1 }
{ "_id" : { "name" : "key2", "value" : true }, "value" : 1 }
{ "_id" : { "name" : "key3", "value" : true }, "value" : 2 }
As you can see even using mapReduce we can't get the expected result which is somehow a bit annoying, but these documents can easily be processed using the .aggregate method.
The first stage in you pipeline is the $project stage where you basically use the $cond conditional operator to "set" the value of "true" and "false". The last stage in the pipeline is the $group stage where you group your documents and use the $sum accumulator operator to return the sum for each group.
db.mresult.aggregate([
{ "$project": {
"true": {
"$cond": [
{ "$eq": [ "$_id.value", true ] },
"$value",
0
]
},
"false": {
"$cond": [
{ "$eq": [ "$_id.value", false ] },
"$value",
0
]
}
}},
{ "$group": {
"_id": "$_id.name",
"true": { "$sum": "$true" },
"false": { "$sum": "$false" }
}}
])
which produces something like this:
{ "_id" : "key3", "true" : 2, "false" : 0 }
{ "_id" : "key2", "true" : 1, "false" : 1 }
{ "_id" : "key1", "true" : 1, "false" : 1 }
Of course this is not exactly you expected output but much more better because, generally speaking using data as key is not a good idea.
I would go with callback passed to find query. it will give you more flexibility.
Sample.find({}, function (err, docs) {
if (!err) {
var result = {};
for (var i = 0; i < docs.length; i++) {
var currDoc = docs[i]._doc;
delete currDoc._id;
Object.keys(currDoc).forEach(function (key) {
var processedKey = result[key] ? result[key] : {"false": 0, "true": 0};
var count = (processedKey["" + currDoc[key]] | 0 ) + 1;
processedKey["" + currDoc[key]] = count;
result[key] = processedKey;
});
}
console.log(result);
process.exit();
} else {
throw err;
}
});
for input
[
{
"key1": true,
"key2": false,
"key3": true
},
{
"key1": true,
"key2": false,
"key3": true
}
]
It will output
{
key3: {
false: 0,
true: 2
},
key2: {
false: 2,
true: 0
},
key1: {
false: 0,
true: 2
}
}