Search in Embedded Documents in MongoDB? - arrays

I have document as shown
[
{
"Users": [
{
"Name": "Kartikey Vaish",
"_id": "1",
},
{
"Name": "Witcher Proxima",
"_id": "2",
}
],
"_id": "12",
},
{
"Users": [
{
"Name": "Witcher Proxima",
"_id": "2",
},
{
"Name": "Saga",
"_id": "4",
}
],
"_id": "13",
}
]
I want to search for those documents whose Users array has that particular ID
For Example if
ID == 1 // should return
[
{
"Users": [
{
"Name": "Kartikey Vaish",
"_id": "1",
},
{
"Name": "Witcher Proxima",
"_id": "2",
}
],
"_id": "12",
}
]
ID == 2 // should return
[
{
"Users": [
{
"Name": "Kartikey Vaish",
"_id": "1",
},
{
"Name": "Witcher Proxima",
"_id": "2",
}
],
"_id": "12",
},
{
"Users": [
{
"Name": "Witcher Proxima",
"_id": "2",
},
{
"Name": "Saga",
"_id": "4",
}
],
"_id": "13",
}
]
ID == 4 // should return
[
{
"Users": [
{
"Name": "Kartikey Vaish",
"_id": "1",
},
{
"Name": "Saga",
"_id": "4",
}
],
"_id": "13",
}
]
As you can see from above my query should return only those objects whose "Users" array contains an object with given ID. I tried this but it doesn't work.
const chats = await Chats.find({
Users: { $elemMatch: { _id: "1" } },
});
// this returns an empty array
const chats = await Chats.find({
Users: { $elemMatch: { Name: "Kartikey Vaish" } },
});
// this returns
[
{
"Users": [
{
"Name": "Kartikey Vaish",
"_id": "1",
},
{
"Name": "Witcher Proxima",
"_id": "2",
}
],
"_id": "12",
}
]
What am I doing wrong here?
Is it something related to _id paramter?
EDIT:
My Chats Schema looks like this -
const Chats = mongoose.model(
"Chats",
new mongoose.Schema({
Users: {
type: Array,
required: true,
default: [],
},
})
);

Update your schema as shown below
const Chats = mongoose.model(
"Chats",
new mongoose.Schema({
Users: [{
_id: {
type: String,
required: true, // Include only if needed!
unique: true // Include only if needed!
},
Name: {
type: String,
index: true // Include only if needed!
}
}]
})
);
If you do not explicitly mention _id MongoDB will create _id field as ObjectId.

Related

MongoDB get only selected elements from objects inside an array

What I have is a collection of documents in MongoDB that have the structure something like this
[
{
"userid": "user1",
"addresses": [
{
"type": "abc",
"street": "xyz"
},
{
"type": "def",
"street": "www"
},
{
"type": "hhh",
"street": "mmm"
},
]
},
{
"userid": "user2",
"addresses": [
{
"type": "abc",
"street": "ccc"
},
{
"type": "def",
"street": "zzz"
},
{
"type": "hhh",
"street": "yyy"
},
]
}
]
If I can give the "type" and "userid", how can I get the result as
[
{
"userid": "user2",
"type": "abc",
"street": "ccc",
}
]
It would also be great even if I can get the "street" only as the result. The only constraint is I need to get it in the root element itself and not inside an array
Something like this:
db.collection.aggregate([
{
$match: {
userid: "user1" , "address.type":"abc"
}
},
{
$project: {
userid: 1,
address: {
$filter: {
input: "$addresses",
as: "a",
cond: {
$eq: [
"$$a.type",
"abc"
]
}
}
}
}
},
{
$unwind: "$address"
},
{
$project: {
userid: 1,
street: "$address.street",
_id: 0
}
}
])
explained:
Filter only documents with the userid & addresess.type you need
Project/Filter only the addresses elements with the needed type
unwind the address array
project only the needed elements as requested
For best results create index on the { userid:1 } field or compound index on { userid:1 , address.type:1 } fields
playground
You should be able to use unwind, match and project as shown below:
db.collection.aggregate([
{
"$unwind": "$addresses"
},
{
"$match": {
"addresses.type": "abc",
"userid": "user1"
}
},
{
"$project": {
"_id": 0,
"street": "$addresses.street"
}
}
])
You can also duplicate the match step as the first step to reduce the number of documents to unwind.
Here is the playground link.
There is a similar question/answer here.

