Document schema is as follows
"events": [
{
"title": "title1"
},
{
"title": "title2"
},
{
"title": "title3"
}
]
I have requirement to search (regex ) in events.title field and get the only matching element/object from the array . For that i am querying like this ,
db.collection.find({ "$or" : [ { "events.title" : { "$regex" : ".*title2.*" , "$options" : "i"}} , { "events.title" : { "$regex" : ".*title5.*" , "$options" : "i"}}] , "events" : { "$elemMatch" : { "title" : { "$ne" : null }}}},{"events.$":1,"_id":0});
I was expecting the result would be { "events" : [ { "title" : "title2"} ] } , but it returns
{ "events" : [ { "title" : "title1"} ] }
How can i update the query so that only matching element in array is returned in result ?
UPDATE
"events": {
$elemMatch: {
"$or": [{
"title": {
"$regex": ".*title2.*",
"$options": "i"
}
},
{
"title": {
"$regex": ".*title5.*",
"$options": "i"
}
}]
}
}
did the trick . Now it returns only matching element in the array irrespective of the position.
Thanks for the help
Try this
db.collection.find({events: {
$elemMatch: {
title: {$in: [/.*title2.*/i,
/.*title5.*/i]
}
}
}
},
{"events.$": 1, _id: 0});
{"events": {
$elemMatch: {
"$or": [{
"title": {
"$regex": ".*title2.*",
"$options": "i"
}
},
{
"title": {
"$regex": ".*title5.*",
"$options": "i"
}
}]
}
}
}
in projection did the trick . Now it returns only matching element in the array irrespective of the position
Related
I would like to push a new value to an array in a specific position but although I tried differents things I can do it. Can someone help me?
My document is:
{
"_id" : ObjectId("55528f000000000000000000"),
"contractId" : "55528f000000000000000000",
"field1": [
{
"name":"example",
"backendData": {
"map": {
"7552" : "RTEST",
"3511" : "TESTR",
"5312" : "JKTLE",
"5310" : "INVTS"
}
},
"data": {
"defaultOrder": [
"7552",
"3511",
"5312",
"5310"
]
}
}
]
}
I've tried with set, but I didn't find a way to add the element in the position I want, then I tried with push but it's not working. I did something like that:
db.collection.update({
"_id": ObjectId("55528f000000000000000000"),
},
{
"$push": {
"field1.$[i].data.$.defaultOrder": {
"$each": [
"9999"
],
"$position": 2
}
}
},
{
arrayFilters: [
{
"i.entities": {
$elemMatch: {
"name": "example"
}
}
}
]
})
And although it gives me a match it doesn't make any change
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
Expected outcome:
{
"_id" : ObjectId("55528f000000000000000000"),
"contractId" : "55528f000000000000000000",
"field1": [
{
"name":"example",
"backendData": {
"map": {
"7552" : "RTEST",
"3511" : "TESTR",
"5312" : "JKTLE",
"5310" : "INVTS"
}
},
"data": {
"defaultOrder": [
"7552",
"3511",
"9999",
"5312",
"5310"
]
}
}
]
}
Thank in advance
Please try this (edited both 2nd and 3rd parts):
db.collection.update({"_id": ObjectId("55528f000000000000000000")},
{"$push": {"field1.$[i].data.defaultOrder":
{"$each": ["9999"], "$position": 2}}},
{arrayFilters: [ { "i.name": "example" } ]})
See:
https://mongoplayground.net/p/8o57t4tlnm6
I have defined a model like this.
const ShotcountSchema = new Schema({
shotCountId : {
type : ObjectId,
required : true,
ref : 'member-info'
},
userId : {
type : String,
required : true,
unique : true
},
shot : [{
shotId : {
type : String,
required : true,
unique : true
},
clubType : {
type : String,
required : true
}
createdAt : {
type : Date,
default : Date.now
}
}]
});
If you perform a query to find subdocuments based on clubType as follows, only the results of the entire document are output.
For example, if I write the following code and check the result, I get the full result.
const shotCount = await ShotcountSchema.aggregate([
{
$match : { shotCountId : user[0]._id }
},
{
$match : { 'shot.clubType' : 'driver' }
}
]);
console.log(shotCount[0]); // Full result output
I would like to filter the subdocuments via clubType or createdAt to explore. So, I want these results to be printed.
{
_id: new ObjectId("61d67f0a74ec8620f34c57ed"),
shot: [
{
shotId: 'undefinedMKSf*Tf#!qHxWpz1hPzUBTz%',
clubType: 'driver',
shotCount: 20,
_id: new ObjectId("61d67f0a74ec8620f34c57ef"),
createdAt: 2022-01-06T05:32:58.391Z
}
]
}
How should I write the code?
db.collection.aggregate([
{
"$match": {
_id: ObjectId("61d67f0a74ec8620f34c57ed"),
"shot.clubType": "driver",
shot: {
$elemMatch: {
$and: [
{
"createdAt": {
$gte: ISODate("2022-01-07T05:32:58.391Z")
}
},
{
"createdAt": {
$lte: ISODate("2022-01-09T05:32:58.391Z")
}
}
]
}
}
}
},
{
"$set": {
shot: {
"$filter": {
"input": "$shot",
"as": "s",
"cond": {
$and: [
{
"$eq": [
"$$s.clubType",
"driver"
]
},
{
"$gte": [
"$$s.createdAt",
ISODate("2022-01-07T05:32:58.391Z")
]
},
{
"$lte": [
"$$s.createdAt",
ISODate("2022-01-09T05:32:58.391Z")
]
}
]
}
}
}
}
}
])
mongoplayground
Sample data: there are multiple similar collection:
{
"_id" : NumberLong(301),
"telecom" : [
{
"countryCode" : {
"value" : "+1"
},
"extension" : [
{
"url" : "primary",
"value" : [
"true"
]
}
],
"modifiedValue" : {
"value" : "8887778888"
},
"system" : {
"value" : "phone"
},
"useCode" : {
"value" : "Home Phone"
},
"value" : {
"value" : "8887778888"
}
},
{
"extension" : [
{
"url" : "primary",
"value" : [
"true"
]
}
],
"modifiedValue" : {
"value" : "abc#test.com"
},
"system" : {
"value" : "email"
},
"useCode" : {
"value" : "work"
},
"value" : {
"value" : "abc#test.com"
}
}
]
}
Issue: I want to cont the collection where telecom.system.value = email and countryCode doesn't exist in the email part object. here I am attaching a script but I need one line query
var count = 0,i;
db.getCollection('practitioner').find({"telecom.system.value":"email"}).forEach(function(practitioner){
//print("updating : " +practitioner._id.valueOf())
telecom = practitioner.telecom.valueOf()
for(i= 0;i<telecom.length;i++){
if(telecom[i].system.value === 'email' && telecom[i].countryCode){
count+=1;
}
}
});
print(" Total count of the practitioner with country code in email object: "+count)
Above mention, the script is working fine and the output is as I expected. but the script is not optimised and I want to write in a single line query. Thanks in advance.
You can try aggregation method aggregate(),
Approach 1:
$match condition for countryCode should exists and system.value should be email
$filter to iterate loop of telecom array and check both condition, this will return expected elements
$size to get total element from above filter result
$group by null and count total
var result = await db.getCollection('practitioner').aggregate([
{
$match: {
telecom: {
$elemMatch: {
countryCode: { $exists: true },
"system.value": "email"
}
}
}
},
{
$project: {
count: {
$size: {
$filter: {
input: "$telecom",
cond: {
$and: [
{ $ne: [{ $type: "$$this.countryCode" }, "missing"] },
{ $eq: ["$$this.system.value", "email"] }
]
}
}
}
}
}
},
{
$group: {
_id: null,
count: { $sum: "$count" }
}
}
]);
print("Total count of the practitioner with country code in email object: "+result[0].count);
Playground
Approach 2:
$match condition for countryCode should exists and system.value should be email
$unwind deconstruct telecom array
$match to filter document using above conditions
$count to get total elements count
var result = await db.getCollection('practitioner').aggregate([
{
$match: {
telecom: {
$elemMatch: {
countryCode: { $exists: true },
"system.value": "email"
}
}
}
},
{ $unwind: "$telecom" },
{
$match: {
"telecom.countryCode": { $exists: true },
"telecom.system.value": "email"
}
},
{ $count: "count" }
]);
print("Total count of the practitioner with country code in email object: "+result[0].count);
Playground
I have not tested the performance but you can check and use as per your requirement.
I have the following example document with four nested arrays
{
"_id" : ObjectId("5ed6bd9596908c36f4200980"),
"attr2" : "hello",
"attr3" : 1234,
"attr1" : {
"firstArray" : [
{
"secondArray" : [
{
"secondAttr1" : "world",
"secondAttr2" : 456,
"secondAttr3" : [
{
"finalArray" : [
{
"finalAttr1" : "alex",
"finalAttr2" : 9876
}
]
}
]
}
]
}
]
}
}
and this aggregate query that finds through nested arrays, unwinding and creating new root for each array processed, returning elements from "finalArray"
db.AssetTest.aggregate(
[
{'$match':{'$and':[{'attr2': {'$eq':'hello'}}]}},
{'$project': {'values': '$attr1.firstArray'}},
{'$unwind':'$values'},
{'$replaceRoot':{'newRoot':'$values'}},
{'$project': {'values': {'$filter': {'input': '$secondArray','cond': {'$and':[{'$eq':['$$this.secondAttr1', 'world']}]}}}}},
{'$unwind':'$values'},
{'$replaceRoot':{'newRoot':'$values'}},
{'$project': {'values': '$secondAttr3'}},
{'$unwind':'$values'},
{'$replaceRoot':{'newRoot':'$values'}},
{'$project': {'values': {'$filter': {'input': '$finalArray','cond': {'$and':[{'$eq':['$$this.finalAttr1', 'alex']}]}}}}},
{'$unwind':'$values'},
{'$replaceRoot':{'newRoot':'$values'}}
]
)
the result for this aggregate is
{
"finalAttr1" : "alex",
"finalAttr2" : 9876
}
my question is, how can I project root field "attr2" through all aggregation stages so it will be on result array?
{
"attr2" : "hello",
"finalAttr1" : "alex",
"finalAttr2" : 9876
}
I'm new to mongo and I'm completely lost on this so any help will be very appreciated.
By the way I'm using mongo 3.4.15.
Many Thanks!!
In your case all you need is $addFields which got introduced in MongoDB version 3.4 :
I've cut down few stages & operators which doesn't seems to be useful, Check this latest aggregation pipeline :
db.collection.aggregate([
{
"$match": { "attr2": "hello" }
},
/** This stage adds new field to the place where you want */
{
$addFields: { "attr1.firstArray.secondArray.secondAttr3.finalArray.attr2": "$attr2" }
},
{
"$unwind": "$attr1.firstArray"
},
{
"$replaceRoot": { "newRoot": "$attr1.firstArray" }
},
{
"$project": {
"secondArray": {
"$filter": { "input": "$secondArray", "cond": { "$eq": [ "$$this.secondAttr1", "world" ] } }
}
}
},
{
"$unwind": "$secondArray"
},
{
"$replaceRoot": { "newRoot": "$secondArray" }
},
{
"$unwind": "$secondAttr3"
},
{
"$replaceRoot": { "newRoot": "$secondAttr3" }
},
{
"$project": {
"finalArray": { "$filter": { "input": "$finalArray", "cond": { "$eq": [ "$$this.finalAttr1", "alex" ] } } }
}
},
{
"$unwind": "$finalArray"
},
{
"$replaceRoot": { "newRoot": "$finalArray"}
}
])
Test : mongoplayground
Ref : $addFields
Note :
You don't need to use $and on single conditions (Refer your 1st stage).
You don't need to multiple $project stages in the middle for conversion (Refer your 2nd stage).
I’m designing a NoSQL database for student attendance system, I want your advices to improve it since I’m new to this field.
I want to query 1. classes for specific student, 2. students on specific class, 3. Attendees for specific class on specific date, 4. attendance and absence count for specific student on specific class.
{
"attendance" : {
"CS 331" : {
"7-3-2019" : {
"2014901001" : true
}
}
},
"class" : {
"class01" : {
"id" : "CS 331",
"name" : "Software Design",
"students" : {
"2014901001" : true
}
}
},
"classEnrollment" : {
"CS 331" : {
"2014901001" : {
"absence" : 0,
"attendant" : 1
}
}
},
"instructor" : {
"instructor01" : {
"id" : "01"
}
},
"instructorEnrollment" : {
"01" : {
"CS 331" : true
}
},
"student" : {
"student01" : {
"id" : "2014901001",
"name" : "Paul Howard"
}
},
"studentEnrollment" : {
"2014901001" : {
"CS 331" : true
}
}
}
This what i will design for such a user requirement
{
"class_collection": {
"documents": [
{
"id": "CS_331_2019_1",
"type": "CS 331",
"name": "Software Design",
"enrolled_instructor": [
"instructor_id_1"
],
"enrolled_students": [
{
"id": "student_id_1",
"absence": 0,
"attendant": 2
},
{
"id": "student_id_2",
"absence": 2,
"attendant": 3
}
]
}
]
},
"class_attendance_collection": {
"documents": [
{
"2019-03-12T18:00:00": {
"class_id": "CS_331_2019_1",
"attended": ["student_id_1"],
"not_attended": ["student_id_2"]
}
},
{
"2019-03-13T16:00:00": {
"class_id": "CS_331_2019_1",
"attended": ["student_id_1","student_id_2"]
}
}
]
},
"student_collection": {
"documents": [
{
"id": "student_id_1",
"name": "name_1"
},
{
"id": "student_id_2",
"name": "name_2"
}
]
},
"instructor_collection": {
"documents": [
{
"id": "instructor_id_1",
"name": "name_1"
},
{
"id": "instructor_id_2",
"name": "name_2"
}
]
}
}
Have fun coding :)