For one of my project, I am working on MongoDB database and Mongoose as ODM in nestjs project.
One of my collection is looks like this data:
[
{
_id: '56cb91bdc3464f14678934ca',
organization: 'New Circle Ltd',
rooms: [
{
name: 'Alex',
workId: 'abc',
},
{
name: 'John',
workId: 'cde',
},
{
name: 'John',
workId: 'abc',
},
{
name: 'Alex',
workId: 'cdlw',
},
{
name: 'Alex',
workId: 'rps',
},
],
},
];
The requirement is to get one data by id and the data should be like the data provided bellow.
{
_id: '56cb91bdc3464f14678934ca',
organization: 'New Circle Ltd',
rooms: [
{
name: 'Alex',
workIds: ['abc', 'cdlw', 'rps'],
},
{
name: 'John',
workIds: ['cde', 'abc'],
},
],
},
Please suggest me how can I get this data
Related
Consider the schema below:
schema/collectible.ts: The items that can be collected
const Collectible = new mongoose.schema({
title: String,
image: String
})
new Collectible({ title: 'ABC', image: 'ABC.png' }).save()
new Collectible({ title: 'DEF', image: 'DEF.png' }).save()
schema/inventory.ts: Storing a Map that represent the number of Collectible that an user owned
const Inventory = new mongoose.schema({
owner: { type: Types.ObjectId, ref: 'User' },
items: {
type: Map,
of: Number
}
})
new Inventory({
owner: ObjectId(USER_ID),
items: {
"abc_objectid_str": 2,
"def_objectid_str": 4,
}
}).save()
The goal of the query result is
[
{ title: 'ABC', image: 'ABC.png', amount: 2 },
{ title: 'DEF', image: 'DEF.png', amount: 4 },
]
One option is to use $lookup pipeline with $objectToArray and $reduce:
db.collectible.aggregate([
{$lookup: {
from: "inventory",
let: {title: {$toLower: "$title"}},
pipeline: [
{$match: {owner: ObjectId("5a934e000102030405000000")}},
{$project: {_id: 0, data: {$objectToArray: "$items"}}},
{$set: {
data: {
$reduce: {
input: "$data",
initialValue: 0,
in: {
$add: [
{$cond: [
{$eq: [{$first: {$split: ["$$this.k", "_"]}}, "$$title"]},
"$$this.v",
0
]},
"$$value"
]
}
}
}
}
}
],
as: "countData"
}
},
{$project: {_id: 0, image: 1, title: 1, amount: {$first: "$countData.data"}}}
])
See how it works on the playground example
I have this model:
const person = new Schema({
name: String,
lastName: String,
accounts: [{
provider: String,
email: {
type: String,
lowercase: true
},
password: String,
}
]
})
Then a user registered first with Facebook and then with email, now in the database there are two documents. But now I wanna combine these two documents in one and in this way the user could log in to the same "account" but with different types of authentication.
My first option consists of finding each user and then merging, adding to the collection and then deleting the old ones. But I think this could cause problems to long-term
*update *
This is doc 1:
{
name: "John",
lastName: "Doe",
accounts: [{
provider: "google",
email: "my#email.com",
password: p4ssw0rd,
}]
}
This is doc2, this doesn't have a name and the last name because these aren't required:
{
accounts: [{
provider: "simple",
email: "my2#email.com",
password: p4ssw0rd2,
}]
}
and then the admin can merge and then have this:
{
name: "John",
lastName: "Doe",
accounts: [{
provider: "google",
email: "my#email.com",
password: p4ssw0rd,
},{
provider: "simple",
email: "my2#email.com",
password: p4ssw0rd2,
}]
}
I see it as follow:
Step 1: Update document doc1 with joined content from doc1 and doc2
var doc3=db.collection.aggregate([
{
$match: {
_id: {
$in: [
1,
2
]
}
}
},
{
$unwind: "$accounts"
},
{
$group: {
_id: 1,
name: {
$first: "$name"
},
lastName: {
$first: "$lastName"
},
accounts: {
$push: "$accounts"
}
}
}
])
db.collection.save(doc3)
playground_step_1
Step 2: Remove the doc2 ( _id:2 )
db.collection.remove({_id:2})
It is interesting to understand how you are going to correlate or understand how doc1 is related to doc2 ...
I have an array of objects - lists[]; and an object - obj;
This is the lists array:
{
_id: new ObjectId("61840c5ce237f22a1c7a1ac7"),
name: 'list1',
content: [ 'aaaa' ],
description: 'aa',
tags: [],
lastmodified: 1,
__v: 0
},{
_id: new ObjectId("61840def80a88d1b2ffce400"),
name: 'list',
content: [ 'list!' ],
description: 'test',
tags: [],
__v: 0
}
and this is the obj object:
{
_id: new ObjectId("61840def80a88d1b2ffce400"),
name: 'list',
content: [ 'list!' ],
description: 'test',
tags: [],
__v: 0
}
Simple Question: How do I delete the object from "lists", similar to the "obj" one
You could use the JavaScript Array.filter() method to exclude the target object from the list:
const list = [{
_id: new ObjectId("61840c5ce237f22a1c7a1ac7"),
name: 'list1',
content: [ 'aaaa' ],
description: 'aa',
tags: [],
lastmodified: 1,
__v: 0
},{
_id: new ObjectId("61840def80a88d1b2ffce400"),
name: 'list',
content: [ 'list!' ],
description: 'test',
tags: [],
__v: 0
}];
const targetObj = {
_id: new ObjectId("61840def80a88d1b2ffce400"),
name: 'list',
content: [ 'list!' ],
description: 'test',
tags: [],
__v: 0
};
const filteredList = list.filter((element) => element._id !== targetObj._id);
use some sort of filtering... heres an idea.
items = [{
_id: new ObjectId("61840c5ce237f22a1c7a1ac7"),
name: 'list1',
content: [ 'aaaa' ],
description: 'aa',
tags: [],
lastmodified: 1,
__v: 0
},{
_id: new ObjectId("61840def80a88d1b2ffce400"),
name: 'list',
content: [ 'list!' ],
description: 'test',
tags: [],
__v: 0
}]
removeID = "61840def80a88d1b2ffce400"
items = [item for item in items if item['id'] != removeID]
Let's say i have 5 documents in members collection as follow:
[
{
_id: ObjectId("60e6afadfb6fe6510155e9b2"),
name: "James",
parentId: ObjectId("60e6afadfb6fe6510155e9b1")
},
{
_id: ObjectId("60e6afadfb6fe6510155e9b1"),
name: "Michael",
parentId: ObjectId("60e6afadfb6fe6510155e9b0")
},
{
_id: ObjectId("60e6afadfb6fe6510155e9b0"),
name: "Josh",
parentId: ObjectId("60e6afadfb6fe6510155e9af")
},
{
_id: ObjectId("60e6afadfb6fe6510155e9af"),
name: "Robert",
parentId: ObjectId("60e6afadfb6fe6510155e9ad")
},
{
_id: ObjectId("60e6afadfb6fe6510155e9ad"),
name: "William",
parentId: null
},
]
All I want is to iterate through those documents to get the full name of James
My output should look like 'James Michael Josh Robert William' consider that the parentId in the child document is the _id of the parent document
You can use the concept of the linked list to traverse through an array. To my knowledge, it is not possible on the DB level, and since you have mentioned the nodejs tag. So here is a nodejs solution
const {Types: {ObjectId}} = require("mongoose")
let data = [
{
_id: ObjectId("60e6afadfb6fe6510155e9b2"),
name: "James",
parentId: ObjectId("60e6afadfb6fe6510155e9b1")
},
{
_id: ObjectId("60e6afadfb6fe6510155e9b1"),
name: "Michael",
parentId: ObjectId("60e6afadfb6fe6510155e9b0")
},
{
_id: ObjectId("60e6afadfb6fe6510155e9b0"),
name: "Josh",
parentId: ObjectId("60e6afadfb6fe6510155e9af")
},
{
_id: ObjectId("60e6afadfb6fe6510155e9af"),
name: "Robert",
parentId: ObjectId("60e6afadfb6fe6510155e9ad")
},
{
_id: ObjectId("60e6afadfb6fe6510155e9ad"),
name: "William",
parentId: null
},
]
let names = []
let current = data[0]
// handle null / undefined current value
while(current) {
names.push(current.name)
current = data.find(v=>v._id.equals(current.parentId))
}
console.log(names.join(' ')) // James Michael Josh Robert William
My data stored for user is in the following format :
{
name: 'XYZ',
email: 'abc#gmail.com',
password: 'encrypted',
categories: [
{
name: 'category1',
type: 'type1',
},
{
name: 'category2',
type: 'type2',
}
],
transactions: [
{
date: 2020-04-09T06:00:30.000Z,
type: 'type1',
category: 'category1',
description: 'some desc',
},
{
date: 2020-04-08T06:00:30.000Z,
type: 'type2',
category: 'category1',
description: 'some desc',
},
{
date: 2020-04-07T06:00:30.000Z,
type: 'type3',
category: 'category1',
description: 'some desc',
}
]
}
I want to get the data of particular user after providing unique email but along with that I want transactions filtered between 2 dates.
Example I want data of user with email = abc#gmail.com and transactions of dates between 2020-04-08 and 2020-04-09.
Sample output looks like this :
{
name: 'XYZ',
email: 'abc#gmail.com',
categories: [...],
transactions: [
{
date: 2020-04-09T06:00:30.000Z,
type: 'type1',
category: 'category1',
description: 'some desc',
},
{
date: 2020-04-08T06:00:30.000Z,
type: 'type2',
category: 'category1',
description: 'some desc',
}]
}
Or only transactions between the given dates for the given user email is also fine.
Tried using this query
User.aggregate([
{$match: {email: email}},
{$unwind: '$transactions'},
{$match: {'transactions.date': {$gt: startDate, $lt: endDate}}},
{
$group: {
_id: '$_id',
transactions: {
$type: "$transactions.type",
$category: "$transactions.category",
$date: "$transactions.date",
$description: "$transactions.decription"
}
}
}
]).exec((err, data) => {
if (err) throw err;
console.log(data);
})
Getting the error as follows The field 'transactions' must specify one accumulator