MongoDB query using values priority - mongodb

I have following documents in my collection:
/* 1 */
{
"_id" : ObjectId("5ea32d11f213c27c35395fd3"),
"name" : "Test",
"state" : "OH",
"code" : "CDM"
}
/* 2 */
{
"_id" : ObjectId("5ea32d29f213c27c35395fe0"),
"name" : "Test1",
"state" : "ALL",
"code" : "CDM"
}
/* 3 */
{
"_id" : ObjectId("5ea32d38f213c27c35395fe7"),
"name" : "Test2",
"state" : "OH",
"code" : "ALL"
}
/* 4 */
{
"_id" : ObjectId("5ea32d46f213c27c35395feb"),
"name" : "Test3",
"state" : "ALL",
"code" : "ALL"
}
Trying to filter documents based on state and code.
But there are few criteria if particular state or code is not found then search with value ALL
Example 1:
state = CA
code = CDM
then return only
{
"_id" : ObjectId("5ea32d29f213c27c35395fe0"),
"name" : "Test1",
"state" : "ALL",
"code" : "CDM"
}
Example 2:
state = CA
code = DCM
then return only
{
"_id" : ObjectId("5ea32d46f213c27c35395feb"),
"name" : "Test3",
"state" : "ALL",
"code" : "ALL"
}
etc..
The query that i tried was state = CA ,code = CDM:
db.getCollection('user_details').aggregate([
{'$match': { 'state': {'$in': ['CA','ALL']},
'code': {'$in': ['CDM','ALL']}}}
])
return was:
/* 1 */
{
"_id" : ObjectId("5ea32d29f213c27c35395fe0"),
"name" : "Test1",
"state" : "ALL",
"code" : "CDM"
}
/* 2 */
{
"_id" : ObjectId("5ea32d46f213c27c35395feb"),
"name" : "Test3",
"state" : "ALL",
"code" : "ALL"
}
Expected was :
{
"_id" : ObjectId("5ea32d29f213c27c35395fe0"),
"name" : "Test1",
"state" : "ALL",
"code" : "CDM"
}
Could you help to solve this issue.

Try below query :
db.collection.find({
$or: [
{
state: "CA",
code: "CDM"
},
{
state: "ALL",
code: "CDM"
},
{
state: "ALL",
code: "ALL"
},
{
state: "CA",
code: "ALL"
}
]
})
So basically you can not achieve what you're looking for in one query, you can try aggregation operator $facet & $switch to do this but I would not prefer to do that cause each stage in facet will do the given operation on entire collection's data. Instead get fewer docs using above query & in your code you can actually filter for best suited doc, Which would be easy & efficient. Don't forget to maintain indexes on DB.
Test : mongoplayground

Try this one:
db.user_details.aggregate([
{
$facet: {
match: [
{
"$match": {
$or: [
{
"state": "CA",
"code": {
"$in": [ "CDM", "ALL" ]
}
},
{
"state": {
"$in": [ "CA", "ALL"]
},
"code": "CDM"
}
]
}
}
],
all: [
{
"$match": {
"state": "ALL",
code: "ALL"
}
}
]
}
},
{
$project: {
match: {
$cond: [
{
$eq: [ { $size: "$match" }, 0]
},
"$all",
"$match"
]
}
}
},
{
$unwind: "$match"
},
{
$replaceWith: "$match"
}
])
MongoPlayground

Related

match element in the array with aggregation

