I'm working on a migration from mongodb to mysql. I have a sub document in one collection, I need to find the data using sub document.
The collection structures looks like this
{
"_id" : ObjectId("578506a90420bec15ea33783"),
"reselected" : "ABCDEFGH",
"reskip" : [],
"restatus" : "active",
"reuser" : {
"activeListings" : [
{
"subcategory" : "mobiles",
"title" : " Mobile 1",
"transactiontype" : "post"
},
{
"category" : "mobiles",
"title" : " Mobile 2",
"transactiontype" : "post"
} ],
"reuserInput" : "2",
"reussdsession" : "303757117" }
I have tried with mapreduce code
mr = db.runCommand({
"mapreduce" : "collection name",
"map" : function() { */map/
for (var key in "this.reuser.activeListings") {
for (var key1 in "this.reuser.activeListings"[key]) {
emit(key1, null);
}
}
},
"reduce" : function(key, stuff) { return null; }, /*reducer/
"out": "my_collection" + "_keys"
})
I need output for activeListings
{subcategory,title,transactiontype,category}
You essentially need to iterate the array and then emit each key. The following mapReduce script will give you the desired key list:
mr = db.runCommand({
"mapreduce": "collectionName",
"map": function() { /* mapper */
this.reuser.activeListings.forEach(function (obj){
for (var key in obj) { emit(key, null); }
});
},
"reduce": function() {}, /* reducer */
"out": "my_collection" + "_keys"
})
To get a list of all the dynamic keys, run distinct on the resulting collection:
> db[mr.result].distinct("_id")
["subcategory", "title", "transactiontype", "category"]
Related
I have a collection where documents can have an unknown number of sub documents:
"agent_id": {
"0":"1234",
"1":"2234",...etc
How do I search for an exact match in all the agent_id sub-fields?
You need to dynamically create an object with properties that are a concatenation of the embedded document name agent_id with the dot (.) and the field name, enclosed in quotes, something like this:
var query = {
"agent_id.0": "78343",
"agent_id.1": "78343",
"agent_id.2": "78343",
"agent_id.3": "78343",
...
"agent_id.n": "78343"
}
One way to create the object is generate the sub-documents keys with mapReduce. The following demonstrates this approach. In the Map-Reduce operation, an array of keys in the agent_id subdocument is generated to an output collection "collection_keys" and then used to produce the find() query expression:
Suppose you populate a sample collection
db.collection.insert([
{
"agent_id": {
"0":"1234",
"1":"2234",
"56":"8451",
"74":"1475",
"10":"1234"
}
},
{
"agent_id": {
"5":"5874",
"18":"2351"
}
}
])
Running the following mapReduce operation
var mr = db.runCommand({
"mapreduce" : "collection",
"map" : function() {
for (var key in this.agent_id) { emit(key, null); }
},
"reduce" : function(key, stuff) {
return null
},
"out": "collection" + "_keys"
});
var query = { "$or": [] },
value = "1234";
db[mr.result].distinct("_id").forEach(function (key){
var obj = {};
obj["agent_id." + key] = value;
query["$or"].push(obj)
});
printjson(query);
will produce:
{
"$or" : [
{
"agent_id.0" : "1234"
},
{
"agent_id.1" : "1234"
},
{
"agent_id.10" : "1234"
},
{
"agent_id.18" : "1234"
},
{
"agent_id.5" : "1234"
},
{
"agent_id.56" : "1234"
},
{
"agent_id.74" : "1234"
}
]
})
You can then use the query document in your find() query:
db.collection.find(query)
which will produce the result:
/* 0 */
{
"_id" : ObjectId("561d5312cd05efc95a1ea1f4"),
"agent_id" : {
"0" : "1234",
"1" : "2234",
"56" : "8451",
"74" : "1475",
"10" : "1234"
}
}
I saw few solutions but those are not exact my solution. I have a DB with name results and collection name is marks like below:
db.marks.find();
{ "_id" : ObjectId("54f57522627af4bfdcf79764"), "name" : "John", "scroe1" : 23, "score2" : 21, "score5" : 12 }
{ "_id" : ObjectId("54f5761a627af4bfdcf79765"), "name" : "Mike", "scroe2" : 22, "score3" : 20, "score4" : 22 }
{ "_id" : ObjectId("559d0bc521cb2e056507c3e3"), "name" : "Bush", "score2" : 30 }
I tried with
var doc=db.marks.findOne(); for (var key in doc) print(key);
and i got
_id
name
score1
score2
score5
But i Want all keys in collection like below:
_id, name, score1, score2, score3, score4, score5
name
scroe1
score2
score3here
findOne will only return the first found document. Since the first document you list does not have the score3 and score4 keys, it will not display them. If you want to show all root-level keys across all documents, you would need to iterate through all the documents in the db.
var keys = [];
db.marks.find().forEach(function(doc){
for (var key in doc){
if(keys.indexOf(key) < 0){
keys.push(key);
}
}
});
print(keys);
mr = db.runCommand({
"mapreduce" : "my_collection",
"map" : function() {
for (var key in this) { emit(key, null); }
},
"reduce" : function(key, stuff) { return null; },
"out": "my_collection" + "_keys"
})
Then run distinct on the resulting collection so as to find all the keys:
db[mr.result].distinct("_id")
["foo", "bar", "baz", "_id", ...]
Mongodb find() command has two arguments first one is query and second is projections.
Something like db.collection.find(query,projection).
if the document is db.myCol.find();, then it returns:
{
{
_id:1
name: ''hello',
age: 23
}, {
_id:2
name: ''bollo',
age: 27
}
}
And db.myCol.find({},{_id:1}); returns:
1
2
I have a collection with multiple documents which follow this structure:
{
"_id" : {
"oid" : XXX
},
"name" : "Name",
"videos" [
{
"id" : 1,
"thumbnail" : "thumbnail.jpg",
"name" : "Name here"
},
{
"id" : 2,
"thumbnail" : "thumbnail.jpg",
"name" : "Name here"
},
{
"id" : 3,
"thumbnail" : "thumbnail.jpg",
"name" : "Name here"
}
]
}
I want to find and update the a thumbnail of a video, of which I only know the id, but not which document it is in.
This is what I've tried so far, but it's not working properly. All the examples I found relied on knowing the document id, and the array position of the object to update. I also found that doing a query like this found the document okay, but set the whole document as the new thumbnail!
db.collection(COLLECTION-NAME, function(err, collection){
collection.update(
{ 'videos.id' : 2 },
{ $set: { thumbnail: "newThumbnail.jpg" } },
function(err, result){
if (!err){
console.log('saved', result)
} else {
console.log('error', err);
}
}
);
});
Use the $ positional operator to update the value of the thumbnail field within the embedded document having the id of 2:
db.collection.update(
{ "videos.id": 2 },
{ "$set": { "videos.$.thumbnail" : "newThumbnail.jpg" } }
)
We have a basic enquiry management tool that we're using to track some website enquiries in our administration suite, and we're using the ObjectId of each document in our enquiries collection to sort the enquiries by the date they were added.
{
"_id" : ObjectId("53a007db144ff47be1000003"),
"comments" : "This is a test enquiry. Please ignore. We'll delete it shortly.",
"customer" : {
"name" : "Test Enquiry",
"email" : "test#test.com",
"telephone" : "07890123456",
"mobile" : "07890123456",
"quote" : false,
"valuation" : false
},
"site" : [],
"test" : true,
"updates" : [
{
"_id" : ObjectId("53a007db144ff47be1000001"),
"status" : "New",
"status_id" : ObjectId("537de7c3a5e6e668ffc2335c"),
"status_index" : 100,
"substatus" : "New Web Enquiry",
"substatus_id" : ObjectId("5396bb9fa5e6e668ffc23388"),
"notes" : "New enquiry received from website.",
},
{
"_id" : ObjectId("53a80c977d299cfe91bacf81"),
"status" : "New",
"status_id" : ObjectId("537de7c3a5e6e668ffc2335c"),
"status_index" : 100,
"substatus" : "Attempted Contact",
"substatus_id" : ObjectId("53a80e06a5e6e668ffc2339e"),
"notes" : "In this test, we pretend that we've not managed to get hold of the customer on the first attempt.",
},
{
"_id" : ObjectId("53a80e539b966b8da5c40c36"),
"status" : "Approved",
"status_id" : ObjectId("52e77a49d85e95f00ebf6c72"),
"status_index" : 200,
"substatus" : "Enquiry Confirmed",
"substatus_id" : ObjectId("53901f1ba5e6e668ffc23372"),
"notes" : "In this test, we pretend that we've got hold of the customer after failing to contact them on the first attempt.",
}
]
}
Within each enquiry is an updates array of objects which also have an ObjectId as their main identity field. We're using an $unwind and $group aggregation to pull the first and latest updates, as well as the count of updates, making sure we only take enquiries where there have been more than one update (as one is automatically inserted when the enquiry is made):
db.enquiries.aggregate([
{
$match: {
"test": true
}
},
{
$unwind: "$updates"
},
{
$group: {
"_id": "$_id",
"latest_update_id": {
$last: "$updates._id"
},
"first_update_id": {
$first: "$updates._id"
},
"update_count": {
$sum: 1
}
}
},
{
$match: {
"update_count": {
$gt: 1
}
}
}
])
This results in the following output:
{
"result" : [
{
"_id" : ObjectId("53a295ad122ea80200000005"),
"latest_update_id" : ObjectId("53a80bdc7d299cfe91bacf7e"),
"first_update_id" : ObjectId("53a295ad122ea80200000003"),
"update_count" : 2
},
{
"_id" : ObjectId("53a007db144ff47be1000003"),
"latest_update_id" : ObjectId("53a80e539b966b8da5c40c36"),
"first_update_id" : ObjectId("53a007db144ff47be1000001"),
"update_count" : 3
}
],
"ok" : 1
}
This is then passed through to our code (node.js, in this case) where we perform a few operations on it and then present some information on our dashboard.
Ideally, I'd like to add another $group pipeline aggregation to the query which would subtract the timestamp of first_update_id from the timestamp of latest_update_id to give us a timespan, which we could then use $avg on.
Can anyone tell me if this is possible? (Thank you!)
As Neil already pointed out, you can't get to the timestamp from the ObjectId in the aggregation framework.
You said that speed is not important, so using MapReduce you can get what you want:
var map = function() {
if (this.updates.length > 1) {
var first = this.updates[0];
var last = this.updates[this.updates.length - 1];
var diff = last._id.getTimestamp() - first._id.getTimestamp();
var val = {
latest_update_id : last._id,
first_update_id : first._id,
update_count : this.updates.length,
diff: diff
}
emit(this._id, val);
}
};
var reduce = function() { };
db.runCommand(
{
mapReduce: "enquiries",
map: map,
reduce: reduce,
out: "mrresults",
query: { test : true}
}
);
This are the results:
{
"_id" : ObjectId("53a007db144ff47be1000003"),
"value" : {
"latest_update_id" : ObjectId("53a80e539b966b8da5c40c36"),
"first_update_id" : ObjectId("53a007db144ff47be1000001"),
"update_count" : 3,
"diff" : 525944000
}
}
Edit:
If you want to get the average diff for all documents you can do it like this:
var map = function() {
if (this.updates.length > 1) {
var first = this.updates[0];
var last = this.updates[this.updates.length - 1];
var diff = last._id.getTimestamp() - first._id.getTimestamp();
emit("1", {diff : diff});
}
};
var reduce = function(key, values) {
var reducedVal = { count: 0, sum: 0 };
for (var idx = 0; idx < values.length; idx++) {
reducedVal.count += 1;
reducedVal.sum += values[idx].diff;
}
return reducedVal;
};
var finalize = function (key, reducedVal) {
reducedVal.avg = reducedVal.sum/reducedVal.count;
return reducedVal;
};
db.runCommand(
{
mapReduce: "y",
map: map,
reduce: reduce,
finalize : finalize,
out: "mrtest",
query: { test : true}
}
);
And the example output:
> db.mrtest.find().pretty()
{
"_id" : "1",
"value" : {
"count" : 2,
"sum" : 1051888000,
"avg" : 525944000
}
}
Given this structure for a school object:
{
"grade_spans" :
{
"0": {
"grade_span_key" : "K_5",
"name": "Elementary School"
},
"1": {
"grade_span_key" : "6_8",
"name": "Junior High-School"
}
}
}
How do I find a school for a given grade_span_key?
db.schools.find({ "grade_span_key": "K_5" })
returns empty.
Update: Sorry, I copied the structure incorrectly. It's actually an Embedded Object not a collection.
Update #2: There was a doctrine2 annotation I was using incorrectly: #MongoDB\EmbedMany(strategy="set"). I change the strategy to pushAll (which is the default)
If this field is just embedded into the main document #sergios answer will work just fine and it is not clear why his query wouldn't work as you don't provide an example of the document structure only of the embedded structure.
Also as #JohnnyHK says, rebuild that object as an array since dynamic keys in this case would be harder.
If you are looking to pick out matching rows from the embedded document and not the full document. This is a little harder but is possible:
db.schools.aggregate({
{$unwind: "$grade_spans"},
{$match: {"grade_spans.grade_span_key": "K_5"}},
{$group: {_id: "$_id", grade_spans: {$push: "$grade_spans"}}}
})
Something like the above should return a document of the structure:
{
_id: {},
grade_spans:[{
"grade_span_key" : "K_5",
"name" : "Elementary School"
}]
}
You should use full path to the property, in dotted notation.
> db.schools.find({"grade_spans.grade_span_key": "K_5"})
{
"_id" : ObjectId("50801cc5ab582e310adc0e41"),
"grade_spans" : [
{
"grade_span_key" : "K_5",
"name" : "Elementary School"
},
{
"grade_span_key" : "6_8",
"name" : "Junior High-School"
}
]
}
Given this structure :
{
"grade_spans" : {
"0": { "grade_span_key" : "K_5",
"name": "Elementary School" },
"1": { "grade_span_key" : "6_8",
"name": "Junior High-School" }
}
}
You can try with map/reduce function :
var mapFunction = function() {
for (var i in this.grade_spans) {
// Add the name of the school in a list
var schools = [];
schools[0] = this.grade_spans[i].name;
// Create out object : { schools : ["Elementary School"] } or { schools : ["Junior High-School"] }
var out = {};
out.schools = schools;
// Create key (K_5 or 6_8)
var key = this.grade_spans[i].grade_span_key;
emit(key, out);
}
};
var reduceFunction = function(key, values) {
var schools = [];
for (var i = 0; i < values.length; i++) {
schools.push.apply(schools, values[i].schools);
}
return {schools:schools};
}
db.schools.mapReduce(
mapFunction,
reduceFunction,
{ out: "map_reduce_grade_spans", sort: {_id:1} }
)
And then :
db.map_reduce_grade_spans.find({_id:"K_5"});