MongoDB show not all elements in subdocument - mongodb

I have the document of the following structure:
{
"_id" : ObjectId("50b8f881065f90c025000014"),
"education" : {
"schoolCountry" : 4,
"schoolTown" : -1,
"uniCountry" : 4,
"uniTown" : -1
},
"info" : {
"ava" : "auto.jpg",
"birthday" : ISODate("1942-04-01T21:00:00Z"),
"email" : "mail#gmail.com",
"name" : "name",
"sex" : 1,
"surname" : "surname"
}
}
I am trying to output only surname and name
The only thing I was able to achieve is this:
db.COLL.find({ }, {
"_id" : 0,
"education" : 0,
"info" : 1
})
My idea to show only elements that I need from subdocument failed:
db.COLL.find({ }, {
"_id" : 0,
"education" : 0,
"info.surname" : 1,
"info.name" : 1,
})
But hidding (info.email : 0) works. Is it possible to achieve my goal without hidding all unneeded fields?

You can't mix including and excluding fields, aside from turning off _id (which is included by default).
So just request the info.surname and info.name fields:
db.coll.find({ }, {
"_id" : 0,
"info.surname" : 1,
"info.name" : 1,
})
Sample output:
{ "info" : { "name" : "name", "surname" : "surname" } }

Related

MongoDB find fields which are substring of a query text

