Split arrays in set mongodb - arrays

I've been trying to make a set of strings < or arrays > but my main issue is that I can't use $unwind because of the other data.
To make it more clear I'll show an example:
This is how part of my documents look like:
{
"key": [
"a",
"b",
"c"
]
},
{
"key": [
"b",
"d"
]
},
{
"key": [
"e"
]
}
And I want to get an array like this:
{
"keys": [
"a",
"b",
"c",
"d",
"e"
]
}
or this:
{
"keys": [
["a"],
["b"],
["c"],
["d"],
["e"]
]
}
I just need unique values in there.
It would be so easy if I could use $unwind and then $addToSet. But as I said before I can't because of the other data that I need.
I was trying to use $cond inside the $addToSet but I don't know how to iterate through an array.
This pipeline doesn't work ->
db.collection.aggregate([
{
"$group": {
"_id": null,
"keys": {
"$addToSet": {
"$concatArrays": [
"$key",
"$this"
]
}
},
}
}
])
Maybe something like this:
{
"$group": {
"_id": null,
"keys": {
"$addToSet": {
"$cond": {
if: {
"$gt": [
{"$size": "$key"},
1
]
},
then: "<SPLIT THE ARRAY IN THE AMOUNT OF ELEMENTS>",
else: "$key"
}
}
}
}
}
I tried this but it doesn't work either.
db.collection.aggregate([
{
"$project": {
"keys": {
"$map": {
"input": {
"$range": [
0,
{
"$size": "$key"
},
1
]
},
as: "index",
in: {
$slice: [
"$key",
"$$index",
1
]
}
}
}
}
},
{
"$group": {
"_id": null,
"keys": {
"$addToSet": {
"$concatArrays": [
"$keys"
]
}
},
}
}
])
Here is my code snippet: https://mongoplayground.net/p/sMENXyrbQgI
Could someone please guide me! I'll appreciate it so much.

So the idea is to use $setUnion along with $reduce to pick unique values from the array:
Solution in MongoPlayground
Try this query:
db.collection.aggregate([
{
$group: {
_id: null,
"keys": { $push: "$key" }
}
},
{
$addFields: {
"keys": {
$reduce: {
input: "$keys",
initialValue: [],
in: {
$setUnion: ["$$value", "$$this"]
}
}
}
}
}
])
Output:
{
"_id" : null,
"keys" : [
"a",
"b",
"c",
"d",
"e"
]
}
Test data in collection:
[
{
"key": [
"a",
"b",
"c"
]
},
{
"key": [
"b",
"d",
"a",
]
},
{
"key": [
"e",
"b"
]
}
]

Related

MongoDB - Match the key

I want to $match in a MongoDB, the number of documents in thousands, so looking for dynamic sol:
$doc.k equal to $info.data.k after k, $match then we have a document that contains only information that $matchs, see the expected output document. If you have any questions let me know.
[
{
"doc": {
"k": "ABC",
"v": {
"sec": 0
}
},
"info": [
{
"data": [
{
"k": "XYZ",
"v": {
"know": "alpha"
}
},
{
"k": "ABC",
"v": {
"know": "alpha"
}
}
]
}
]
}
]
The expected output document will look like this
[
{
"doc": {
"k": "ABC",
"v": {
"sec": 0
}
},
"info": [
{
"data": [
{
"k": "ABC",
"v": {
"know": "alpha"
}
}
]
}
]
}
]
$set - Update info field.
1.1. $map - As info is an array, need to iterate each document in the info array and return a new array.
1.1.1. $filter - Filter the document by doc.k and current iterate k value.
db.collection.aggregate([
{
$set: {
info: {
$map: {
input: "$info",
in: {
data: {
$filter: {
input: "$$this.data",
cond: {
$eq: [
"$doc.k",
"$$this.k"
]
}
}
}
}
}
}
}
}
])
Sample Mongo Playground

How to preserve the order of mongoID in an array after a mongo query

Im trying to query an array of mongoIds and return data in the same order, however whenever I try to do this the order is not the same as declared below.
Sample code:
let id = [
"620d323b8d0273004c8993a4",
"620d32498d0273004c8993a5",
"61e730a745171f002d85df4b",
"620cfe708d0273004c89933a"
]
let test = await User.find({'_id': {$in: id}}, 'id text')
console.log(test)
After logging test the output returned as:
[
{"_id": "61e730a745171f002d85df4b"},
{"_id": "620cfe708d0273004c89933a"},
{"_id": "620d323b8d0273004c8993a4"},
{"_id": "620d32498d0273004c8993a5"}
]
I want to return this as the first code snippet, any help would be appreciated.
db.collection.aggregate([
{
"$match": {
_id: {
"$in": [
"620d323b8d0273004c8993a4",
"620d32498d0273004c8993a5",
"61e730a745171f002d85df4b",
"620cfe708d0273004c89933a"
]
}
}
},
{
"$group": {
"_id": null,
"docs": {
"$push": "$$ROOT"
}
}
},
{
"$set": {
"docs": {
"$map": {
"input": [
"620d323b8d0273004c8993a4",
"620d32498d0273004c8993a5",
"61e730a745171f002d85df4b",
"620cfe708d0273004c89933a"
],
"as": "s",
"in": {
"$filter": {
"input": "$docs",
"as": "d",
"cond": {
"$eq": [
"$$d._id",
"$$s"
]
}
}
}
}
}
}
},
{
"$unwind": "$docs"
},
{
"$replaceWith": {
"$first": "$docs"
}
}
])
mongoplayground

