MONGO - Know if all elements of input array is in mongoDB - mongodb

I have a problem with mongo query. I have the next colecction.
"id" : "TEST"
"client" : "0048948",
"contracts" : [
{
"contractNumberId" : "contractNumber1",
"otherField" : "XXXXX",
"contractFormats" : [
{
"formatId" : "format12"
},
{
"formatId" : "format10"
}
]
},{
"contractNumberId" : "contractNumber2",
"otherField" : "XXXXX",
"contractFormats" : [
{
"formatId" : "format12"
},
{
"formatId" : "format15"
}
]
}
]
And I want to search if I have a collection in which I have all the values ​​of an array of contracts or formats that are passed to me. For example if in my input I have the next contracts or formats [contractNumber1, format15] My query should return true because I found it in my database, in contracts or format, but if for example mi inputs was [contractNumbe1, format50, contract3, 10], my query should return false, because any document of my database match that all ocurrences of the input is in any document.
The query should find in contracts and formats all inputs values of input array.
Can anyone help me please?. I have tried with $elemMatch, $nor and $in, $unwind but unsatisfactory result.
Thanks to all for your time.

Related

mongoDB: Querying for documents that may have some specifics options

I'm quite new to mongodb and there is one thing I can't solve right now:
Let's pretend, you have the following document structure:
{
"_id": ObjectId("some object id"),
name: "valueName",
options: [
{idOption: "optionId", name: "optionName"},
{idOption: "optionId", name: "optionName"}
]
}
And each document can have multiples options that are already classified.
I'm trying to get all the documents in the collection that have, at least one, of the multiples options that I pass for the query.
I was trying with the operator $elemMatch something like this:
db.collectioName.find({"options.name": { $elemMatch: {"optName1","optName2"}}})
but it never show me the matches documents.
Can someone help and show me, what I'm doing wrong?
Thanks!
Given a collection which contains the following documents:
{
"_id" : ObjectId("5a023b8d027b5bd06add627a"),
"name" : "valueName",
"options" : [
{
"idOption" : "optionId",
"name" : "optName1"
},
{
"idOption" : "optionId",
"name" : "optName2"
}
]
}
{
"_id" : ObjectId("5a023b9e027b5bd06add627d"),
"name" : "valueName",
"options" : [
{
"idOption" : "optionId",
"name" : "optName3"
},
{
"idOption" : "optionId",
"name" : "optName4"
}
]
}
This query ...
db.collection.find({"options": { $elemMatch: {"name": {"$in": ["optName1"]}}}})
.. will return the first document only.
While, this query ...
db.collection.find({"options": { $elemMatch: {"name": {"$in": ["optName1", "optName3"]}}}})
...will return both documents.
The second example (I think) meeets this requirement:
I'm trying to get all the documents in the collection that have, at least one, of the multiples options that I pass for the query.

Why do I get this result from a multiple criteria query?

On mongo, I issue the following query:
db.usertest.find({ "events.event":"event0", "events.event":"event1", "events.event":"eventX" })
As per the docs here http://docs.mongodb.org/manual/tutorial/query-documents/#id4 I would expect this to only match documents that had all these three elements in an array.
However, it matches this doc:
{ "_id" : "userbruce", "events" : [ { "event" : "eventX" } ] }
Why?
The docs say that this format matches documents that "contain elements that in some combination satisfy the query conditions" What is matching event0 or event1?
EDIT: Is it getting confused because I put in the same field each time?
For AND operation in with this data, please try below query:
db.usertest.find({$and : [{ "events.event":"event0"}, {"events.event":"event1"}, {"events.event":"eventX" }]})
And yes, the confusion is because you given same field each time. IF you try something like below document:
{
"_id" : "userbruce",
"events" : [
{
"eventA" : "eventX"
},
{
"eventB" : "event0"
},
{
"eventC" : "event1"
}
]
}
then, your query would not have given any result. So, your query stands correct, but the data is not appropriate for that query.

Mongodb Update/Upsert array exact match