I have been looking for a way to do that but couldn't find any.
I'd like to know if is possible to, from a given query, return all fields that are contained in that query.
For example my dataset is as follows:
{ "_id" : ObjectId("5d5c2b4cc1f74ace3a48a072"), "id" : 0, "term" : "shorts" }
{ "_id" : ObjectId("5d5c2b4cc1f74ace3a48a072"), "id" : 0, "term" : "jacket" }
{ "_id" : ObjectId("5d5c2b4cc1f74ace3a48a072"), "id" : 1, "term" : "yellow jacket" }
{ "_id" : ObjectId("5d5c2b56c1f74ace3a48a073"), "id" : 2, "term" : "blue jacket" }
{ "_id" : ObjectId("5d5c2b65c1f74ace3a48a074"), "id" : 3, "term" : "blue shorts" }
{ "_id" : ObjectId("5d5c2b71c1f74ace3a48a075"), "id" : 4, "term" : "red shorts" }
And now, given a text like: "I really love blue shorts", the return should be only:
{ "_id" : ObjectId("5d5c2b71c1f74ace3a48a075"), "id" : 3, "term" : "blue shorts" }
{ "_id" : ObjectId("5d5c2b4cc1f74ace3a48a072"), "id" : 0, "term" : "shorts" }
It's something like query.contains(field)
Using $where is generally discouraged in mongodb because of
javascript execution in the query system and can be slow.
You can try this out if the dataset is not very large. Its like doing reverse regex for the field value contained in the query.
db.collection.find({$where: "\""I really love blue shorts\".match(this.term)"});
Which outputs:
{ "_id" : ObjectId("5d5c32c1236f19364a8aad4d"), "id" : 0, "term" : "shorts"}
{ "_id" : ObjectId("5d5c32c1236f19364a8aad51"), "id" : 3, "term" : "blue shorts"}
NOTE: This takes the assumption that term is defined in the documents, else you can use a javascript function for the $where value to deal with edge cases such as not defined fields, etc.
{ $where: function() { return /* after edge cases dealt with*/ }
The following query can get us the expected output:
db.collection.aggregate([
{
$addFields:{
"searchString":"I really love blue shorts"
}
},
{
$match:{
$expr:{
$gt:[
{
$indexOfBytes:["$searchString","$term"]
},
-1
]
}
}
},
{
$project:{
"searchString":0
}
}
]).pretty()
Data set:
{
"_id" : ObjectId("5d5c2b4cc1f74ace3a48a070"),
"id" : 0,
"term" : "shorts"
}
{
"_id" : ObjectId("5d5c2b4cc1f74ace3a48a071"),
"id" : 0,
"term" : "jacket"
}
{
"_id" : ObjectId("5d5c2b4cc1f74ace3a48a072"),
"id" : 1,
"term" : "yellow jacket"
}
{
"_id" : ObjectId("5d5c2b56c1f74ace3a48a073"),
"id" : 2,
"term" : "blue jacket"
}
{
"_id" : ObjectId("5d5c2b65c1f74ace3a48a074"),
"id" : 3,
"term" : "blue shorts"
}
{
"_id" : ObjectId("5d5c2b71c1f74ace3a48a075"),
"id" : 4,
"term" : "red shorts"
}
Output:
{
"_id" : ObjectId("5d5c2b4cc1f74ace3a48a070"),
"id" : 0,
"term" : "shorts"
}
{
"_id" : ObjectId("5d5c2b65c1f74ace3a48a074"),
"id" : 3,
"term" : "blue shorts"
}

Update multiple subdocument without exact sub document level condition

I have collection with the following document:
{
"_id" : ObjectId("56cbf9cd75de3ee9057b23c7"),
"title" : "Abc",
"comments" : [
{
"_id" : "",
"message" : "",
"active" : 1,
"status" : 1,
},
{
"_id" : "",
"message" : "",
"active" : 0,
"status" : 1,
},
{
"_id" : "",
"message" : "",
"active" : 1,
"status" : 1,
}
],
}
{
"_id" : ObjectId("553744ae75de3eb9128b4568"),
"title" : "Jhon",
"comments" : [
{
"_id" : "",
"message" : "",
"active" : 1,
"status" : 1,
}
]
}
I need to update all status of comments as 0 where comments.active=1. I tried to solve it as below, but only first record of the comments is updated. Please help me..
db.comments.update({'comments.active':1},{$set:{'comments.$.status':0}})
Per this issue New operator to update all matching items in an array, currently there is no operation to do that in mongodb. Feel so sad, this issue lasts for 6 years.
There could be one work around in mongo shell as below.
> db.comments
.find({})
.forEach(function(doc) {
doc.comments.map(function(c) {
if (c.active == 1) {
c.status = 0;
}
});
db.comments.update(
{_id: doc._id},
{$set: {comments: doc.comments}});
});

What index to be added in MongoDB to support $elemMatch query on embedded document

Suppose we have a following document
{
embedded:[
{
email:"abc#abc.com",
active:true
},
{
email:"def#abc.com",
active:false
}]
}
What indexing should be used to support $elemMatch query on email and active field of embedded doc.
Update on question :-
db.foo.aggregate([{"$match":{"embedded":{"$elemMatch":{"email":"abc#abc.com","active":true}}}},{"$group":{_id:null,"total":{"$sum":1}}}],{explain:true});
on querying this i am getting following output of explain on aggregate :-
{
"stages" : [
{
"$cursor" : {
"query" : {
"embedded" : {
"$elemMatch" : {
"email" : "abc#abc.com",
"active" : true
}
}
},
"fields" : {
"_id" : 0,
"$noFieldsNeeded" : 1
},
"planError" : "InternalError No plan available to provide stats"
}
},
{
"$group" : {
"_id" : {
"$const" : null
},
"total" : {
"$sum" : {
"$const" : 1
}
}
}
}
],
"ok" : 1
}
I think mongodb internally not using index for this query.
Thanx in advance :)
Update on output of db.foo.stats()
db.foo.stats()
{
"ns" : "test.foo",
"count" : 2,
"size" : 480,
"avgObjSize" : 240,
"storageSize" : 8192,
"numExtents" : 1,
"nindexes" : 3,
"lastExtentSize" : 8192,
"paddingFactor" : 1,
"systemFlags" : 0,
"userFlags" : 1,
"totalIndexSize" : 24528,
"indexSizes" : {
"_id_" : 8176,
"embedded.email_1_embedded.active_1" : 8176,
"name_1" : 8176
},
"ok" : 1
}
db.foo.getIndexes();
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.foo"
},
{
"v" : 1,
"key" : {
"embedded.email" : 1,
"embedded.active" : 1
},
"name" : "embedded.email_1_embedded.active_1",
"ns" : "test.foo"
},
{
"v" : 1,
"key" : {
"name" : 1
},
"name" : "name_1",
"ns" : "test.foo"
}
]
Should you decide to stick to that data model and your queries, here's how to create indexes that match the query:
You can simply index "embedded.email", or use a compound key of embedded indexes, i.e. something like
> db.foo.ensureIndex({"embedded.email" : 1 });
- or -
> db.foo.ensureIndex({"embedded.email" : 1, "embedded.active" : 1});
Indexing boolean fields is often not too useful, since their selectivity is low.

