Convert $objectToArray map element to String - javascript

I have a collection in database that I am trying to retrieve some data from it , the query is working fine when $orderID has string elements , but is failing when $orderID has some numbers in array , and it is throwing
query failed: (Location40395) PlanExecutor error during aggregation :: caused by :: $arrayToObject requires an array of key-value pairs, where the key must be of type string. Found key type: double
I think there must be some old data when we were saving orderID as a number so that is why it is failing from some range of dates
Query
{
"Order_Details": {
"$map": {
"input": {
"$objectToArray": {
"$arrayToObject": {
"$zip": {
"inputs": [
"$orderID",
"$total_value_of_order"
]
}
}
}
},
"as": "el",
"in": {
"orderID": "$$el.k",
"total_value_of_order": "$$el.v"
}
}
}
}
I am trying to typecast el.k to string I am using $toString but can't seem to work , the way I am trying it is
{
"as": "el",
"in": {
"orderID": {
"$toString": "$$el.k"
},
"total_value_of_order": "$$el.v"
}
}
Example collection
[
{
"_id": ObjectId("5e529ee5f8647eb59e5620a2"),
"visitID": "dVmy7flXFHzzkn9HiMt8IoWvthoTZW",
"date": ISODate("2022-02-08T16:29:13.413Z"),
"control": true,
"orderID": [
122343242
],
"target": "test",
"total_value_of_order": [
60
]
}
]

You are close, the approach is fine. you just have a couple of syntax issues.
The major thing that needs to change is the input for $arrayToObject, currently your input looks like this:
[[number, number], [number, number]]
However $arrayToObject expects input in a certain format:
[{k: string, v: value}]
So this it what we'll add, like so:
db.collection.aggregate([
{
$project: {
"Order_Details": {
"$map": {
"input": {
"$objectToArray": {
"$arrayToObject": {
$map: {
input: {
"$zip": {
"inputs": [
"$orderID",
"$total_value_of_order"
]
}
},
in: {
k: {
$toString: {
"$arrayElemAt": [
"$$this",
0
]
}
},
v: {
"$arrayElemAt": [
"$$this",
1
]
}
}
}
}
}
},
"as": "el",
"in": {
"orderID": "$$el.v",
"total_value_of_order": "$$el.k"
}
}
}
}
}
])
Mongo Playground
Notice the "orderid" format changes to string which affects it's structure, I recommend just switching between the k and v in the pipeline, like this

Related

How to get object value with dynamic key in $project in mongoDB

I want to retrieve a value from an object with a dynamic key
[
{
"_id": 1,
"item": "sweatshirt",
"price": {
"INR": 45.99
},
"currency": 'INR'
}
]
db.collection.aggregate([
{
"$project": {
"pricenew": "$price.currency"
}
}
])
If I do price.INR it will work fine but here I have currency dynamic, so I want something like price.currency but here currency is coming like "INR" and it gives no data.
I really appreciate any help you can provide.
You need to convert the price object to an array using $objectToArray, filter it and then convert it back, like so:
db.collection.aggregate([
{
$replaceRoot: {
newRoot: {
"$mergeObjects": [
{
"$arrayToObject": {
$map: {
input: {
$filter: {
input: {
"$objectToArray": "$price"
},
cond: {
$eq: [
"$$this.k",
"$currency"
]
}
}
},
in: {
k: "pricenew",
v: "$$this.v"
}
}
}
},
{
_id: "$_id"
}
]
}
}
}
])
Mongo Playground

how to query in mongodb with loop in same collection, until i find null or empty value?

I have stacked in a nested object. here is my collection.
{
"key": 1,
"subKey": ""
},
{
"key": 2,
"subKey": 1
},
{
"key": 3,
"subKey": 2
},
{
"key": 4,
"subKey": 3
}
I want to query Key:4, which gives me result
{
"key": 4,
"subKey": 3
}
after getting result i want to query "subKey": 3 as a key:"$subKey" and i want to run a loop, until i find a empty subKey in our case It is Key:1. and whenever i found an empty subKey i want it document as a parent.
In the end, I want the result
{
"key": 4,
"parent":{"key":1,"subKey":"",....}
}
or similar.
Is it possible by using MongoDB built-in function? if not available how do I achieve this goal?
also, I want an alternative solution for it if there is.
You can achieve using $graphLookup
play
db.collection.aggregate([
{
$graphLookup: {
from: "collection",
startWith: "$key",
connectFromField: "subKey",
connectToField: "key",
as: "keys"
}
}
])
If you want a match filter add it,
play
db.collection.aggregate([
{
$match: {
key: 4
}
},
{
$graphLookup: {
from: "collection",
startWith: "$key",
connectFromField: "subKey",
connectToField: "key",
as: "keys"
}
}
])
Important consideration:
The $graphLookup stage must stay within the 100 MiB memory limit. If allowDiskUse: true is specified for the aggregate() operation, the $graphLookup stage ignores the option
To transform the data, you cannot have duplicate keys in parent object. So parent should be an array
play
db.collection.aggregate([
{
$match: {
key: 4
}
},
{
$graphLookup: {
from: "collection",
startWith: "$key",
connectFromField: "subKey",
connectToField: "key",
as: "keys"
}
},
{
"$addFields": {
"parent": {
"$map": {
"input": "$keys",
"as": "res",
"in": {
"key": "$$res.key",
"subKey": "$$res.subKey"
}
}
},
"key": "$key",
}
},
{
$project: {
keys: 0
}
}
])

