And not working in mongodb - mongodb

i have a collection named student like
{
"_id" : ObjectId("5693b549c4fd0e0bf4782d73"),
"nameIdentity" : [
{
"name" : "a"
},
{
"name" : "b"
}
]
},
{
"_id" : ObjectId("5693b549c4fd0e0bf4782d74"),
"nameIdentity" : [
{
"name" : "a"
}
]
},
{
"_id" : ObjectId("5693b549c4fd0e0bf4782d75"),
"nameIdentity" : [
{
"name" : "a"
},
{
"name" : "b"
}
]
},
{
"_id" : ObjectId("5693b549c4fd0e0bf4782d76"),
"nameIdentity" : [
{
"name" : "b"
}
]
}
i am trying to query using 'and' but its giving me some different output.
When i query for all the nameIdentities which has only name "a" db.student.find({"nameIdentity.name":"a"}) i am expecting the output as 1 where as i am getting 3. Again when i query for names with both a and b db.student.find({"nameIdentity.name":"a"},{"nameIdentity.sourceReferenceId.sourceName":"b"}) i am expecting output as 2 but i am getting 4. Can any one suggest me where am i going wrong?

use this.
db.student.find({"nameIdentity.name":"a","nameIdentity": { $size: 1 }})

in your query
db.student.find({"nameIdentity.name":"a"}) you are searching for data which nameIdentity is "a" not only a. and in your second query use like
db.student.find({"nameIdentity.name":"a","nameIdentity.name":"b"})

Related

Search array for dates within a range

I am new to MongoDB, keep that in mind as I ask my questions please. I have the following collection named 'documents' where I have an array named attributes which contains key:value pairs of different types.
{
"_id" : ObjectId("5d376c67f6c305c7571f7dd7"),
"name" : "testContract.pdf",
"fileType" : "pdf",
"attributes" : [
{
"abc" : 1
},
{
"def" : ISODate("2012-12-01T08:00:00Z")
},
{
"ghi" : "test"
}
]
}
{
"_id" : ObjectId("5d376ca4f6c305c7571f7dd8"),
"name" : "1099.pdf",
"fileType" : "pdf",
"attributes" : [
{
"def" : ISODate("2012-06-03T07:00:00Z")
},
{
"ghi" : "taxes"
}
]
}
What I would like to do is return one or more documents from this collection that fit within a date range. For example I can return all documents that have a fileType of 'pdf' with the following query -->
db.documents.find({"fileType":"pdf"});
But what I am trying to figure out is can I search an array containing different data types like dates in a range successfully while also being able to search for strings. I can also search the attributes array with the following -->
db.documents.find({"attributes":{"ghi":"test"}});
Here is an example of what I am trying to get but it is not working...
db.documents.find({'attributes':$match:{[{'def':{$gte:new Date('2011-11-30')}}]}});
Is something like this what you are looking for?
Based on the following documents:
{
"_id" : ObjectId("5d38aad64850fbd5d13f14bd"),
"name" : "testxx.pdf",
"attributes" : [
{
"abc" : 1
},
{
"def" : ISODate("2012-12-01T08:00:00Z")
},
{
"ghi" : "test"
}
]
}
{
"_id" : ObjectId("5d38b0eae4adbe945b6cbb89"),
"name" : "testyy.pdf",
"attributes" : [
{
"abc" : 2
},
{
"def" : ISODate("2013-12-01T08:00:00Z")
},
{
"ghi" : "test1"
}
]
}
{
"_id" : ObjectId("5d38b12f21e647b8d384d841"),
"name" : "testzz.pdf",
"attributes" : [
{
"abc" : 3
},
{
"def" : ISODate("2012-05-01T08:00:00Z")
},
{
"ghi" : "test"
}
]
}
Query def > 2010/11/30 - returns all 3 docs above
db.chkdates.find({'attributes.def':{$gte:new Date(2010,11,30)}}).pretty()
Adding another key/value pair and range looks like:
db.chkdates.find({'attributes.def':{$gte:new Date(2011,12,12),
$lte:new Date(2012,10,12)},
'attributes.ghi':'test'}).pretty()
Returns only 1 document:
{
"_id" : ObjectId("5d38b12f21e647b8d384d841"),
"name" : "testzz.pdf",
"attributes" : [
{
"abc" : 3
},
{
"def" : ISODate("2012-05-01T08:00:00Z")
},
{
"ghi" : "test"
}
]
}
You can use the $gte and $lte for querying within a date range. It would look something like this:
{'def': { $gte: qryDateFrom, $lte: qryDateTo }}
Depending on if you're using aggregate pipleline or a regular mongoose query, you'd just apply this accordingly.
For instance, using $match in an aggregate with a string match included, it would look like this:
$match: {
$and: [
{'id': { $ne: req.user._id }},
{'def': { $gte: qryDateFrom, $lte: qryDateTo }}
]
}

Update a object field in an array

