MongoDB query AND/OR Precedent - javascript

In MongoDB, if I wanted to express A AND (B OR C), I can easily do by:
db.demo.find( { a: 1, $or: [ {b:1}, {c:1} ] });
But if I wanted (A AND B) OR C -- it does not seem to work. If I try:
db.demo.find( { $or: [ {a:1,b:1}, {c:1} ] });
the above is treating it as A or B or C, which is same as
db.demo.find( { $or: [ {a:1}, {b:1}, {c:1} ] });
At this point in time, I do not want to use Javascript expression $where to accomplish this (but will have no choice, if nothing else works) - the reason being, I am creating a general filter rules, which is being directly converted as MongoDB query.
Any help?

The query works actually, exactly as it should. The question is incorrect.
Here is what a view of what I tried and how it works!
Data:
> db.demo.find()
{ "_id" : 1, "age" : 38, "mgmt" : "no", "salary" : 100 }
{ "_id" : 2, "age" : 45, "salary" : 200, "mgmt" : "no" }
{ "_id" : 3, "age" : 50, "salary" : 250, "mgmt" : "no" }
{ "_id" : 4, "age" : 51, "salary" : 75, "mgmt" : "yes" }
{ "_id" : 5, "age" : 45, "salary" : 75, "mgmt" : "no" }
Query to get (Age>40 AND Salary>200) OR mgmt='yes'
> db.demo.find( { $or: [ { age: {$gt:40}, salary:{$gt:200} }, {mgmt: 'yes'} ] })
{ "_id" : 3, "age" : 50, "salary" : 250, "mgmt" : "no" }
{ "_id" : 4, "age" : 51, "salary" : 75, "mgmt" : "yes" }
Query to get (Age>40) OR (Salary>200) OR mgmt='yes'
> db.demo.find( { $or: [ { age: {$gt:40}}, {salary:{$gt:201} }, {mgmt: 'yes'} ] })
{ "_id" : 2, "age" : 45, "salary" : 200, "mgmt" : "no" }
{ "_id" : 3, "age" : 50, "salary" : 250, "mgmt" : "no" }
{ "_id" : 4, "age" : 51, "salary" : 75, "mgmt" : "yes" }
{ "_id" : 5, "age" : 45, "salary" : 75, "mgmt" : "no" }
All of them working as it should. Great!

Related

How to return the variant values of each product if that product is a variant?

I have a database in MongoDB like this
{"productId" : 1,
"isVariant": 1,
"variantId" : 1,
"attributeSet" : [
{
"name" : "Capacity",
"value" : "500 GB",
"id" : 3
},
{
"name" : "Form Factor",
"value" : "5 inch",
"id" : 4
},
{
"id" : 5,
"name" : "Memory Components",
"value" : "3D NAND"
}
]
},
{"productId" : 2,
"isVariant": 1,
"variantId" : 1,
"attributeSet" : [
{
"name" : "Capacity",
"value" : "1 TB",
"id" : 3
},
{
"name" : "Form Factor",
"value" : "5 inch",
"id" : 4
},
{
"id" : 5,
"name" : "Memory Components",
"value" : "3D NAND"
}
]
},
{"productId" : 3,
"isVariant": 1,
"variantId" : 1,
"attributeSet" : [
{
"name" : "Capacity",
"value" : "500 GB",
"id" : 3
},
{
"name" : "Form Factor",
"value" : "2.5 inch",
"id" : 4
},
{
"id" : 5,
"name" : "Memory Components",
"value" : "3D NAND"
}
]
},
{"productId" : 4,
"isVariant": 1,
"variantId" : 1,
"attributeSet" : [
{
"name" : "Capacity",
"value" : "1 TB",
"id" : 3
},
{
"name" : "Form Factor",
"value" : "2.5 inch",
"id" : 4
},
{
"id" : 5,
"name" : "Memory Components",
"value" : "3D NAND"
}
]
}
Now I want to return data where 500 GB has been in productId 1 and 3
The response should be like this:
variantValues : [{
attributeValue : "500 GB",
data : [
{productId : 1},
{productId : 3}
]},
{
attributeValue : "1 TB",
data : [
{productId : 2},
{productId : 4}
]},
{
attributeValue : "2.5 inch",
data : [
{productId : 3},
{productId : 4}
]},
{
attributeValue : "5 inch",
data : [
{productId : 1},
{productId : 2}
]}]
I have the possible values that I store in another collection for variantPossible values. The values that i am storing are like this:
"VariantValues" : {
"3" : [
"500 GB",
"1 TB"
],
"4" : [
"2.5 inch",
"5 inch"
]
},
I want to return the variant values of each product if that product is a variant with the above format. can anyone help me with this.
You should be able to achieve this using $unwind and $group in your aggregation pipeline. This first flattens each attribute into a single document and on those you can group by the attribute value.
Finally, you can use $project to get the desired name for attributeValue:
db.collection.aggregate([
{
$unwind: "$attributeSet"
},
{
$group: {
_id: "$attributeSet.value",
data: {
"$addToSet": {
productId: "$productId"
}
}
}
},
{
"$project": {
_id: 0,
data: 1,
attributeValue: "$_id"
}
}
])
See this simplifed example on mongoplayground: https://mongoplayground.net/p/VASadZnDedc