How do I get all attributes which are numeric types in mongo db?

I need to extract all the attributes which are of numeric types. For example, if the different attributes are
{
age: 32
gender: "female"
year: 2020
name: "Abc"
}
My query should return ["age","year"]
I think the below query should help you out.
db.test.aggregate([
// Remove this `$limit` stage if your Collection schema is dynamic and you want to process all the documents instead of just one
{
"$limit": 1
},
{
"$project": {
"arrayofkeyvalue": {
"$filter": {
"input": {"$objectToArray":"$$ROOT"},
"as": "keyValPairs",
"cond": {
"$in": [{"$type": "$$keyValPairs.v"}, ["double", "int", "long"]],
// Change the above line to the following to get only `int` keys instead of `int, double` and `long`:
// "$eq": [{"$type": "$$keyValPairs.v"}, "int"],
}
}
}
}
},
{
"$group": {
"_id": null,
"unique": {"$addToSet": "$arrayofkeyvalue.k"}
}
},
{
"$project": {
"_id": 0,
"intKeyNames": {
"$reduce": {
input: "$unique",
initialValue: [],
in: {$setUnion : ["$$value", "$$this"]}
}
}
}
},
])
The above query result will be something like this:
{
"intKeyNames" : [
"_id",
"abc",
"paymentMonth",
"paymentYear",
"value"
]
}

mongodb to return object from facet

Is it possible to have facet to return as an object instead of an array? It seems a bit counter intuitive to need to access result[0].total instead of just result.total
code (using mongoose):
Model
.aggregate()
.match({
"name": { "$regex": name },
"user_id": ObjectId(req.session.user.id),
"_id": { "$nin": except }
})
.facet({
"results": [
{ "$skip": start },
{ "$limit": finish },
{
"$project": {
"map_levels": 0,
"template": 0
}
}
],
"total": [
{ "$count": "total" },
]
})
.exec()
Each field you get using $facet represents separate aggregation pipeline and that's why you always get an array. You can use $addFields to overwrite existing total with single element. To get that first item you can use $arrayElemAt
Model
.aggregate()
.match({
"name": { "$regex": name },
"user_id": ObjectId(req.session.user.id),
"_id": { "$nin": except }
})
.facet({
"results": [
{ "$skip": start },
{ "$limit": finish },
{
"$project": {
"map_levels": 0,
"template": 0
}
}
],
"total": [
{ "$count": "total" },
]
})
.addFields({
"total": {
$arrayElemAt: [ "$total", 0 ]
}
})
.exec()
You can try this as well
Model
.aggregate()
.match({
"name": { "$regex": name },
"user_id": ObjectId(req.session.user.id),
"_id": { "$nin": except }
})
.facet({
"results": [
{ "$skip": start },
{ "$limit": finish },
{
"$project": {
"map_levels": 0,
"template": 0
}
}
],
"total": [
{ "$count": "total" },
]
})
.addFields({
"total": {
"$ifNull": [{ "$arrayElemAt": [ "$total.total", 0 ] }, 0]
}
})
.exec()
imagine that you want to pass the result of $facet to the next stage, let's say $match. well $match accepts an array of documents as input and return an array of documents that matched an expression, if the output of $facet was just an element we can't pass its output to $match because the type of output of $facet is not the same as the type of input of $match ($match is just an example). In my opinion it's better to keep the output of $facet as array to avoid handling those types of situations.
PS : nothing official in what i said

How to write a MongoDB query that finds a field match from a JavaScript array

I'm trying to put together a MongoDB query that will return results where a field equals a value that is present in a JavaScript array. I've put together the following code so far but it isn't working. I would like the number field in the collection to find a match within the versions array. I'm thinking that my "list" part using the $map under the first $project is wrong.
//example data
var versions = ['v1', 'v2', 'v3'];
function getResults(versions) {
var query =
{
"db": "db-name",
"collection": "collection-name",
"query":
[
{
$match: {
time: getStartTime(),
}
},
{
$project:
{
number: "$number",
session_id: "$session_id",
round_id: "$_id",
"list": {
"$map": {
"input": { "$filter": {
"input": versions,
"as": "versions",
"cond": {
"$eq": [ "$$versions.value", "$number" ]
}
}},
"as": "versions",
"in": "$$versions.value"
}
}
}
},
{
$group:
{
_id: "$session_id",
number: { $first: "$list" },
rounds: { $addToSet: "$round_id" }
}
},
{
$project:
{
number: "$number",
rounds: "$rounds"
}
}
]
}
return query;
}
Can you provide more info about you are triyng to do?
Maybe you can use filter by javascript array.
Here is an example code:
function isBigEnough(value) {
return value >= 10;
}
var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
// filtered is [12, 130, 44]
Maybe filter could be useful for you.

Categories

Resources