i have mongo db collection the follwing structure
{
{
"_id" : ObjectId("63e37afe7a3453d5014c011b"),
"schemaVersion" : NumberInt(1),
"Id" : "ObjectId("63e37afe7a3453d5014c0112")",
"Id1" : "ObjectId("63e37afe7a3453d5014c0113")",
"Id2" : "ObjectId("63e37afe7a3453d5014c0114")",
"collectionName" : "Country",
"List" : [
{
"countryId" : NumberInt(1),
"name" : "Afghanistan",
},{
"countryId" : NumberInt(1),
"name" : "India",
},
{
"countryId" : NumberInt(1),
"name" : "USA",
}
}
i need to match the value with id, id1, id2, collectionName and name in the list to get country id for example if match the below value
"Id" : "ObjectId("63e37afe7a3453d5014c0112")",
"Id1" : "ObjectId("63e37afe7a3453d5014c0113")",
"Id2" : "ObjectId("63e37afe7a3453d5014c0114")",
"collectionName" : "Country",
"name" : "Afghanistan",
i need result
{
"countryId" : 1,
"name" : "Afghanistan",
}
i tried like below
db.country_admin.aggregate([
{ $match: { collectionName: "Country" } },
{ $unwind : '$countryList' },
{ $project : { _id : 0, 'countryList.name' : 1, 'countryList.countryId' : 1 } }
]).pretty()
and i have following output
[
{
"List" : {
"countryId" : 1.0,
"name" : "Afghanistan"
}
},
{
"List" : {
"countryId" : 2.0,
"name" : "india"
}
},
{
"List" : {
"countryId" : 3.0,
"name" : "USA"
}
}]```
You can try using $filter to avoid $unwind like this example:
First $match by your desired condition(s).
Then $filter and get the first element (as "List.name": "Afghanistan" is used into $match stage there will be at least one result).
And output only values you want using $project.
db.collection.aggregate([
{
"$match": {
"Id": ObjectId("63e37afe7a3453d5014c0112"),
"Id1": ObjectId("63e37afe7a3453d5014c0113"),
"Id2": ObjectId("63e37afe7a3453d5014c0114"),
"collectionName": "Country",
"List.name": "Afghanistan",
}
},
{
"$project": {
"country": {
"$arrayElemAt": [
{
"$filter": {
"input": "$List",
"cond": {
"$eq": [
"$$this.name",
"Afghanistan"
]
}
}
},
0
]
}
}
},
{
"$project": {
"_id": 0,
"countryId": "$country.countryId",
"name": "$country.name"
}
}
])
Example here
By the way, using $unwind is also possible and you can check this example

Facing a problem with the lookup in the second (student) table that matches all incoming output records mongodb aggregation

I'm facing a problem with the lookup in the second (student) table that matches all incoming output records of the first(test) table. I have two collections "tests" and "students". "Test" collection contains all school tests and the "student" table contains student's attended tests. Student table contains "pastTest"(test attended in past with status "pass" or "fail")array. I want to retrieve student who passed all incoming tests (we retrieve from the tests table)
test table: _id (primary ket)
student.pastTests.testId (need to match with test._id)
Test Document:
{
"_id" : ObjectId("5c9b5c1005729b2bf23f3290"),
"testDate" : {
"term" : 1,
"week" : 7
},
"retestDate" : {
"term" : 1,
"week" : 10
},
"testOrder" : "1.1",
"testDateScheduled" : true,
"retestDateScheduled" : true
}
Student Document:
{
"_id" : ObjectId("5c92dd994e8e6b2c1647d0d0"),
"completedYears" : [],
"firstName" : "Andrew",
"lastName" : "Jonhson",
"teacherId" : ObjectId("5bf36b1076696374e65feb4f"),
"yearGroup" : "0",
"schoolId" : 40001,
"currentTest" : ObjectId("5c9b5c1005729b2bf23f3290"),
"pastTests" : [
{
"_id" : ObjectId("5d3570645045863d373f6db1"),
"testId" : ObjectId("5c9b5c1005729b2bf23f3290"),
"status" : "pass"
},
{
"_id" : ObjectId("5d425af07708f5636c3bec1c"),
"testId" : ObjectId("5c9b5fc460e39c2c58e44109"),
"status" : "pass"
},
{
"_id" : ObjectId("5d5e54a875fab079f4d03570"),
"testId" : ObjectId("5c9b6492bb581c2ceb553fef"),
"status" : "fail"
},
],
"createdAt" : ISODate("2019-03-21T00:40:57.401Z"),
"updatedAt" : ISODate("2020-09-24T19:55:38.291Z"),
"__v" : 0,
"holdTests" : [],
"completedTests" : [],
"className" : "dd",
}
Query:
db.getCollection('tests').aggregate([
{
$match: {
yearGroup: '-1',
$or : [
{
$and: [
{'retestDateScheduled': true},
{ 'retestDate.term': { $lt: 4 } },
]
},
{
$and: [
{'testDateScheduled': true},
{ 'testDate.term': { $lt: 4 } },
]
}
]
}
},
{
$lookup: {
from: 'students',
let: {testId: '$_id', schoolId: 49014, yearGroup: '-1'},
pipeline: [
]
}
}
])
Note: Initial match query returns all tests of the term-1, now I have to retrieve students who passed in all tests of the term-1.
Lookup stage is pending - facing problem with lookup in second (student) table who match all incoming output records of first(test) collection
Thanks in advance !!
Try this:
db.tests.aggregate([
{
$match: {
// Your match condition
}
},
{
$group: {
_id: null,
term_1_testIds: { $push: "$_id" },
test_count: { $sum: 1 }
}
},
{
$lookup: {
from: "students",
let: { term_1_testIds: '$term_1_testIds', schoolId: 40001, totalTestCount: "$test_count" },
pipeline: [
{
$match: {
$expr: { $eq: ["$schoolId", "$$schoolId"] }
}
},
{ $unwind: "$pastTests" },
{
$match: {
"pastTests.status": "pass",
$expr: { $in: ["$pastTests.testId", "$$term_1_testIds"] }
}
},
{
$group: {
_id: "$_id",
firstName: { $first: "$firstName" },
yearGroup: { $first: "$yearGroup" },
schoolId: { $first: "$schoolId" },
currentTest: { $first: "$currentTest" },
passedTestCount: { $sum: 1 },
pastTests: { $push: "$pastTests" }
}
},
{
$match: {
$expr: { $eq: ["$passedTestCount", "$$totalTestCount"] }
}
}
],
as: "students"
}
}
]);
Output:
{
"_id" : null,
"term_1_testIds" : [
ObjectId("5c9b5c1005729b2bf23f3290"),
ObjectId("5c9b5fc460e39c2c58e44109"),
ObjectId("5c9b6492bb581c2ceb553fef")
],
"test_count" : 3,
"students" : [
{
"_id" : ObjectId("5c92dd994e8e6b2c1647d0d1"),
"firstName" : "Dheemanth",
"yearGroup" : "0",
"schoolId" : 40001,
"currentTest" : ObjectId("5c9b5c1005729b2bf23f3290"),
"passedTestCount" : 3,
"pastTests" : [
{
"_id" : ObjectId("5d3570645045863d373f6db1"),
"testId" : ObjectId("5c9b5c1005729b2bf23f3290"),
"status" : "pass"
},
{
"_id" : ObjectId("5d425af07708f5636c3bec1c"),
"testId" : ObjectId("5c9b5fc460e39c2c58e44109"),
"status" : "pass"
},
{
"_id" : ObjectId("5d5e54a875fab079f4d03570"),
"testId" : ObjectId("5c9b6492bb581c2ceb553fef"),
"status" : "pass"
}
]
}
]
}
This how my tests collection looks like
/* 1 createdAt:3/27/2019, 5:24:58 PM*/
{
"_id" : ObjectId("5c9b6492bb581c2ceb553fef"),
"name" : "Test 3"
},
/* 2 createdAt:3/27/2019, 5:04:28 PM*/
{
"_id" : ObjectId("5c9b5fc460e39c2c58e44109"),
"name" : "Test 2"
},
/* 3 createdAt:3/27/2019, 4:48:40 PM*/
{
"_id" : ObjectId("5c9b5c1005729b2bf23f3290"),
"name" : "Test 1"
}
This is how my students collection looks like:
/* 1 createdAt:3/21/2019, 6:10:57 AM*/
{
"_id" : ObjectId("5c92dd994e8e6b2c1647d0d1"),
"firstName" : "Dheemanth",
"yearGroup" : "0",
"schoolId" : 40001,
"currentTest" : ObjectId("5c9b5c1005729b2bf23f3290"),
"pastTests" : [
{
"_id" : ObjectId("5d3570645045863d373f6db1"),
"testId" : ObjectId("5c9b5c1005729b2bf23f3290"),
"status" : "pass"
},
{
"_id" : ObjectId("5d425af07708f5636c3bec1c"),
"testId" : ObjectId("5c9b5fc460e39c2c58e44109"),
"status" : "pass"
},
{
"_id" : ObjectId("5d5e54a875fab079f4d03570"),
"testId" : ObjectId("5c9b6492bb581c2ceb553fef"),
"status" : "pass"
}
]
},
/* 2 createdAt:3/21/2019, 6:10:57 AM*/
{
"_id" : ObjectId("5c92dd994e8e6b2c1647d0d0"),
"firstName" : "Andrew",
"yearGroup" : "0",
"schoolId" : 40001,
"currentTest" : ObjectId("5c9b5c1005729b2bf23f3290"),
"pastTests" : [
{
"_id" : ObjectId("5d3570645045863d373f6db1"),
"testId" : ObjectId("5c9b5c1005729b2bf23f3290"),
"status" : "pass"
},
{
"_id" : ObjectId("5d425af07708f5636c3bec1c"),
"testId" : ObjectId("5c9b5fc460e39c2c58e44109"),
"status" : "pass"
},
{
"_id" : ObjectId("5d5e54a875fab079f4d03570"),
"testId" : ObjectId("5c9b6492bb581c2ceb553fef"),
"status" : "fail"
}
]
}
Also:
In your first $match stage, $and operator is redundant inside $or array it should be like this:
{
$match: {
yearGroup: '-1',
$or: [
{
'retestDateScheduled': true,
'retestDate.term': { $lt: 4 }
},
{
'testDateScheduled': true,
'testDate.term': { $lt: 4 }
}
]
}
}

Finding/Counting Duplicate Values in Array in MongoDB

I am new to the mongo database. Using Robo3t software
I have to find out duplicate values inside an array based on channel_id.
I did a research and found that aggregation needs to be used to do grouping and find respective count.
I have developed the following query but results are not as expected.
Sample Documents:
{
"_id" : ObjectId("59b674d141b47e5401897d31"),
"subscribed_channels" : [
{
"channel_id" : "1001",
"channel_name" : "StarPlus",
"channelPrice":"100"
},
{
"channel_id" : "1002",
"channel_name" : "StarGold",
"channelPrice":"75"
},
{
"channel_id" : "1001",
"channel_name" : "StarPlus",
"channelPrice":"100"
},
{
"channel_id" : "1003",
"channel_name" : "SetMax",
"channelPrice":"80"
}
],
"viewer_account_id" : "59b6745b41b47e5401143b3d",
"public_id_type" : "PHONE_NUMBER",
"viewer_id" : "+919322264403",
"role" : "CONSUMER",
"active" : true,
"date_time_created" : NumberLong(1505129681330),
"date_time_modified" : NumberLong(1569320824387)
}
{
"_id" : ObjectId("59b674d141b47e5401897d31"),
"subscribed_channels" : [
{
"channel_id" : "1001",
"channel_name" : "StarPlus",
"channelPrice":"100"
},
{
"channel_id" : "1002",
"channel_name" : "StarGold",
"channelPrice":"75"
},
{
"channel_id" : "1001",
"channel_name" : "StarPlus",
"channelPrice":"100"
},
{
"channel_id" : "1001",
"channel_name" : "StarPlus",
"channelPrice":"100"
}
],
"viewer_account_id" : "59b6745b41b47e5401143c56",
"public_id_type" : "PHONE_NUMBER",
"viewer_id" : "+919322264404",
"role" : "CONSUMER",
"active" : true,
"date_time_created" : NumberLong(1505129681330),
"date_time_modified" : NumberLong(1569320824387)
}
Above are just 2 records of document viewers
Query :
db.getCollection('viewers').aggregate([
{
"$group" :
{_id:{
//viewer_id:"$consumer_id",
enterprise_id:"$subscribed_channels.channel_id",
},
"viewer_id": {
$first: "$viewer_id"
},
count:{$sum:1}
}},
{
"$match": {"count": { "$gt": 1 }}
}
])
Actual Output :
{
"_id" : {
"enterprise_id" : [
"1001",
"1001",
"1002",
"1003"
]
},
"consumer_id" : "+919322264403",
"count" : 2.0
}
{
"_id" : {
"enterprise_id" : [
"1001",
"1002",
"1001",
"1001
]
},
"consumer_id" : "+919322264404",
"count" : 2.0
}
Expected Output :
I want to group based on subscribed_channels.channel_id and get a count respectively
{
"_id" : {
"enterprise_id" : [
"1001",
"1001",
"1002",
"1003"
]
},
"consumer_id" : "+919322264403",
"count" : 2.0
}
{
"_id" : {
"enterprise_id" : [
"1001",
"1001",
"1001",
"1002
]
},
"consumer_id" : "+919322264404",
"count" : 3.0
}
Grouping is not happening based on channel_id, also the count is incorrect.
The count is not even giving me no of channel-id subscribed, also not giving duplicate channel_ids.
Please guide me in building a query that gives the correct result.
Try below query :
Query :
db.collection.aggregate([
/** project only needed fields & transform fields as you like */
{
$project: {
customer_id: "$viewer_id",
enterprise_id: "$subscribed_channels.channel_id",
count: {
/** Subtract size of original array & newly formed array which has unique values to get count of duplicates */
$subtract: [
{
$size: "$subscribed_channels.channel_id" // get size of original array
},
{
$size: {
$setUnion: ["$subscribed_channels.channel_id", []] // This will give you an array with unique elements & get size of it
}
}
]
}
}
}
]);
Test : MongoDB-Playground

Querying Properties Array mongodb

I am trying to get building by its id:
I have a collection called buildings:
{
"_id" : ObjectId("5b3b1cc79c23061e4d4634e4"),
"buildings" : [
{
"id" : 0,
"name" : "Farm",
"description" : "Best farm of all times",
"img" : "http://i.hizliresim.com/yq5g57.png",
"wood" : "50",
"stone" : "10"
},
{
"id" : 1,
"name" : "Storage",
"description" : "Store your resources.",
"img" : "http://i.hizliresim.com/yq5g47.png",
"wood" : "100",
"stone" : "200"
}
]
}
For example with id 0,i would like to get data of Farm.
I tried this:
db.getCollection('buildings').find({"buildings.id":0})
not working
sample output :
{
"id" : 0,
"name" : "Farm",
"description" : "Best farm of all times",
"img" : "http://i.hizliresim.com/yq5g57.png",
"wood" : "50",
"stone" : "10"
}
Tried:
var data = Buildings.find({},{buildings:{$elemMatch:{id:0}}}).fetch();
console.log(JSON.stringify(data));
result:(all data)
[{"_id":{"_str":"5b3b1cc79c23061e4d4634e4"},"buildings":[{"id":0,"name":"Farm","description":"Best farm of all times","img":"http://i.hizliresim.com/yq5g57.png","wood":"50","stone":"10"},{"id":1,"name":"Storage","description":"Store your resources.","img":"http://i.hizliresim.com/yq5g47.png","wood":"100","stone":"200"}]}]
You can use $filter aggregation to exclude the unwanted elements from the array
db.collection.aggregate([
{ "$match": { "buildings.id": 0 }},
{ "$project": {
"shapes": {
"$arrayElemAt": [
{ "$filter": {
"input": "$buildings",
"as": "building",
"cond": {
"$eq": [
"$$building.id",
0
]
}
}},
0
]
},
"_id": 0
}}
])
Try for this
db.getCollection("collectionName").find({buildings: {"$elemMatch": {"id" : "0"}}})
Here the find method will look(cursor) for the data with buildings and id=0
db.collection.find({
buildings: {
$elemMatch: {
id: 0
}
}
}, {
'buildings.$': 1
})

mongodb aggregation match multiple $and on the same field

i have a document like this :
{
"ExtraFields" : [
{
"value" : "print",
"fieldID" : ObjectId("5535627631efa0843554b0ea")
},
{
"value" : "14",
"fieldID" : ObjectId("5535627631efa0843554b0eb")
},
{
"value" : "POLYE",
"fieldID" : ObjectId("5535627631efa0843554b0ec")
},
{
"value" : "30",
"fieldID" : ObjectId("5535627631efa0843554b0ed")
},
{
"value" : "0",
"fieldID" : ObjectId("5535627631efa0843554b0ee")
},
{
"value" : "0",
"fieldID" : ObjectId("5535627731efa0843554b0ef")
},
{
"value" : "0",
"fieldID" : ObjectId("5535627831efa0843554b0f0")
},
{
"value" : "42",
"fieldID" : ObjectId("5535627831efa0843554b0f1")
},
{
"value" : "30",
"fieldID" : ObjectId("5535627831efa0843554b0f2")
},
{
"value" : "14",
"fieldID" : ObjectId("5535627831efa0843554b0f3")
},
{
"value" : "19",
"fieldID" : ObjectId("5535627831efa0843554b0f4")
}
],
"id" : ObjectId("55369e60733e4914550832d0"), "title" : "A product"
}
what i want is to match one or more sets from the ExtraFields array. For example, all the products that contain the values print and 30. Since a value may be found in more than one fieldID (like 0 or true) we need to create a set like
WHERE (fieldID : ObjectId("5535627631efa0843554b0ea"), value : "print")
Where i'm having problems is when querying more than one fields. The pipeline i came up with is :
db.products.aggregate([
{'$unwind': '$ExtraFields'},
{
'$match': {
'$and': [{
'$and': [{'ExtraFields.value': {'$in': ["A52A2A"]}}, {
'ExtraFields.fieldID': ObjectId("5535627631efa0843554b0ea")
}]
}
,
{
'$and': [{'ExtraFields.value': '14'}, {'ExtraFields.fieldID': ObjectId("5535627631efa0843554b0eb")}]
}
]
}
},
]);
This returns zero results, but this is what i want to do in theory. Match all items that contain set 1 AND all that contain set 2.
The end result should look like a faceted search output :
[
{
"_id" : {
"values" : "18",
"fieldID" : ObjectId("5535627831efa0843554b0f3")
},
"count" : 2
},
{
"_id" : {
"values" : "33",
"fieldID" : ObjectId("5535627831efa0843554b0f2")
},
"count" : 1
}
]
Any ideas?
You could try the following aggregation pipeline
db.products.aggregate([
{
"$match": {
"ExtraFields.value": { "$in": ["A52A2A", "14"] },
"ExtraFields.fieldID": {
"$in": [
ObjectId("5535627631efa0843554b0ea"),
ObjectId("5535627631efa0843554b0eb")
]
}
}
},
{
"$unwind": "$ExtraFields"
},
{
"$match": {
"ExtraFields.value": { "$in": ["A52A2A", "14"] },
"ExtraFields.fieldID": {
"$in": [
ObjectId("5535627631efa0843554b0ea"),
ObjectId("5535627631efa0843554b0eb")
]
}
}
},
{
"$group": {
"_id": {
"value": "$ExtraFields.value",
"fieldID": "$ExtraFields.fieldID"
},
"count": {
"$sum": 1
}
}
}
])
With the sample document provided, this gives the output:
/* 1 */
{
"result" : [
{
"_id" : {
"value" : "14",
"fieldID" : ObjectId("5535627631efa0843554b0eb")
},
"count" : 1
}
],
"ok" : 1
}