unable to group date and field in mongodb

I am trying to group date and name field then finding the count of each day.I am not able to differentiate date and time so My approach is doing the grouping based on date as well time in ISO format.
My approach:-
db.getCollection('blog').aggregate([
{ "$group": {
"_id": {"name":"$name","date":"$date"},
"Count": {
"$sum": {
"$sum": "$Count"
}
}
}},
{ "$project": {
"name": "$_id",
"Count": "$Count",
"_id": 0
}}
]).toArray()
input:-
{ "_id" : ObjectId("1"), "Count" : 4 , "name" : Ram, "date" : ISODate("2017-02-01T00:00:00Z") }
{ "_id" : ObjectId("2"), "Count" : 4, "name" : Arjun, "date" : ISODate("2017-01-08T00:00:00Z") }
{ "_id" : ObjectId("3"), "Count" :2 , "name" : Ram, "date" : ISODate("2017-02-01T00:00:00Z")}
{ "_id" : ObjectId("4"), "Count" : 2, "name" : Arjun, "date" : ISODate("2017-01-08T00:00:00Z") }
{ "_id" : ObjectId("5"), "Count" : 6, "name" : Arjun, "date" : ISODate("2017-01-08T00:00:00Z") }
{ "_id" : ObjectId("6"), "Count" : 6, "name" : Shyam, "date" : ISODate("2017-02-09T00:00:00Z")}
{ "_id" : ObjectId("7"), "count" : 1, "name" : Shyam, "date" : ISODate("2017-02-03T00:00:00Z") }
{ "_id" : ObjectId("8"), "loginID" : 2, "name" : Arjun, "date" : ISODate("2017-02-08T00:00:00Z") }
Expected output:-
{
name:Ram,
Count:6,
date:2017-02-01
}
{
name: Arjun,
Count:12,
date:2017-02-08
}
{
name: Arjun,
Count:2,
date:2017-01-08
}
....Something like that
You are using $sum twice in your $group stage, you only need it once.
db.getCollection('blog').aggregate([
{
$group: {
_id: {
name: "$name",
date: "$date"
},
Count: {
$sum: "$Count"
}
}
},
{
$project: {
name: "$_id.name",
date: "$_id.date",
Count: 1,
_id: 0
}
}
])

Add the X values in Json with respect to Image

