How to read multikey index values in MongoDB? - mongodb

I would like to have autocomplete feature that suggests keywords from database. If I use MongoDB and multikey index, I kind of already have these keywords in database, but can I access them somehow?
If I have following object in collection:
{
"title": "Some awesome title",
"keywords": [
"some",
"awesome",
"title"
]
}
And I have multikey index:
db.somecollection.ensureIndex({ "keywords" : 1 })
So index would contain following values:
"some"
"awesome"
"title"
If user enters "s" letter into autocomplete control, application should suggest "some" keyword. Can I search keywords from index or how should I do this?

You can choose to use aggregation framework:
DEV:history:PRI > db.test.find()
{ "_id" : ObjectId("51c37c0c20d107378f9af3cc"), "title" : "Some awesome title", "keywords" : [ "some", "awesome", "title" ] }
{ "_id" : ObjectId("51c37de420d107378f9af3ce"), "title" : "Some awesome title", "keywords" : [ "some", "awesome", "something" ] }
{ "_id" : ObjectId("51c37f1920d107378f9af3cf"), "title" : "Some awesome title", "keywords" : [ "something", "awesome", "someone" ] }
DEV:history:PRI > db.test.aggregate({$match : {keywords : /^som/}}, {$project:{keywords:1, _id : 0}}, {$unwind : "$keywords"}, {$match : {keywords : /^som/}}, {$group: {_id : '$keywords', count : {$sum : 1}}})
{
"result" : [
{
"_id" : "someone",
"count" : 1
},
{
"_id" : "something",
"count" : 2
},
{
"_id" : "some",
"count" : 2
}
],
"ok" : 1
}
Note: Multi-key index are not covered and there are some JIRA issues already filed. So, despite of using indexes the queries are not covered (for multi-key indexes)
We can optionally use the 'count' params to decide on the ordering in showing the auto-complete. If you don't require, remove from the query.

Related

Adding new field to the document in the array of documents

I have mongo document like that:
{
"_id" : ObjectId("61b4cd63465cd7ace1e12341"),
"artist" : "Short",
"album" : "Track",
"tracks" : [
{
"title" : "100m",
"length" : 10
},
{
"title" : "200m",
"length" : 20,
"guest" : "Big Bad"
}
]
}
I'm trying to add field quest to the tracks array with title 100m.
So that a document looks like that:
{
"_id" : ObjectId("61b4cd63465cd7ace1e12341"),
"artist" : "Short",
"album" : "Track",
"tracks" : [
{
"title" : "100m",
"length" : 10,
"guest": : "John Travolta"
},
{
"title" : "200m",
"length" : 20,
"guest" : "Big Bad"
}
]
}
I was tryin got achive that using db.collection.update and $set, but no good results.
How can I achieve that?
You have to use positional operator $ like this:
db.collection.update({
"tracks.title": "100m"
},
{
"$set": {
"tracks.$.guest": "John Travolta"
}
})
This query tells to mongo "For the element into the tracks array where the title is 100m, insert the field guest with value John Travolta". And all of this using $ and the query stage "tracks.title": "100m"
Example here

Mongo Update Query on Array Index value matching

student collection look like below.
[{
"_id" : NumberLong(1),
"studentId" : "70133",
"subjects" : [
"Mathematics",
"Biology and Zoology"
]
},
{
"_id" : NumberLong(2),
"studentId" : "70134",
"subjects" : [
"Mathematics",
"English"
]
},
{
"_id" : NumberLong(3),
"studentId" : "70135",
"subjects" : [
"English",
"Mathematics",
"Chemistry"
]
}]);
I want to write a query which will update the student collection subject array values like this:
If subject matches English have to be updated to ENG
If subject matches Biology and Zoology have to be updated to BAZ
If subject matches Mathematics have to be updated to MAT
If subject matches Chemistry have to be updated to CHM
After update documents should look like below. How Can I achieve this.
[{
"_id" : NumberLong(1),
"studentId" : "70133",
"subjects" : [
"MAT",
"BAZ"
]
},
{
"_id" : NumberLong(2),
"studentId" : "70134",
"subjects" : [
"MAT",
"ENG"
]
},
{
"_id" : NumberLong(3),
"studentId" : "70135",
"subjects" : [
"ENG",
"MAT",
"CHM"
]
}]);
This has to be done with multiple updates like:
db.collection.update(
{ "subjects" : { $in : [ "Mathematics" ] } },
{ $set: { "subjects.$" : "MAT" } },
{ multi: true }
)
See this page for more information regarding updating multiple document
if multi is set to true, the db.collection.update() method updates all documents that meet the criteria. The multi update operation may interleave with other read/write operations.
https://docs.mongodb.com/master/reference/method/db.collection.update/index.html#multi-parameter