I have a collection :
gStats : {
"_id" : "id1",
"criteria" : ["key1":"value1", "key2":"value2"],
"groups" : [
{"id":"XXXX", "visited":100, "liked":200},
{"id":"YYYY", "visited":30, "liked":400}
]
}
I want to be able to update a document of the stats Array of a given array of criteria (exact match).
I try to do this on 2 steps :
Pull the stat document from the array of a given "id" :
db.gStats.update({
"criteria" : {$size : 2},
"criteria" : {$all : [{"key1" : "2096955"},{"value1" : "2015610"}]}
},
{
$pull : {groups : {"id" : "XXXX"}}
}
)
Push the new document
db.gStats.findAndModify({
query : {
"criteria" : {$size : 2},
"criteria" : {$all : [{"key1" : "2015610"}, {"key2" : "2096955"}]}
},
update : {
$push : {groups : {"id" : "XXXX", "visited" : 29, "liked" : 144}}
},
upsert : true
})
The Pull query works perfect.
The Push query gives an error :
2014-12-13T15:12:58.571+0100 findAndModifyFailed failed: {
"value" : null,
"errmsg" : "exception: Cannot create base during insert of update. Cause
d by :ConflictingUpdateOperators Cannot update 'criteria' and 'criteria' at the
same time",
"code" : 12,
"ok" : 0
} at src/mongo/shell/collection.js:614
Neither query is working in reality. You cannot use a key name like "criteria" more than once unless under an operator such and $and. You are also specifying different fields (i.e groups) and querying elements that do not exist in your sample document.
So hard to tell what you really want to do here. But the error is essentially caused by the first issue I mentioned, with a little something extra. So really your { "$size": 2 } condition is being ignored and only the second condition is applied.
A valid query form should look like this:
query: {
"$and": [
{ "criteria" : { "$size" : 2 } },
{ "criteria" : { "$all": [{ "key1": "2015610" }, { "key2": "2096955" }] } }
]
}
As each set of conditions is specified within the array provided by $and the document structure of the query is valid and does not have a hash-key name overwriting the other. That's the proper way to write your two conditions, but there is a trick to making this work where the "upsert" is failing due to those conditions not matching a document. We need to overwrite what is happening when it tries to apply the $all arguments on creation:
update: {
"$setOnInsert": {
"criteria" : [{ "key1": "2015610" }, { "key2": "2096955" }]
},
"$push": { "stats": { "id": "XXXX", "visited": 29, "liked": 144 } }
}
That uses $setOnInsert so that when the "upsert" is applied and a new document created the conditions specified here rather than using the field values set in the query portion of the statement are used instead.
Of course, if what you are really looking for is truly an exact match of the content in the array, then just use that for the query instead:
query: {
"criteria" : [{ "key1": "2015610" }, { "key2": "2096955" }]
}
Then MongoDB will be happy to apply those values when a new document is created and does not get confused on how to interpret the $all expression.

Resolving MongoDB DBRef array using Mongo Native Query and working on the resolved documents

