How to boost Mongodb search result based on Criteria given - mongodb

I am working on the requirement where have write query in which if users enters any acronym of university(Ex: MIT) have to get the result from database. JSON looks like this:
{
"_id" : ObjectId("5d68cdcac8acd826e6a386b2"),
"name" : "Massachusetts Institute of Technology",
"acronyms" : [
"MIT"
]
}
,
{
"_id" : ObjectId("5d68ce0bc8acd826e6a45b29"),
"name" : "Manukau Institute of Technology",
"acronyms" : [
"MIT"
]
}
User might input "Name" as well. I have written "OR" query for that.
db.getCollection('universityCollection').find(
{$or: [{"name":"MIT"},{"acronyms":"MIT"}]}
)
Now my requirement is if users enters "input" and if it matches with acronym it should return it first after that it will return items which matches with name.
Current or query is not returning expected order.
Any pointers will help.

Please try below query.
db.getCollection('test').aggregate(
{ $match : { $or : [{ "name":"MIT" }, {"acronyms":"MIT" } ] } }
,{ "$project": {
"name": 1,
"acronyms": 1,
"sortOrder": {
"$setIsSubset": [ ["MIT" ] , "$acronyms" ] }
}
}
,{ "$sort": { "sortOrder": -1 } }
)
If you are not familiar with MongoDB aggregates, check the below links.
https://docs.mongodb.com/manual/reference/method/db.collection.aggregate/
https://docs.mongodb.com/manual/reference/operator/aggregation/setIsSubset/

Related

Complex MongoDB query?