In a Json, there are multiple image sources as "src" : "image.png", , "src" : "image2.png",
For image.png , right now i am fetching X value as "40" [3rd position in below image]
For image2.png , right now i am fetching X value as "100" [6th position in below image]
Requirement :
Instead of that, i need to add 1st (10) + 3rd (40) + 4th (50) positions values and fetch final X value for "src" : "image.png", as 100 [10+40+50] and
"src" : "image1.png" = 1st (10) + 6th (100) + 7th (105) positions & final value of X is 215....
Codepen : https://codepen.io/kidsdial/pen/zbKaEJ
let jsonData = {
"layers" : [
{
"x" : 10,
"layers" : [
{
"x" : 20,
"y" : 30,
"name" : "L2a"
},
{
"x" : 40,
"layers" : [
{
"x" : 50,
"src" : "image.png",
"y" : 60,
"name" : "L2b-1"
},
{
"x" : 70,
"y" : 80,
"name" : "L2b-2"
}
],
"y" : 90,
"name" : "user_image_1"
},
{
"x" : 100,
"layers" : [
{
"x" : 105,
"src" : "image2.png",
"y" : 110,
"name" : "L2C-1"
},
{
"x" : 120,
"y" : 130,
"name" : "L2C-2"
}
],
"y" : 140,
"name" : "L2"
}
],
"y" : 150,
"name" : "L1"
}
]
};
function getData(data) {
var dataObj = {};
let layer1 = data.layers;
let layer2 = layer1[0].layers;
for (i = 1; i < layer2.length; i++) {
var x = layer2[i].x;
var src = layer2[i].layers[0].src;
document.write("src :" + src);
document.write("<br>");
document.write("x:" + x);
document.write("<br>");
}
}
getData(jsonData);
Using a recursive function you can find all the src and its corresponding summed x values.
Below is a crude example. Refactor as you see fit.
let jsonData = {
"layers" : [
{
"x" : 10,
"layers" : [
{
"x" : 20,
"y" : 30,
"name" : "L2a"
},
{
"x" : 40,
"layers" : [
{
"x" : 50,
"src" : "image.png",
"y" : 60,
"name" : "L2b-1"
},
{
"x" : 70,
"y" : 80,
"name" : "L2b-2"
}
],
"y" : 90,
"name" : "user_image_1"
},
{
"x" : 100,
"layers" : [
{
"x" : 105,
"src" : "image2.png",
"y" : 110,
"name" : "L2C-1"
},
{
"x" : 120,
"y" : 130,
"name" : "L2C-2"
}
],
"y" : 140,
"name" : "L2"
}
],
"y" : 150,
"name" : "L1"
}
]
};
function getAllSrc(layers){
let arr = [];
layers.forEach(layer => {
if(layer.src){
arr.push({src: layer.src, x: layer.x});
}
else if(layer.layers){
let newArr = getAllSrc(layer.layers);
if(newArr.length > 0){
newArr.forEach(({src, x}) =>{
arr.push({src, x: (layer.x + x)});
});
}
}
});
return arr;
}
function getData(data) {
let arr = getAllSrc(data.layers);
for (let {src, x} of arr){
document.write("src :" + src);
document.write("<br>");
document.write("x:" + x);
document.write("<br>");
}
}
getData(jsonData);
Here is the CodePen for the same: https://codepen.io/anon/pen/Wmpvre
Hope this helps :)
You can do that using recursion. While going though nested obj you could parent x value each item in layers and check if the element in layers have src property add the previous x value to it.
let jsonData = {
"layers" : [
{
"x" : 10,
"layers" : [
{
"x" : 20,
"y" : 30,
"name" : "L2a"
},
{
"x" : 40,
"layers" : [
{
"x" : 50,
"src" : "image.png",
"y" : 60,
"name" : "L2b-1"
},
{
"x" : 70,
"y" : 80,
"name" : "L2b-2"
}
],
"y" : 90,
"name" : "user_image_1"
},
{
"x" : 100,
"layers" : [
{
"x" : 105,
"src" : "image2.png",
"y" : 110,
"name" : "L2C-1"
},
{
"x" : 120,
"y" : 130,
"name" : "L2C-2"
}
],
"y" : 140,
"name" : "L2"
}
],
"y" : 150,
"name" : "L1"
}
]
};
function changeData(obj,x=0){
if(obj.src) obj.x += x;
if(obj.layers){
for(const item of obj.layers){
changeData(item,x+obj.x || 0);
}
}
}
changeData(jsonData);
function showData(obj){
if(obj.src) document.body.innerHTML += `src:${obj.src}<br>
x:${obj.x}<br>`;
if(obj.layers){
for(let i of obj.layers) showData(i)
}
}
showData(jsonData);
console.log(jsonData);
Here is my solution with recursion and mutation. You can cloneDeep the array before if you don't want to mutate it directly.
// Code
function recursion(obj, currentX = 0) {
if(obj.src) obj.x = currentX
else if(obj.layers) {
obj.layers.forEach(subObj => recursion(subObj, (currentX + subObj.x)))
}
}
// Data
let jsonData = {
"layers" : [
{
"x" : 10,
"layers" : [
{
"x" : 20,
"y" : 30,
"name" : "L2a"
},
{
"x" : 40,
"layers" : [
{
"x" : 50,
"src" : "image.png",
"y" : 60,
"name" : "L2b-1"
},
{
"x" : 70,
"y" : 80,
"name" : "L2b-2"
}
],
"y" : 90,
"name" : "user_image_1"
},
{
"x" : 100,
"layers" : [
{
"x" : 105,
"src" : "image2.png",
"y" : 110,
"name" : "L2C-1"
},
{
"x" : 120,
"y" : 130,
"name" : "L2C-2"
}
],
"y" : 140,
"name" : "L2"
}
],
"y" : 150,
"name" : "L1"
}
]
};
// Use
recursion(jsonData)
console.log(jsonData)
let jsonData = {
"layers": [{
"x": 10,
"layers": [{
"x": 20,
"y": 30,
"name": "L2a"
},
{
"x": 40,
"layers": [{
"x": 50,
"src": "image.png",
"y": 60,
"name": "L2b-1"
},
{
"x": 70,
"y": 80,
"name": "L2b-2"
}
],
"y": 90,
"name": "user_image_1"
},
{
"x": 100,
"layers": [{
"x": 105,
"src": "image2.png",
"y": 110,
"name": "L2C-1"
},
{
"x": 120,
"y": 130,
"name": "L2C-2"
}
],
"y": 140,
"name": "L2"
}
],
"y": 150,
"name": "L1"
}]
};
function getData(data) {
var dataObj = {};
let layer1 = data.layers;
let layer2 = layer1[0].layers;
let y = layer1[0].x;
for (i = 1; i < layer2.length; i++) {
var x = y;
x += layer2[i].x;
x += layer2[i].layers[0].x;
var src = layer2[i].layers[0].src;
document.write("src :" + src);
document.write("<br>");
document.write("x:" + x);
document.write("<br>");
}
}
getData(jsonData);
Here is My Solution. Check this out
let jsonData = {
"layers" : [
{
"x" : 10, //
"layers" : [
{
"x" : 20,
"y" : 30,
"name" : "L2a"
},
{
"x" : 40, //
"layers" : [
{
"x" : 50, //
"src" : "image.png",
"y" : 60,
"name" : "L2b-1"
},
{
"x" : 70,
"y" : 80,
"name" : "L2b-2"
}
],
"y" : 90,
"name" : "user_image_1"
},
{
"x" : 100,
"layers" : [
{
"x" : 105,
"src" : "image2.png",
"y" : 110,
"name" : "L2C-1"
},
{
"x" : 120,
"y" : 130,
"name" : "L2C-2"
}
],
"y" : 140,
"name" : "L2"
}
],
"y" : 150,
"name" : "L1"
}
]
};
var dataObj = [];
var x, src;
function getData(data) {
let layer1 = data.layers;
for(var i = 0; i < layer1.length; i++){
if(x === 0){
x = layer1[i].x;
continue;
}
x = layer1[i].x;
if(typeof layer1[i].layers !== 'undefined')
createOutput(layer1[i].layers);
}
return dataObj;
}
function createOutput(dataArray){
if(typeof dataArray === 'undefined'){
dataObj.push({x: x, src: src});
x = 0;
getData(jsonData);
return;
}
dataArray.forEach(element => {
if(typeof element.layers !== 'undefined' || typeof element.src !== 'undefined'){
x += element.x;
src = element.src;
createOutput(element.layers);
}
})
}
console.log(JSON.stringify(getData(jsonData)));