In mondodb I want to update a field of an object within an array. The example database looks like this:
{
"_id" : ObjectId("5ad237559d30d918c89c7f46"),
"myArray" : [
{
"name" : "a",
"name2" : "a",
"value" : 900000 //<--- instead of this...
},
{
"name" : "b",
"name2" : "b",
"value" : 0
}
]
},
{
"_id" : ObjectId("5ad238049d30d918c89c7f47"),
"myArray" : [
{
"name" : "b",
"name2" : "b",
"value" : 0
},
{
"name" : "c",
"name2" : "a",
"value" : 0 //... I want to update this
}
]
}
I want to update the last value field by querying name:c AND name2:a. I tried it with the following instruction, but it sets the value of the first object (name:a name2:a). Does the problem lie near the $ char?
db.test.updateOne({$and:[{"myArray.name" : "c"}, {"myArray.name2" : "a"}]},
{$set:{"myArray.$.value" : 900000}})
You need to do an $elemMatch to match the specific item in the array and then you can use the positional operator:
db.test.updateOne(
{ "myArray": { $elemMatch: { "name": "c", "name2"; "a" } } },
{ $set: { "myArray.$.value": 900000 } }
);
You can use arrayFilters.
db.test.updateOne({}, {$set:{"myArray.$[element].value" : 900000}} {
multi: true,
arrayFilters: [ {$and:[{"element.name" : "c"}, {"element.name2" : "a"}]} ]
},
)
Sorry, I have no mongodb right there to test it, the query will probably need to be tuned a little

mongo shell: return only 1 element of nested array

I have the following data on a mongodb:
{
"name" : "bla",
"log" : [
{
"A" : 1,
"B" : 10
},
{
"A" : 2,
"B" : 20
}
]
}
I understand how to return all values of A from the mongoshell:
db.test.find({},{'name':1,'log.A':1})
{ "_id" : ObjectId("52712539c99a2fc6f6088cd4"), "name" : "bla", "log" : [ { "A" : 1 }, { "A" : 2 } ] }
but how can I limit the output of A to only the first element? This is the output that I extect to have:
{ "_id" : ObjectId("52712539c99a2fc6f6088cd4"), "name" : "bla", "log.A" : 1, "log.B":10}
I don't mind in having log.A or just A, or even having some [ ] in the output, as soon as it is always only one entry for A and for B
how can I do it?
You can use the $slice array projection operator to do that:
db.test.find({}, {name: 1, 'log.A':1, log: {$slice: 1}})
Outputs:
{ "_id" : ObjectId("..."), "name" : "bla", "log" : [ { "A" : 1 } ] }

MongoDB Update $push Cannot apply the positional operator without a corresponding query field containing an array

model:
{
"_id" : "a62107e10f388c90a3eb2d7634357c8b",
"_appid" : [
{
"_id" : "1815aaa7f581c838",
"events" : [
{
"_id" : "_TB_launch",
"boday" : [
{
"VERSIONSCODE" : "17",
"NETWORK" : "cmwap",
"VERSIONSNAME" : "2.4.0",
"IMSI" : "460026319223205",
"PACKAGENAME" : "com.androidbox.astjxmjmmshareMM",
"CHANNELID" : "xmjmm17",
"CHANNELNAME" : "浠..?.M寰.俊?.韩?.?1.x锛.,
"eventid" : "_TB_launch",
"uuid" : "a62107e10f388c90a3eb2d7634357c8b",
"creattime" : "1366300799766",
"ts" : ISODate("2013-04-25T06:28:36.403Z")
}
],
"size" : 1
}
],
"size" : 1
}
],
"size" : 1
}
> db.events.update(
{
"_id":"039e569770cec5ff3811e7410233ed27",
"_appid._id":"e880db04064b03bc534575c7f831a83a",
"_appid.events._id":"_TB_launch"
},
{
"$push":{
"_appid.$.events.$.boday":{"111":"123123"}
}
}
);
Cannot apply the positional operator without a corresponding query field containing an array.
Why?!!
You are trying to reference multiple levels of embedding - you can only have one positional $ operator. You won't be able to do something like this until this feature request has been implemented.
Response Here
The short answer is, "no", but working with nested arrays gets
tricky. Here is an example:
db.foo.save({_id: 1, a1:[{_a1id:1, a2:[{a2id:1, a3:[{a3id:1, a4:"data"}]}]}]})
db.foo.find()
{ "_id" : 1, "a1" : [
{ "_a1id" : 1, "a2" : [
{ "a2id" : 1, "a3" : [
{ "a3id" : 1, "a4" : "data" }
] }
] }
] }
db.foo.update({_id:1}, {$push:{"a1.0.a2.0.a3":{a3id:2, a4:"other data"}}})
db.foo.find()
{ "_id" : 1, "a1" : [
{ "_a1id" : 1, "a2" : [
{ "a2id" : 1, "a3" : [
{ "a3id" : 1, "a4" : "data" },
{ "a3id" : 2, "a4" : "other data" }
] }
] }
] }
If you are unsure where one of your sub-documents lies within an
array, you may use one positional operator, and Mongo will update the
first sub-document which matches. For example:
db.foo.update({_id:1, "a1.a2.a2id":1}, {$push:{"a1.0.a2.$.a3":{a3id:2, a4:"other data"}}})