$in Query on Array Field Mongo db

I have a doc like this
{
"_id" : "oidfi",
"users": [
{
"_id": "q",
"tags": ["a", "b", "c"],
"age": 20
},
{
"_id": "q",
"tags": ["x", "y", "z"],
"age": 30
}
],
"type": "repo"
}
I want to filter the users array with several fields ( I'm able to do that with the below query )
{
"$match": {
"$and": [
{
"_id": "a506f8af6f510f616bea14715276d474d2b363f0aef10443cc0f11559b139147"
}
]
}
},
{
"$project": {
"users": {
"$filter": {
"input": "$users",
"as": "users",
"cond": {
"$and": [
{
"$gte": [
"$$user.age",
15
]
}
]
}
}
}
}
}
]
is there a way for me to add condition where I give a list of tags items and get the users who are matching at least one item from their tags array
$$user.tags.containsAny(["a", "x"])
Thanks
You can do the followings in an aggregation pipeline:
$setIntersection to find out the intersection of the users.tags array and your input array i.e. ["a", "x"] in your example
$size to find the size of array in step 1
$match by the size of step 2 larger than 0
"anyTagsMatched": {
$gt: [
{
$size: {
"$setIntersection": [
"$users.tags",
[
"a",
"x"
]
]
}
},
0
]
}
Here is the Mongo playground for your reference.
You may have like following
db.collection.aggregate([
{
"$project": {
"users": {
"$filter": {
"input": "$users",
"cond": {
"$and": [
{ "$gte": [ "$$this.age", 15 ] },
{
$gt: [
{
$size: {
"$setIntersection": [
"$$this.tags",
[ "a", "x" ]
]
}
},
0
]
}
]
}
}
}
}
}
])
Working Mongo playground

Select specifics items from array objects in mongoose

I have an mongoDB objetc like :
[
{
"items": [
{
"title": "hello",
"options": [
{
"value": "A",
"image": "img1",
"des": "des1"
},
{
"value": "B",
"image": "img2",
"des": "des2"
},
]
}
]
}
]
When I retrieve data from the collection, I want to retrieve only value key from the options of the items array.
Output should look like :
{
"items":[
{
"options" :[
{ "value" :"A" },
{ "value" :"B" },
]
}
]
}
How can i do this?
You can do it like this:
db.collection.find({}, { "items.options.value": 1})
Here is the working example: https://mongoplayground.net/p/FqDaQ7Q-y-Y
Or you can do it like this:
db.collection.aggregate([
{
"$project": {
"items.options.value": 1
}
}
])
Here is the working example: https://mongoplayground.net/p/jPFYtXYBvpI

MongoDB get results where date is equal to max date

Assume I have the following document:
[
{
"callId": "17dac51e-125e-499e-9064-f20bd3b1a9d8",
"caller": {
"firstName": "Test",
"lastName": "Testing",
"phoneNumber": "1231231234"
},
"routeHistory": [
{
"assignedUserId": "cfa0ffe9-c77d-4eec-87d7-4430f7772e81",
"routeDate": "2020-01-01T06:00:00.000Z",
"status": "routed"
},
{
"assignedUserId": "cfa0ffe9-c77d-4eec-87d7-4430f7772e81",
"routeDate": "2020-01-03T06:00:00.000Z",
"status": "ended"
}
]
}
]
I want to get results where routeHistory.routeDate is equal to the $max routeDate value in routeHistory. I would expect my results to look like the following:
[
{
"callId": "17dac51e-125e-499e-9064-f20bd3b1a9d8",
"caller": {
"firstName": "Test",
"lastName": "Testing",
"phoneNumber": "1231231234"
},
"routeHistory": [
{
"assignedUserId": "cfa0ffe9-c77d-4eec-87d7-4430f7772e81",
"routeDate": "2020-01-03T06:00:00.000Z",
"status": "ended"
}
]
}
]
Is there a clean way to do this in a single aggregate, so that additional $match criteria can be applied?
You can use $let to define temporary variable being $max date and the use $filter along with $arrayElemAt to get first matching element:
db.collection.aggregate([
{
$addFields: {
routeHistory: {
$let: {
vars: {
maxDate: { $max: "$routeHistory.routeDate" }
},
in: {
$arrayElemAt: [
{ $filter: { input: "$routeHistory", cond: { $eq: [ "$$maxDate", "$$this.routeDate" ] } } },
0
]
}
}
}
}
}
])
Mongo Playground
EDIT:
version without $let:
db.collection.aggregate([
{
$addFields: {
maxDate: {
$max: "$routeHistory.routeDate"
}
}
},
{
$addFields: {
routeHistory: {
$arrayElemAt: [
{
$filter: { input: "$routeHistory", cond: { $eq: [ "$$maxDate", "$$this.routeDate" ] } }
},
0
]
}
}
}
}
])

Resources