Group by Day and Item Total, but Output Item Names as Keys

I've been trying these examples : https://docs.mongodb.com/manual/reference/operator/aggregation/push/ and
https://docs.mongodb.com/manual/reference/operator/aggregation/addToSet/
Sample documents:
{ "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2, "date" : ISODate("2014-01-01T08:00:00Z") }
{ "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1, "date" : ISODate("2014-02-03T09:00:00Z") }
{ "_id" : 3, "item" : "xyz", "price" : 5, "quantity" : 5, "date" : ISODate("2014-02-03T09:05:00Z") }
{ "_id" : 4, "item" : "abc", "price" : 10, "quantity" : 10, "date" : ISODate("2014-02-15T08:00:00Z") }
{ "_id" : 5, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-02-15T09:05:00Z") }
{ "_id" : 6, "item" : "xyz", "price" : 5, "quantity" : 5, "date" : ISODate("2014-02-15T12:05:10Z") }
{ "_id" : 7, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-02-15T14:12:12Z") }
But my need is kind of mixes of them. In push example, the results look like:
{
"_id" : { "day" : 46, "year" : 2014 },
"itemsSold" : [
{ "item" : "abc", "quantity" : 10 },
{ "item" : "xyz", "quantity" : 10 },
{ "item" : "xyz", "quantity" : 5 },
{ "item" : "xyz", "quantity" : 10 }
]
}
{
"_id" : { "day" : 34, "year" : 2014 },
"itemsSold" : [
{ "item" : "jkl", "quantity" : 1 },
{ "item" : "xyz", "quantity" : 5 }
]
}
{
"_id" : { "day" : 1, "year" : 2014 },
"itemsSold" : [ { "item" : "abc", "quantity" : 2 } ]
}
And in $addToSet example, results look like:
{ "_id" : { "day" : 46, "year" : 2014 }, "itemsSold" : [ "xyz", "abc" ] }
{ "_id" : { "day" : 34, "year" : 2014 }, "itemsSold" : [ "xyz", "jkl" ] }
{ "_id" : { "day" : 1, "year" : 2014 }, "itemsSold" : [ "abc" ] }
What I want is going to be like:
{ "_id" : { "day" : 46, "year" : 2014 }, "itemsSold" : { "xyz": 25, "abc": 10 } }
{ "_id" : { "day" : 34, "year" : 2014 }, "itemsSold" : { "xyz": 5, "jkl": 1 ] }
{ "_id" : { "day" : 1, "year" : 2014 }, "itemsSold" : { "abc": 2 } }
Is this possible? If it is, any guide, direction would be helpful.
Based on your data you want two $group stages, in order to first collect per "item" and then to add those item details to an array.
Depending on your MongoDB version you have available is how you process the rest. For MongoDB 3.6 ( of from 3.4.7 ) you can use $arrayToObject in order to reshape the data:
db.collection.aggregate([
{ "$group": {
"_id": {
"year": { "$year": "$date" },
"dayOfYear": { "$dayOfYear": "$date" },
"item": "$item"
},
"total": { "$sum": "$quantity" }
}},
{ "$group": {
"_id": {
"year": "$_id.year",
"dayOfYear": "$_id.dayOfYear"
},
"itemsSold": { "$push": { "k": "$_id.item", "v": "$total" } }
}},
{ "$sort": { "_id": -1 } },
{ "$addFields": {
"itemsSold": { "$arrayToObject": "$itemsSold" }
}}
])
Or with earlier versions, you can simply post process the results. All the "aggregation" work is done before the last stage anyway:
db.collection.aggregate([
{ "$group": {
"_id": {
"year": { "$year": "$date" },
"dayOfYear": { "$dayOfYear": "$date" },
"item": "$item"
},
"total": { "$sum": "$quantity" }
}},
{ "$group": {
"_id": {
"year": "$_id.year",
"dayOfYear": "$_id.dayOfYear"
},
"itemsSold": { "$push": { "k": "$_id.item", "v": "$total" } }
}},
{ "$sort": { "_id": -1 } },
/*
{ "$addFields": {
"itemsSold": { "$arrayToObject": "$itemsSold" }
}}
*/
]).map( d => Object.assign( d,
{
itemsSold: d.itemsSold.reduce((acc,curr) =>
Object.assign(acc, { [curr.k]: curr.v }),
{}
)
}
))
Either way produces the same desired result:
{
"_id" : {
"year" : 2014,
"dayOfYear" : 46
},
"itemsSold" : {
"xyz" : 25,
"abc" : 10
}
}
{
"_id" : {
"year" : 2014,
"dayOfYear" : 34
},
"itemsSold" : {
"jkl" : 1,
"xyz" : 5
}
}
{
"_id" : {
"year" : 2014,
"dayOfYear" : 1
},
"itemsSold" : {
"abc" : 2
}
}
So you can do things with new aggregation features, but really that end result is just "reshaping" which is usually best left to client processing instead.

data manipulation in javascript

i have an array of information containing folders like this:
[
{
"ACLID" : 0,
"ID" : 100,
"auditInfoVersion" : 3435973836,
"createBy" : 2,
"createDate" : "04/12/2012 17:38:46",
"isSubFolder" : 0,
"name" : "Piyush1",
"objectType" : 3,
-->"parentID" : 3,
"policyID" : 2,
"sDescription" : "",
"updateBy" : 2,
"updateDate" : "04/12/2012 17:38:46"
},
{
"ACLID" : 0,
"ID" : 102,
"auditInfoVersion" : 3435973836,
"createBy" : 2,
"createDate" : "05/10/2012 12:38:25",
"isSubFolder" : 0,
"name" : "New",
"objectType" : 3,
-->"parentID" : 3,
"policyID" : 2,
"sDescription" : "",
"updateBy" : 2,
"updateDate" : "05/10/2012 12:38:25"
},
{
"ACLID" : 0,
"ID" : 103,
"auditInfoVersion" : 3435973836,
"createBy" : 2,
"createDate" : "05/14/2012 18:00:22",
"isSubFolder" : 0,
"name" : "SegFolderWithDiffPolicy",
"objectType" : 3,
-->"parentID" : 3,
"policyID" : 39,
"sDescription" : "",
"updateBy" : 2,
"updateDate" : "05/14/2012 18:00:22"
},
{
"ACLID" : 0,
"ID" : 104,
"auditInfoVersion" : 3435973836,
"createBy" : 2,
"createDate" : "05/17/2012 14:13:56",
"isSubFolder" : 0,
"name" : "New1",
"objectType" : 3,
-->"parentID" : 100,
"policyID" : 2,
"sDescription" : "",
"updateBy" : 2,
"updateDate" : "05/17/2012 14:13:56"
},
{
"ACLID" : 0,
"ID" : 105,
"auditInfoVersion" : 3435973836,
"createBy" : 2,
"createDate" : "05/17/2012 14:14:13",
"isSubFolder" : 0,
"name" : "abcasdna",
"objectType" : 3,
-->"parentID" : 104,
"policyID" : 2,
"sDescription" : "",
"updateBy" : 2,
"updateDate" : "05/17/2012 14:14:13"
},
{
"ACLID" : 0,
"ID" : 106,
"auditInfoVersion" : 3435973836,
"createBy" : 2,
"createDate" : "05/18/2012 12:51:39",
"isSubFolder" : 0,
"name" : "new2",
"objectType" : 3,
-->"parentID" : 100,
"policyID" : 2,
"sDescription" : "",
"updateBy" : 2,
"updateDate" : "05/18/2012 12:51:39"
}
]
now the required type of data is:
[{
name:'Piyush1',
children: [{
name: 'New1',
children:[{
name: 'abcasdna'
}]
},{
name: 'New2'
}]
},{
name: 'New'
}]
Now the thing is that there could be any number of folders and any level of hierarchy.
so is there a way i can achieve this conversion?

Categories

Resources