Aggregate inside document in mongodb

I have document sales
db.sale.findOne({_id : ObjectId("52ea4dd29dbc7923107ddb97")})
{
"_id" : ObjectId("52ea4dd29dbc7923107ddb97"),
"firm" : ObjectId("52e56c009dbc794999ea5c3d"),
"patient" : {
"last" : "",
"first" : ""
},
"doc" : "",
"hospital" : "",
"discount" : 0,
"dd" : "",
"mode" : "cash",
"invoice" : "300114-undefined-1",
"items" : [
{
"bat" : "BJFE",
"narco" : 0,
"name" : "GDRNCCD",
"mrp" : 1,
"free" : 0,
"qty" : 1,
"item_discount" : 0,
"wpr" : 1,
"exp" : "1425168000000"
},
{
"bat" : "",
"narco" : 0,
"name" : "GDRN vbhjdsfb",
"mrp" : 1,
"free" : 0,
"qty" : 1,
"item_discount" : 0,
"wpr" : 0,
"exp" : "[object Object]"
},
{
"bat" : "",
"narco" : 0,
"name" : "GDRN vbhjdsfb",
"mrp" : 1,
"free" : 0,
"qty" : 1,
"item_discount" : 0,
"wpr" : 0,
"exp" : "[object Object]"
}
],
"date" : ISODate("2014-01-30T00:00:00Z"),
"mob" : "",
"email" : ""
}
How can I Aggregate total numbers if items in one field and sum up mrp *qty of all the items into one field.
I have read the mognodb aggregation but it only aggregates among group of matched documents not inside a single document. Is it possile?
{
"_id" : ObjectId("52ea4dd29dbc7923107ddb97"),
"firm" : ObjectId("52e56c009dbc794999ea5c3d"),
'total_items' : items.length,
"total" : mrp*qty of all items,
}
db.sales.aggregate(
{$unwind: "$items"},
{$project: {_id: 1,firm:1, total: {$multiply: ["$items.mrp", "$items.qty"]}}},
{$group: {_id: {"id":"$_id", firm:"$firm"}, count: {$sum:1} , total : {$sum:"$total"}}}
)
With a slight change : _id contains id and firm, you will need an extra projection to match your desired document, but I don't think it's important.
Plus, you can easily change to group by farm only
Thanks to Orid,I tried this,
db.sale.aggregate(
{ $match :{ firm :ObjectId("52e56c009dbc794999ea5c3d") } },
{$project : {day : {$dayOfMonth : '$date'},items :1,patient :1,invoice :1}},
{$match : {day: {$gte : new Date().getDate()}}},
{$unwind : "$items"},
{$project: {_id: 1,patient:1,invoice :1, total: {$multiply: ["$items.mrp", "$items.qty"]}}},
{$group: {_id: {"id":"$_id", invoice:"$invoice",patient :"$patient"}, count: {$sum:1} , total : {$sum:"$total"}}})
Result
{
"result" : [
{
"_id" : {
"id" : ObjectId("52eab6129dbc7923107ddbaf"),
"invoice" : "310114-undefined-1",
"patient" : {
"last" : "",
"first" : ""
}
},
"count" : 2,
"total" : 25
},
{
"_id" : {
"id" : ObjectId("52eab6129dbc7923107ddbb0"),
"invoice" : "310114-undefined-1",
"patient" : {
"last" : "",
"first" : ""
}
},
"count" : 1,
"total" : 1
},
{
"_id" : {
"id" : ObjectId("52eab6129dbc7923107ddbae"),
"invoice" : "310114-undefined-1",
"patient" : {
"last" : "",
"first" : ""
}
},
"count" : 2,
"total" : 5
}
],
"ok" : 1
}