Aggregate of different subtypes in document of a collection

abstract document in collection md given:
{
vals : [{
uid : string,
val : string|array
}]
}
the following, partially correct aggregation is given:
db.md.aggregate(
{ $unwind : "$vals" },
{ $match : { "vals.uid" : { $in : ["x", "y"] } } },
{
$group : {
_id : { uid : "$vals.uid" },
vals : { $addToSet : "$vals.val" }
}
}
);
that may lead to the following result:
"result" : [
{
"_id" : {
"uid" : "x"
},
"vals" : [
[
"24ad52bc-c414-4349-8f3a-24fd5520428e",
"e29dec2f-57d2-43dc-818a-1a6a9ec1cc64"
],
[
"5879b7a4-b564-433e-9a3e-49998dd60b67",
"24ad52bc-c414-4349-8f3a-24fd5520428e"
]
]
},
{
"_id" : {
"uid" : "y"
},
"vals" : [
"0da5fcaa-8d7e-428b-8a84-77c375acea2b",
"1721cc92-c4ee-4a19-9b2f-8247aa53cfe1",
"5ac71a9e-70bd-49d7-a596-d317b17e4491"
]
}
]
as x is the result aggregated on documents containing an array rather than a string, the vals in the result is an array of arrays. what i look for in this case is to have a flattened array (like the result for y).
for me it seems like that what i want to achieve by one aggegration call only, is currently not supported by any given operation as e.g. a type conversion cannot be done or unwind expectes in every case an array as input type.
is map reduce the only option i have? if not ... any hints?
thanks!
You can use the aggregation to do the computation you want without changing your schema (though you might consider changing your schema simply to make queries and aggregations of this field easier to write).
I broke up the pipeline into multiple steps for readability. I also simplified your document slightly, again for readability.
Sample input:
> db.md.find().pretty()
{
"_id" : ObjectId("512f65c6a31a92aae2a214a3"),
"uid" : "x",
"val" : "string"
}
{
"_id" : ObjectId("512f65c6a31a92aae2a214a4"),
"uid" : "x",
"val" : "string"
}
{
"_id" : ObjectId("512f65c6a31a92aae2a214a5"),
"uid" : "y",
"val" : "string2"
}
{
"_id" : ObjectId("512f65e8a31a92aae2a214a6"),
"uid" : "y",
"val" : [
"string3",
"string4"
]
}
{
"_id" : ObjectId("512f65e8a31a92aae2a214a7"),
"uid" : "z",
"val" : [
"string"
]
}
{
"_id" : ObjectId("512f65e8a31a92aae2a214a8"),
"uid" : "y",
"val" : [
"string1",
"string2"
]
}
Pipeline stages:
> project1 = {
"$project" : {
"uid" : 1,
"val" : 1,
"isArray" : {
"$cond" : [
{
"$eq" : [
"$val.0",
[ ]
]
},
true,
false
]
}
}
}
> project2 = {
"$project" : {
"uid" : 1,
"valA" : {
"$cond" : [
"$isArray",
"$val",
[
null
]
]
},
"valS" : {
"$cond" : [
"$isArray",
null,
"$val"
]
},
"isArray" : 1
}
}
> unwind = { "$unwind" : "$valA" }
> project3 = {
"$project" : {
"_id" : 0,
"uid" : 1,
"val" : {
"$cond" : [
"$isArray",
"$valA",
"$valS"
]
}
}
}
Final aggregation:
> db.md.aggregate(project1, project2, unwind, project3, group)
{
"result" : [
{
"_id" : "z",
"vals" : [
"string"
]
},
{
"_id" : "y",
"vals" : [
"string1",
"string4",
"string3",
"string2"
]
},
{
"_id" : "x",
"vals" : [
"string"
]
}
],
"ok" : 1
}
If you modify your schema using always "vals.val" field as an array field (even when the record contains only one element) you can do it easily as follows:
db.test_col.insert({
vals : [
{
uid : "uuid1",
val : ["value1"]
},
{
uid : "uuid2",
val : ["value2", "value3"]
}]
});
db.test_col.insert(
{
vals : [{
uid : "uuid2",
val : ["value4", "value5"]
}]
});
Using this approach you only need to use two $unwind operations: one unwinds the "parent" array and the second unwinds every "vals.val" value. So, querying like
db.test_col.aggregate(
{ $unwind : "$vals" },
{ $unwind : "$vals.val" },
{
$group : {
_id : { uid : "$vals.uid" },
vals : { $addToSet : "$vals.val" }
}
}
);
You can obtain your expected value:
{
"result" : [
{
"_id" : {
"uid" : "uuid2"
},
"vals" : [
"value5",
"value4",
"value3",
"value2"
]
},
{
"_id" : {
"uid" : "uuid1"
},
"vals" : [
"value1"
]
}
],
"ok" : 1
}
And no, you can't execute this query using your current schema, since $unwind fails when the field isn't an array field.