I'm pretty brand new to Mongo and queries still, so that said, I'm trying to build a query that will find me results that match these three types of dog breeds and in addition to that, check for additional two specs. And finally, sort all by age. All the data comes from a csv file (scrnshot), there aren't any sub categories to any of the entries.
db.animals.find({
"animal_id" : 1,
"breed" : "Labrador Retriever Mix",
"breed" : "Chesapeake Bay Retriever",
"breed" : "Newfoundland",
$and : [ { "age_upon_outcome_in_weeks" :{"$lt" : 156, "$gte" : 26} ],
$and: {"sex_upon_outcome" : "Intact Female"}}).sort({"age_upon_outcome_in_weeks" : 1})
This is throwing a number of errors, such as :
Error: error: {
"ok" : 0,
"errmsg" : "$and must be an array",
"code" : 2,
"codeName" : "BadValue"
}
What am I messing up? Or is there a better way to do it?
As mentionend by takis in the comments, you cannot repeat a key in a mongo query - you have to imagine that your query document becomes a json object, and each time a key is repeated is replaces the previous one. To go around this problem, mongodb supports $or and $and operators. For complex queries like this one, I would recommend starting with a global each containing a single constraint or a $or constraint. Your query becomes this:
db.coll.find({
"$and": [
{ "animal_id": 1 },
{ "age_upon_outcome_in_weeks": { "$lt": 156, "$gte": 26 } },
{ "sex_upon_outcome": "Intact Female" },
{ "$or": [
{ "breed": "Labrador Retriever Mix" },
{ "breed": "Chesapeake Bay Retriever" },
{ "breed": "Chesapeake Bay Retriever" },
{ "breed": "Newfoundland" }
]
}
]
})
.sort({"age_upon_outcome_in_weeks" : 1})
--- edit
You can also consider using the $in instead of the $or:
db.coll.find({
"animal_id": 1,
"age_upon_outcome_in_weeks": { "$lt": 156, "$gte": 26 },
"sex_upon_outcome": "Intact Female",
"breed": { "$in": [
"Labrador Retriever Mix",
"Chesapeake Bay Retriever",
"Chesapeake Bay Retriever",
"Newfoundland"
] }
})
.sort({"age_upon_outcome_in_weeks" : 1})

How to update Meteor array element inside a document

I have a Meteor Mongo document as shown below
{
"_id" : "zFndWBZTvZPgSKXHP",
"activityId" : "aRDABihAYFoAW7jbC",
"activityTitle" : "Test Mongo Document",
"users" : [
{
"id" : "b1#gmail.com",
"type" : "free"
},
{
"id" : "JqKvymryNaCjjKrAR",
"type" : "free"
},
],
}
I want to update a specific array element's email with custom generated id using Meteor query something like the below.
for instance, I want to update the document
if 'users.id' == "b1#gmail.com" then update it to users.id = 'SomeIDXXX'
So updated document should looks like below.
{
"_id" : "zFndWBZTvZPgSKXHP",
"activityId" : "aRDABihAYFoAW7jbC",
"activityTitle" : "Test Mongo Document",
"users" : [
{
"id" : "SomeIDXXX",
"type" : "free"
},
{
"id" : "JqKvymryNaCjjKrAR",
"type" : "free"
},
],
}
I have tried the below but didnt work.
Divisions.update(
{ activityId: activityId, "users.id": emailId },
{ $set: { "users": { id: _id } } }
);
Can someone help me with the relevant Meteor query ? Thanks !
Your query is actually almost right except for a small part where we want to identify the element to be updated by its index.
Divisions.update({
"activityId": "aRDABihAYFoAW7jbC",
"users.id": "b1#gmail.com"
}, {
$set: {"users.$.id": "b2#gmail.com"}
})
You might need the arrayFilters option.
Divisions.update(
{ activityId: activityId },
{ $set: { "users.$[elem].id": "SomeIDXXX" } },
{ arrayFilters: [ { "elem.id": "b1#gmail.com" } ], multi: true }
);
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/
You need to use the $push operator instead of $set.
{ $push: { <field1>: <value1>, ... } }

How to combine Documents in aggregation pipeline with MongoDB Java driver 3.6?

I am using an aggregation pipeline with the MongoDB Java driver version 3.6. If I have documents that look something like:
doc1 --
{
"CAR": {
"VIN": "ASDF1234",
"YEAR": "2018",
"MAKE": "Honda",
"MODEL": "Accord"
},
"FEATURES": [
{
"AUDIO": "MP3",
"TIRES": "All Season",
"BRAKES": "ABS"
}
]
}
doc2 --
{
"CAR": {
"VIN": "ASDF1234",
"AVAILABILITY": "In Stock"
}
}
And if I submit a query like:
collection.aggregate(
Arrays.asList(
Aggregates.match(
and(
in("CAR.VIN", vinList),
or(
eq("CAR.MAKE", carMake),
eq("CAR.AVAILABILITY", carAvailability),
)
)
)
)
)
Let us assume that there are exactly two different records for which the "CAR.VIN" criteria match for every VIN, and I am going to get two results. Rather than deal with two results each time, I would like to merge the documents so that the result looks like this:
{
"CAR": {
"VIN": "ASDF1234",
"YEAR": "2018",
"MAKE": "Honda",
"MODEL": "Accord",
"AVAILABILITY": "In Stock"
},
"FEATURES": [
{
"AUDIO": "MP3",
"TIRES": "All Season",
"BRAKES": "ABS"
}
]
}
The example where I have two and only two results trivializes my need for this. Imagine that vinList is a list of 10000 values, and it might return 2 x 10000 documents. When I return an AggregateIterable to the client that is calling my code, I do not want to impose the requirement that they have to group or collate the results in any way, but that they will receive one document for each result that has all of the information that they will want to parse, cleanly and easily.
Of course, people will suggest that the data is simply combined into one document with all of the data in the MongoDB collection. For reasons that I cannot control, there are two separate documents corresponding to each VIN in the same collection, and that is something that I am unable to change. There is a value in our system that makes this more reasonable than it might seem, so please don't focus on this apparent problem with the data.
I am trying, with not much luck, to utilize the Aggretes.group() operation to merge the fields in my aggregation pipeline. Accumulators.push seems to be the closest operation to what I need, but I do not want to complicate the document structure with extra arrays, etc. Is there a straightforward approach that I am not seeing?
you can try $mergeObjects added in mongo v3.6
db.cc.aggregate(
[
{
$group: {
_id : "$CAR.VIN",
CAR : {$mergeObjects : "$CAR"},
FEATURES : {$mergeObjects : {$arrayElemAt : ["$FEATURES", 0 ]}}
}
}
]
).pretty()
result
{
"_id" : "ASDF1234",
"CAR" : {
"VIN" : "ASDF1234",
"YEAR" : "2018",
"MAKE" : "Honda",
"MODEL" : "Accord",
"AVAILABILITY" : "In Stock"
},
"FEATURES" : {
"AUDIO" : "MP3",
"TIRES" : "All Season",
"BRAKES" : "ABS"
}
}
>
to get features as array
db.cc.aggregate(
[
{
$group: {
_id : "$CAR.VIN",
CAR : {$mergeObjects : "$CAR"},
FEATURES : {$push : {$arrayElemAt : ["$FEATURES", 0 ]}}
}
}
]
).pretty()
result
{
"_id" : "ASDF1234",
"CAR" : {
"VIN" : "ASDF1234",
"YEAR" : "2018",
"MAKE" : "Honda",
"MODEL" : "Accord",
"AVAILABILITY" : "In Stock"
},
"FEATURES" : [
{
"AUDIO" : "MP3",
"TIRES" : "All Season",
"BRAKES" : "ABS"
},
null
]
}
>

Getting the total documents that is relevant to a value in MongoDB

I'm quite new to mongodb so please pardon my obliviousness.
A sample of my database is provided below :
{
"_id" : ObjectId("58db82583d5b9f0a47c3db6f"),
"People" : [
"Ivar Zapp"
],
"Link" : [
"https://wikileaks.org/gifiles/docs/19/1971280_panama-costa-rica-cuba-111104-.html"
]
}
{
"_id" : ObjectId("58db82583d5b9f0a47c3db70"),
"People" : [
"Ivar Zapp"
],
"Link" : [
"https://wikileaks.org/gifiles/docs/87/872609_panama-costa-rica-cuba-111104-.html"
]
}
{
"_id" : ObjectId("58db82583d5b9f0a47c3db71"),
"People" : [
"Ivar Zapp"
],
"Link" : [
"https://wikileaks.org/gifiles/docs/19/1964024_-latam-centam-brief-111104-.html"
]
}
My question now is, according to the snippet above, how do I get the count of the link relevant to a person's name? Example output below :
{
"People" : "Ivar Zapp",
"link_Count" : "3"
}
I've thought of aggregation, but I can't really seem to figure out a solution using aggregation for quite a while now. I'm open to any solutions or feedback as long as it helps me progress just a tad bit. Any solutions or feedback provided is most appreciated! Thanks in advance!
You need to use $group operations:
You try following command,
db.collection.aggregate([
{ $group: {
_id: '$People',
count: { $sum: 1 }
}},
{ $project: {
_id: 0,
People: '$_id',
count: 1
}}
]);
Output :
{
"count": 3,
"People": ["Ivar Zapp"]
}

Is there a way to query for dict of arrays of array in MongoDB

My mongo db collection contains the structure as :
{
"_id" : ObjectId("5889ce0d2e9bfa938c49208d"),
"filewise_word_freq" : {
"33236365" : [
[
"cluster",
4
],
[
"question",
2
],
[
"differ",
2
],[
"come",
1
]
],
"33204685" : [
[
"node",
6
],
[
"space",
4
],
[
"would",
3
],[
"templat",
1
]
]
},
"file_root" : "socialcast",
"main_cluster_name" : "node",
"most_common_words" : [
[
"node",
16
],
[
"cluster",
7
],
[
"n't",
3
]
]
}
I want to search for a value "node" inside the arrays of arrays of the filename (in my case its "33236365","33204685" and so on...) of the dict filewise_word_freq.
And if the value("node") is present inside any one of the array of arrays of the filename(33204685), then should return the filename(33204685).
I tried from this link of stackoverflow :
enter link description here
I tried to execute for my use case it didn't work. And above all this I didn't no how to return only the filename rather the entire object or document.
db.frequencydist.find({"file_root":'socialcast',"main_cluster_name":"node","filewise_word_freq":{$elemMatch:{$elemMatch:{$elemMatch:{$in:["node"]}}}}}).pretty().
It returned nothing.
Kindly help me.
the data model you have chosen has made it extremely difficult to either query or even for aggregation. I would suggest to revise your document model. However I think you can use $where
db.collection.find({"file_root": 'socialcast',
"main_cluster_name": "node", $where : "for(var i in this.filewise_word_freq){for(var j in this.filewise_word_freq[i]){if(this.filewise_word_freq[i][j].indexOf("node")>=0){return true}}}"})
yes, this will return you the whole document and from your application you might need to filter the files name out.
you might also want to see map-reduce functionality, though that's not recommended.
One other way is to do it through functions, functions runs on mongo server and are saved in a special collection.
Still going back to the db model, do revise it if that's a possibility. maybe something like
{
"_id" : ObjectId("5889ce0d2e9bfa938c49208d"),
"filewise_word_freq" : [
{
"fileName":"33236365",
"word_counts" : {
"cluster":4,
"question":2,
"differ":2,
"come":1
}
},
{
"fileName":"33204685",
"word_counts" : {
"node":6,
"space":4,
"would":3,
"template":1
}
}
]
"file_root" : "socialcast",
"main_cluster_name" : "node",
"most_common_words" : [
{
"node":16
},
{
"cluster":7
},
{
"n't":3
}
]
}
It would be a lot easier to run aggregation on these.
For this model, the aggregation would be something like
db.collection.aggregate([
{$unwind : "$filewise_word_freq"},
{$match : {'filewise_word_freq.word_counts.node' : {$gte : 0}}},
{$group :{_id: 1, fileNames : {$addToSet : "$filewise_word_freq.fileName"}}},
{$project :{ _id:0}}
])
this will provide you a single document with a single field fileNames with list of all the filename
{
fileNames : ["33204685"]
}
You can try something like this. This will match the node as part of the query and returns filewise_word_freq.33204685 as part of the projection.
db.collection.find({
"file_root": 'socialcast',
"main_cluster_name": "node",
"filewise_word_freq.33204685": {
$elemMatch: {
$elemMatch: {
$in: ["node"]
}
}
}
}, {
"filewise_word_freq.33204685": 1
}).pretty();