How can Update a Nested array in mongoDB

let data = [
{
"_id": '101',
"name": 'category_1',
"subcategory": [
{
"_id": '201',
"name": 'subCategory_1'
},
{
"_id": '202',
"name": 'subCategory_2',
"subsubcategory": [
{
"_id": '301',
"name": 'subsubcategory_1'
},
{
"_id": '302',
"name": 'subsubcategory_2'
}
]
}
]
}
]
How can I change subsubcategory name ?

MongoDB $push in Array of Array Error: No value exists in scope for the shorthand property 'elements'

I wish to add data into Elements but get error. How to solve this mongodb/typescript problem?
Attempt 1 Error: No value exists in scope for the shorthand property 'elements'. Either declare one or provide an initializer.ts(18004)
Attempt 1
async add<T extends { id: string; parentID: string, elementNum: number, name: string, link: string}>(collectionName: string, args: T) {
const db = await this.getDB();
const collection = db.collection(collectionName);
return new Promise((resolve, reject) => {
collection.updateOne({ id: args.id }, {$push: {elements[args.elementNum]: {id: uuid(), name: args.name, link: args.link, elements: [] }}}, (err, res) => {
if (err) {
reject(err);
}
resolve(res);
});
});
}
Attempt 2
changed the following
collection.updateOne({ id: args.id }, {$push: {elements: {id: uuid(), name: args.name, link: args.link, elements: [] }}},
Attempt 2 results in Null added in the end
{
"id": "1",
"name": "wk1",
"iconFile": "icon.png",
"elements": [
[
{
"id": "2",
"name": "element2",
"link": "https",
"elements": [
{
"id": "1",
"name": "element1",
"link": "https:"
}
]
}
],
[
{
"id": "3",
"name": "element3",
"link": "https://",
"elements": [
{
"id": "4",
"name": "w",
"link": "http:/"
}
]
}
],
[
{
"id": "3",
"name": "element3",
"link": "https://",
"elements": [
{
"id": "4",
"name": "w",
"link": "http://"
}
]
},
{
"id": "3",
"name": "element3",
"link": "https://",
"elements": [
{
"id": "4",
"name": "w",
"link": "http://www."
}
]
}
],
null,
]
}
What I want to achieve is the following
{
"id": "1",
"name": "wk1",
"iconFile": "icon.png",
"elements": [
[
{
"id": "2",
"name": "element2",
"link": "https",
"elements": [
{
"id": "1",
"name": "element1",
"link": "https:"
}
]
},
{
"id": "newid",
"name": "newname",
"link": "newlink"
"elements":[]
}
],
[
{
"id": "3",
"name": "element3",
"link": "https://",
"elements": [
.......
]
}
Demo - https://mongoplayground.net/p/Dnmg3lL2891
Use - $[]
The all positional operator $[] indicates that the update operator
should modify all elements in the specified array field.
The $[] operator has the following form:
{ <update operator>: { "<array>.$[]" : value } }
db.collection.update({ _id: 1, "add.id": "1"},
{ $push: { "add.$[].elements": { id: "3", name: "a", link: "" } } })
Demo to push array instead of object - https://mongoplayground.net/p/dh3NSutIv4-
db.collection.update({ _id: 1, "add.id": "1"},
{ $push: { "add.$[].elements": [{ id: "3", name: "a", link: "" }] } })
const args = {};
args.elementNum = 0;
const update = {
[`add.$[].elements.${args.elementNum}`]: {
a: 1
}
};
console.log(update);
//collection.updateOne({ id: args.id }, update); // use like this

How to remove duplicate values inside a list array in MongoDB?

I have many records in one collection in MongoDB and this is 3 examples to remove only based one QUESTION match criteria.
{
"_id": {
"$oid": "5f0f561256efe82f5082252e"
},
"Item1": false,
"Item2": "",
"Item3": 1,
"Item4": [
{
"Name": "TYPE",
"Value": "QUESTION"
},
{
"Name": "QUESTION",
"Value": "What is your name?"
},
{
"Name": "CORRECT_ANSWER",
"Value": "1"
},
{
"Name": "ANSWER_1",
"Value": "name one"
},
{
"Name": "ANSWER_2",
"Value": "name two"
}
],
"Item5": [
10
],
"Item6": false
}
and another one to compare
{
"_id": {
"$oid": "5f0f561256efe82f5082252c"
},
"Item1": false,
"Item2": "",
"Item3": 2,
"Item4": [
{
"Name": "TYPE",
"Value": "QUESTION"
},
{
"Name": "QUESTION",
"Value": "What is your name?"
},
{
"Name": "CORRECT_ANSWER",
"Value": "1"
},
{
"Name": "ANSWER_1",
"Value": "name one"
},
{
"Name": "ANSWER_2",
"Value": "name two"
}
],
"Item5": [
10
],
"Item6": false
}
the third one :
{
"_id": {
"$oid": "5f0f561256efe82f5082252d"
},
"Item1": false,
"Item2": "",
"Item3": 3,
"Item4": [
{
"Name": "TYPE",
"Value": "QUESTION"
},
{
"Name": "QUESTION",
"Value": "What is your last name?"
},
{
"Name": "CORRECT_ANSWER",
"Value": "1"
},
{
"Name": "ANSWER_1",
"Value": "name one"
},
{
"Name": "ANSWER_2",
"Value": "name two"
}
],
"Item5": [
10
],
"Item6": false
}
What I'm trying here is to make query with aggregation approach and I only want to focus on Item4 for exactly ("Name": "QUESTION") and the value (the question) for identifying the duplication.
The idea is to looking for duplication in the the question itself only ("What is your name?") in our example here. and I don't want to specify witch question because there are long list of them.
I'm looking just for the duplicated questions no mater what is the question look like.
I used the following approach but still I cannot narrow down the output to be only related to question and its value in order to delete the duplicate in the another step.
db.collections.aggregate([{ $unwind: "$Item4" }, {$group: { _id: { QUESTION: "$Item4.Name.4", Value: "$Item4.Value.4" }}}]).pretty()
I'm executing from mongo shell directly.
The following aggregation will list all the documents (the _ids) which have the duplicates of "Item4.Value" for the condition "Item4.Name": "QUESTION".
db.test.aggregate( [
{
$unwind: "$Item4"
},
{
$match: { "Item4.Name": "QUESTION" }
},
{
$group: {
_id: { "Item4_Value": "$Item4.Value" },
ids: { $push: "$_id" }
}
},
{
$match: { $expr: { $gt: [ { $size: "$ids" }, 1 ] } }
}
] )
It works! thanks a lot. I add it to the rest of code as below :
db.test.find().count()
const duplicatesIds = [];
db.test.aggregate( [
{
$unwind: "$Item4"
},
{
$match: { "Item4.Name": "QUESTION" } //here is the trick...to filter the array to pass only the condition "Item4.Name": "QUESTION".
},
{
$group: {
_id: { "Item4_Value": "$Item4.Value" },
ids: { $push: "$_id" }
}
}
],
{
allowDiskUse: true
}
).forEach(function (doc) {
doc.ids.shift();
doc.ids.forEach(function (dupId) {
duplicatesIds.push(dupId);
})
});
printjson(duplicatesIds);
db.test.remove({_id:{$in:duplicatesIds}})
db.test.find().count()

how to find duplicate array value on document mongodb

I got question might be many of you can help me.
so I have data on mongodb.
first data
{
"name" : 'david'
contacts : [
{
"name" : 'john',
"phone" : '123456'
},
{
"name" : 'george',
"phone" : '0987654'
}
]
}
second data
{
"name" : 'anita',
"contacts" : [
{
"name" : 'harry',
"phone" : '123456'
},
{
"name" : 'kurita',
"phone" : '323434'
}
]
}
the problem is,
can I query to find data that have duplicate contacts.phone.
so the result whill show like this.
{
"name" : 'david',
"contacts" : [
{
"name" : 'john',
"phone" : '123456'
}
]
}
{
"name" : 'anita',
"contacts" : [
{
"name" : 'harry',
"phone" : '123456'
}
]
}
data john and anita will show because they have similar data on contacts.phone
sorry for my english btw,
I hope you all understand what I mean.
thank you so much
There are a few steps involved to get the results you need.
We are going to write an aggregate pipeline to get the work done.
First you need to unwind your array values with the following:
{
$unwind: "$contacts"
}
Doc: https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/
This would result into:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"contacts": {
"name": "john",
"phone": "123456"
},
"name": "david"
},
{
"_id": ObjectId("5a934e000102030405000000"),
"contacts": {
"name": "george",
"phone": "0987654"
},
"name": "david"
},
{
"_id": ObjectId("5a934e000102030405000001"),
"contacts": {
"name": "harry",
"phone": "123456"
},
"name": "anita"
},
{
"_id": ObjectId("5a934e000102030405000001"),
"contacts": {
"name": "kurita",
"phone": "323434"
},
"name": "anita"
}
]
This would be much easier for us to group by field.
Doc: https://docs.mongodb.com/manual/reference/operator/aggregation/group/
{
$group: {
_id: {
phone: "$contacts.phone"
},
name: {
$addToSet: "$name"
},
contacts: {
$addToSet: "$contacts.name"
},
count: {
$sum: 1
}
}
}
That gives the following output:
[
{
"_id": {
"phone": "323434"
},
"contacts": [
"kurita"
],
"count": 1,
"name": [
"anita"
]
},
{
"_id": {
"phone": "123456"
},
"contacts": [
"john",
"harry"
],
"count": 2,
"name": [
"david",
"anita"
]
},
{
"_id": {
"phone": "0987654"
},
"contacts": [
"george"
],
"count": 1,
"name": [
"david"
]
}
]
Based on the output we need to match the count greater then 1 like:
Doc: https://docs.mongodb.com/manual/reference/operator/aggregation/match/
{
$match: {
count: {
"$gt": 1
}
}
}
Result is:
[
{
"_id": {
"phone": "123456"
},
"contacts": [
"john",
"harry"
],
"count": 2,
"name": [
"david",
"anita"
]
}
]
The query would look like:
Doc: https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/
db.collection.aggregate([
{
$unwind: "$contacts"
},
{
$group: {
_id: {
phone: "$contacts.phone"
},
name: {
$addToSet: "$name"
},
contacts: {
$addToSet: "$contacts.name"
},
count: {
$sum: 1
}
}
},
{
$match: {
count: {
"$gt": 1
}
}
}
])
MongoPlayground: https://mongoplayground.net/p/qSvhcYyAcQO
I hope this gives you a small idea what is possible with the aggregation pipeline.
Update / fix
According to your requirements you wish to have 2 objects foreach name that has duplicate contacts then you could use unwind again after the match.
[
{
"_id": {
"phone": "123456"
},
"contacts": [
"harry",
"john"
],
"count": 2,
"name": "david"
},
{
"_id": {
"phone": "123456"
},
"contacts": [
"harry",
"john"
],
"count": 2,
"name": "anita"
}
]
Cheers, Kevin

Resources