MongoDB positional operator with nested arrays

Sample collection structure:
{
"_id" : ObjectId("57cfd62001ca2dd672cfebb1"),
"name" : "Category",
"parent" : ObjectId("57cfd5d101ca2dd672cfebb0"),
"posts" : [
{
"name" : "Post",
"author" : ObjectId("57cfd09401ca2dd672cfebac"),
"content" : "Some content.",
"comments" : [
{
"author" : ObjectId("57cfd09401ca2dd672cfebab"),
"content" : "First comment",
"rating" : 2
},
{
"author" : ObjectId("57cfd09401ca2dd672cfebac"),
"content" : "Second comment",
"rating" : 5
}
]
}
]
}
I would like to select all comments whose author is ObjectId("57cfd09401ca2dd672cfebab").
This query is working,
db.categories.find({ 'posts.comments.author':ObjectId("57cfd09401ca2dd672cfebab") })
but I would like to return only first matching comment with positional operator. Something like this is not working. Does MongoDB support positional operator with nested arrays?
db.categories.find({ 'posts.comments.author': ObjectId("57cfd09401ca2dd672cfebab") },
{ 'posts.comments.$': 1 })
You are having more than two level of nesting ...find() may not work ,instead try aggregation:-
> db.categories.aggregate([{$unwind:"$posts"},{$unwind:"$posts.comments"},
{$match:{"posts.comments.author":ObjectId("57cfd09401ca2dd672cfebab")}},
{$project:{_id:0,"posts.comments":1}}]).pretty()
Output:
{
"posts" : {
"comments" : {
"author" : ObjectId("57cfd09401ca2dd672cfebab"),
"content" : "First comment",
"rating" : 2
}
}
}

MongoDB: query nested array by more than one condition

Say this is one item from my users collection:
{
"_id" : "5545f4c4d0dd52c355a99fbe",
"name" : "Dollie James",
"device" : "iOS",
"gender" : "Female",
"wallet" : [
{
"store" : "All Saints",
"balance" : "$196.11",
"date" : "2014-02-22T22:09:38 -10:00",
"tags" : [
"Tshirt",
"Summer"
]
},
{
"store" : "Nike",
"balance" : "$367.76",
"date" : "2014-04-18T14:44:30 -10:00",
"tags" : [
"Shoes"
]
}
]
}
This record was returned from the following query:
db.users.findOne({$and:[{"wallet.tags" : "Tshirt"}, {"wallet.store":"Nike"}]})
However this is not the result I want because Tshirt and Nike are not in the same object in the wallet array. It seems that mongo is doing a query on the entire array. How can I target this query to only return my two conditions that are in the same object within the array?
You don't need $and (as it will apply conditions independently) but $elemMatch. From the doc:
The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.
In your specific case:
> db.wallet.find(
{wallet: { $elemMatch:{"tags" : "Tshirt", "store":"Nike"}}}
).pretty()
{
"_id" : "5545f4c4d0dd52c355a99f00",
"name" : "Working example",
"device" : "iOS",
"gender" : "Female",
"wallet" : [
{
"store" : "Nike",
"balance" : "$196.11",
"date" : "2014-02-22T22:09:38 -10:00",
"tags" : [
"Tshirt",
"Summer"
]
}
]
}

Conditional $inc in MongoDB query

I have a survey system with documents like this:
{
"_id" : ObjectId("555b0b33ed26911e080102c4"),
"question" : "survey",
"subtitle" : "",
"answers" : [
{
"title" : "option 1",
"color" : "#FFEC00",
"code" : "opt1",
"_id" : ObjectId("555b0b33ed26911e080102ce"),
"votes" : 0,
"visible" : true
},
{
"title" : "option 2",
"color" : "#0bb2ff",
"code" : "opt2",
"_id" : ObjectId("555b0b33ed26911e080102cd"),
"votes" : 0,
"visible" : true
}
]
}
Now, I'm working on submit vote, so I need to increase 'votes' field for an specific survey (depending on option selected by user).
My problem is: I can have multiple documents like that, so how can I $inc field votes inside this array for an specific document? I tried this query (based on this website), but it didn't work:
db.bigsurveys.update(
{_id: ObjectId('555b0b33ed26911e080102c4'), 'answers.$.code' : 'opt1'},
{ $inc : { 'answers.$.votes' : 1 } }
)
The main problem here is that I can have multiple documents like this. Thanks in advance!
Use $elemMatch and postional operator $ to update query as :
db.bigsurveys.update({
"_id": ObjectId("555b0b33ed26911e080102c4"),
"answers": {
"$elemMatch": {
"code": "opt1"
}
}
}, {
"$inc": {
"answers.$.votes": 1
}
})