mongoimport doesn't import the object properly

In Mongodb v2.2 When I try to import one simple json document file like this from my .json file into an empty collection I get 13 objects imported. Here is what I'm doing.
this is the data (I've shortened the field names to protect data):
[
{
"date" : ISODate("2012-08-01T00:00:00Z"),
"start" : ISODate("2012-08-01T00:00:00Z"),
"xxx" : 1,
"yyt" : 5,
"p" : 6,
"aam" : 20,
"dame" : "denon",
"33" : 10,
"xxt" : 8,
"col" : 3,
"rr" : [
{ "name" : "Plugin 1", "count" : 1 },
{ "name" : "Plugin 2", "count" : 1 },
{ "name" : "Plugin 3", "count" : 1 }
],
"xkx" : { "y" : 0, "n" : 1 },
"r" : { "y" : 0, "n" : 1 },
"po" : { "y" : 0, "n" : 1 },
"pge" : { "posts" : 0, "pages" : 1 },
"pol" : { "y" : 0, "n" : 1 },
"lic" : { "y" : 0, "n" : 1 },
"count" : 30,
"tx" : [
{ "zone" : -7, "count" : 1 }
],
"yp" : "daily",
"ons" : [
{ "version" : "9.6.8", "count" : 1 }
],
"ions" : [
{ "version" : "10.0.3", "count" : 1 }
]
}
]
with this command:
mongoimport --db development_report --collection xxx --username xxx --password xxx --file /Users/Alex/Desktop/daily2.json --type json --jsonArray --stopOnError --journal
I get this weired response:
Mon Sep 3 12:09:12 imported 13 objects
and this 13 new documents end up in the collection instead of one:
{ "_id" : ObjectId("5044114815e24c08bcdc988e") }
{ "_id" : ObjectId("5044114815e24c08bcdc988f"), "name" : "Plugin 1", "count" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9890"), "name" : "Plugin 2", "count" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9891"), "name" : "Plugin 3", "count" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9892"), "y" : 0, "n" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9893"), "y" : 0, "n" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9894"), "y" : 0, "n" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9895"), "posts" : 0, "pages" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9896"), "y" : 0, "n" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9897"), "y" : 0, "n" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9898"), "zone" : -7, "count" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc9899"), "version" : "9.6.8", "count" : 1 }
{ "_id" : ObjectId("5044114815e24c08bcdc989a"), "version" : "10.0.3", "count" : 1 }
What am I doing wrong?
The problem you are having is with the two ISODate fields you have at the start of your document.
JSON does not have any "date" type, so it does not handle ISODate fields in your document. You would need to convert these like so:
[
{
"date" : { "$date" : 1343779200000 },
"start" : { "$date" : 1343779200000 },
...
And your import will work.
The reason this comes about is because MongoDB handles more types than are available in the JSON spec. You can see more information in the documentation. There is also an open ticket to make MongoImport handle all the formats MongoDB does here and more details here
This is really frustrating; I couldn't get anywhere fast with the import tool so I used the load() function within the mongo client to load a script which inserted my records.
> load('/Users/Alex/Desktop/daily.json');
I obviously had to modify the json file to include insert commands like so:
>db.mycollection.insert(
{ DOCUMENT 1 },
...
{ DOCUMENT N }
);
This is really late, but in case it can help anyone else - you should not be passing a JSON array. Simply list 1 JSON document per line, and each line will create a separate document. The file below would insert 2 documents:
{ "date" : { "$date": 1354320000000 }, "xxx" : 1, "yyt" : 5, ... }
{ "date" : { "$date": 1354320000000 }, "xxx" : 2, "yyt" : 6, ... }