My MongoDB collection is made up of 2 main collections :
1) Maps
{
"_id" : ObjectId("542489232436657966204394"),
"fileName" : "importFile1.json",
"territories" : [
{
"$ref" : "territories",
"$id" : ObjectId("5424892224366579662042e9")
},
{
"$ref" : "territories",
"$id" : ObjectId("5424892224366579662042ea")
}
]
},
{
"_id" : ObjectId("542489262436657966204398"),
"fileName" : "importFile2.json",
"territories" : [
{
"$ref" : "territories",
"$id" : ObjectId("542489232436657966204395")
}
],
"uploadDate" : ISODate("2012-08-22T09:06:40.000Z")
}
2) Territories, which are referenced in "Map" objects :
{
"_id" : ObjectId("5424892224366579662042e9"),
"name" : "Afghanistan",
"area" : 653958
},
{
"_id" : ObjectId("5424892224366579662042ea"),
"name" : "Angola",
"area" : 1252651
},
{
"_id" : ObjectId("542489232436657966204395"),
"name" : "Unknown",
"area" : 0
}
My objective is to list every map with their cumulative area and number of territories. I am trying the following query :
db.maps.aggregate(
{'$unwind':'$territories'},
{'$group':{
'_id':'$fileName',
'numberOf': {'$sum': '$territories.name'},
'locatedArea':{'$sum':'$territories.area'}
}
})
However the results show 0 for each of these values :
{
"result" : [
{
"_id" : "importFile2.json",
"numberOf" : 0,
"locatedArea" : 0
},
{
"_id" : "importFile1.json",
"numberOf" : 0,
"locatedArea" : 0
}
],
"ok" : 1
}
I probably did something wrong when trying to access to the member variables of Territory (name and area), but I couldn't find an example of such a case in the Mongo doc. area is stored as an integer, and name as a string.
I probably did something wrong when trying to access to the member variables of Territory (name and area), but I couldn't find an example
of such a case in the Mongo doc. area is stored as an integer, and
name as a string.
Yes indeed, the field "territories" has an array of database references and not the actual documents. DBRefs are objects that contain information with which we can locate the actual documents.
In the above example, you can clearly see this, fire the below mongo query:
db.maps.find({"_id":ObjectId("542489232436657966204394")}).forEach(function(do
c){print(doc.territories[0]);})
it will print the DBRef object rather than the document itself:
o/p: DBRef("territories", ObjectId("5424892224366579662042e9"))
so, '$sum': '$territories.name','$sum': '$territories.area' would show you '0' since there are no fields such as name or area.
So you need to resolve this reference to a document before doing something like $territories.name
To achieve what you want, you can make use of the map() function, since aggregation nor Map-reduce support sub queries, and you already have a self-contained map document, with references to its territories.
Steps to achieve:
a) get each map
b) resolve the `DBRef`.
c) calculate the total area, and the number of territories.
d) make and return the desired structure.
Mongo shell script:
db.maps.find().map(function(doc) {
var territory_refs = doc.territories.map(function(terr_ref) {
refName = terr_ref.$ref;
return terr_ref.$id;
});
var areaSum = 0;
db.refName.find({
"_id" : {
$in : territory_refs
}
}).forEach(function(i) {
areaSum += i.area;
});
return {
"id" : doc.fileName,
"noOfTerritories" : territory_refs.length,
"areaSum" : areaSum
};
})
o/p:
[
{
"id" : "importFile1.json",
"noOfTerritories" : 2,
"areaSum" : 1906609
},
{
"id" : "importFile2.json",
"noOfTerritories" : 1,
"areaSum" : 0
}
]
Map-Reduce functions should not be and cannot be used to resolve DBRefs in the server side.
See what the documentation has to say:
The map function should not access the database for any reason.
The map function should be pure, or have no impact outside of the
function (i.e. side effects.)
The reduce function should not access the database, even to perform
read operations. The reduce function should not affect the outside
system.
Moreover, a reduce function even if used(which can never work anyway) will never be called for your problem, since a group w.r.t "fileName" or "ObjectId" would always have only one document, in your dataset.
MongoDB will not call the reduce function for a key that has only a
single value

How to access data from a nested array structured collection

I have the following structure in my MongoDB .
I have a nested structure of my collection named chains which is shown below .
I am trying to access options of a particular date as shown below which is 2015-01-17 in my case .
db.chains.find({ "symbol" : "UBSC" ,"option_exp.expiration_dt" : "2015-01-17"}).pretty()
But the following query above is returning me all the data related to that Symbol .
{
"_id" : ObjectId("52000a90d293b0e4134e8c35"),
"symbol" : "UBSC",
"option_exp" : [
{
"expiration_dt" : "2015-01-17",
"options" : [
{
"mult" : "10"
},
{
"mult" : "10"
}
]
},
{
"expiration_dt" : "2014-01-18",
"options" : [
{
"prem_mult" : "10"
},
{
"prem_mult" : "10"
}
}
]
}
],
}
This is the way i was trying to access through java
BasicDBObject query = new BasicDBObject();
query.append("symbol", "UBSC");
query.append("option_exp.expiration_dt", "2015-01-17");
Could anybody please help me as how to access data of a particular date .
Use $elemMatch to limit content of option_exp array field that is included in result:
db.chains.find({symbol : "UBSC" ,"option_exp.expiration_dt" : "2015-01-17"},
{option_exp: {$elemMatch: {expiration_dt: "2015-01-17"}}})
This will select documents which have symbol equal to "UBSC" and option_exp array items with expiration_dt equal to "2015-01-17". Then we limit option_exp array content to items which have required expiration date (otherwise whole document will all option_exp items will be returned).