MongoDB Search nested Objects without knowing Key - java

I have a list of objects that are given somewhat arbitrary Object keys as a result of using the async Java driver + BSON.
My issue is given the fact that jobStatuses are an arbitrary list of Dictionary items where I don't know the key, I have no idea how to access its sub-values. In the end, I'm trying to build a query that returns if ANY of jobStatus.*._id are true given a list of potential Object ID's.
So I'd be giving a list of ID's and want to return true if ANY of the items in jobStatuses have any of the given ID's. Any ideas?

Let's try this :
db.yourCollectionName.aggregate([
{
$project: {
_id: 0,
jobStatutses: { $arrayElemAt: [{ $objectToArray: "$jobStatutses" }, 0] }
}
}, {
$match: { 'jobStatutses.v._id': { $in: [ObjectId("5d6d8c3a5a0d22d3c84dd6dc"), ObjectId("5d6d8c3a5a0d22d3c84dd6ed")] } }
}
])
Collection Data :
/* 1 */
{
"_id" : ObjectId("5e06319c400289966eea6a07"),
"jobStatutses" : {
"5d6d8c3a5a0d22d3c84dd6dc" : {
"_id" : ObjectId("5d6d8c3a5a0d22d3c84dd6dc"),
"accepted" : "123",
"completed" : 0
}
},
"something" : 1
}
/* 2 */
{
"_id" : ObjectId("5e0631ad400289966eea6dd1"),
"jobStatutses" : {
"5d6d8c3a5a0d22d3c84dd6ed" : {
"_id" : ObjectId("5d6d8c3a5a0d22d3c84dd6ed"),
"accepted" : "456",
"completed" : 0
}
},
"something" : 2
}
/* 3 */
{
"_id" : ObjectId("5e0631cd400289966eea7542"),
"jobStatutses" : {
"5e06319c400289966eea6a07" : {
"_id" : ObjectId("5e06319c400289966eea6a07"),
"accepted" : "789",
"completed" : 0
}
},
"something" : 3
}
Output :
/* 1 */
{
"jobStatutses" : {
"k" : "5d6d8c3a5a0d22d3c84dd6dc",
"v" : {
"_id" : ObjectId("5d6d8c3a5a0d22d3c84dd6dc"),
"accepted" : "123",
"completed" : 0
}
}
}
/* 2 */
{
"jobStatutses" : {
"k" : "5d6d8c3a5a0d22d3c84dd6ed",
"v" : {
"_id" : ObjectId("5d6d8c3a5a0d22d3c84dd6ed"),
"accepted" : "456",
"completed" : 0
}
}
}
All you need is to check if at least one doc gets returned from DB for a given list or not, So we don't need to worry about document structure then just do result.length in your code to say at least one doc got matched for the input list.

Related

How to make a select with grouping in MongoDB

I am new to mongo and I try to obtain a group given by the identity document, the equivalent in sql would have the following form:
SELECT fecIni, idc , cic
from db.cllAuditoria
WHERE fecIni BETWEEN '2017-07-01T05:00:00.000Z' AND '2017-07-31T05:00:00.000Z'
group by idc , cic
please friends, this would help me a lot I have researched from different sources without coming up with the solution, I thank you in advance.
Assuming U have collection like:
/* 1 */
{
"_id" : ObjectId("5a58dafe1aadbcd8ae74572e"),
"fecIni" : ISODate("2017-07-30T05:00:00.000Z"),
"idc" : "idc 1",
"cic" : "cic 1"
}
/* 2 */
{
"_id" : ObjectId("5a58dc3a1aadbcd8ae745758"),
"fecIni" : ISODate("2017-07-29T05:00:00.000Z"),
"idc" : "idc 1",
"cic" : "cic 1"
}
/* 3 */
{
"_id" : ObjectId("5a58dc671aadbcd8ae74575e"),
"fecIni" : ISODate("2017-07-28T05:00:00.000Z"),
"idc" : "idc 1",
"cic" : "cic 2"
}
Grouping by idc, cic fields could be performed by query:
db.cllAuditoria.aggregate([
{
$match : {
fecIni: {
$gte: ISODate("2017-07-01T05:00:00.000Z"),
$lte: ISODate("2017-07-31T05:00:00.000Z")
}
}
},
{ $group : { _id : { "idc": "$idc", "cic" : "$cic"} } }
]);
Outcome will looks:
/* 1 */
{
"_id" : {
"idc" : "idc 1",
"cic" : "cic 2"
}
}
/* 2 */
{
"_id" : {
"idc" : "idc 1",
"cic" : "cic 1"
}
}
If U want to count items in group:
db.cllAuditoria.aggregate([
{
$match : {
fecIni: {
$gte: ISODate("2017-07-01T05:00:00.000Z"),
$lte: ISODate("2017-07-31T05:00:00.000Z")
}
}
},
{ $group : {
_id : { "idc": "$idc", "cic" : "$cic"},
count: { $sum: 1 }
} }
]);
Result:
/* 1 */
{
"_id" : {
"idc" : "idc 1",
"cic" : "cic 2"
},
"count" : 1.0
}
/* 2 */
{
"_id" : {
"idc" : "idc 1",
"cic" : "cic 1"
},
"count" : 2.0
}
Hope, it will helps U. If your data differs, please, provide example of collection and what U want to get as a result.

Accessing the "_embedded" data in spring data REST interface

The project is a visual analysis of business data, MongoDB through Spring Data, REST interface, then d3.js.
On the database level, my data looks like this:
/* 1 */
{
"_id" : ObjectId("58ac466160fb39e5e8dc8b70"),
"dots" : {
"x" : 4,
"y" : 3
}
}
/* 2 */
{
"_id" : ObjectId("58ac468060fb39e5e8dc8b7e"),
"squares" : {
"x" : 12,
"y" : 2
}
}
The REST interface (spring) delivers this:
{
"_embedded" : {
"JSON" : [ {
"squares" : null,
"dots" : {
"dot" : {
"x" : 4,
"y" : 3
}
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/JSON/58ac466160fb39e5e8dc8b70"
},
"jSON" : {
"href" : "http://localhost:8080/JSON/58ac466160fb39e5e8dc8b70"
}
}
}, {
"squares" : {
"square" : {
"x" : 12,
"y" : 2
}
},
"dots" : null,
"_links" : {
"self" : {
"href" : "http://localhost:8080/JSON/58ac468060fb39e5e8dc8b7e"
},
"jSON" : {
"href" : "http://localhost:8080/JSON/58ac468060fb39e5e8dc8b7e"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/JSON"
},
"profile" : {
"href" : "http://localhost:8080/profile/JSON"
}
},
"page" : {
"size" : 20,
"totalElements" : 2,
"totalPages" : 1,
"number" : 0
}
}
Now I'm having trouble accessing this for processing in d3.js for visualization, whatever way I try to access the data, I only get "undefined" with no values back on the console.
Should I reformat to get rid of the "_embedded" part, or to "flatten" the data generally, or do I need a specific way to access it?
As of now, I'm just using "d3.json("/JSON")" to access the interface, but can't extract any data.

Update elements inside a array MongoDB using Java

A sample JSON Document
{
"_id" : "ab85cebc-8c7a-43bf-8efc-1151ccaa4f84",
"address" : {
"city" : "Bangalore",
"postcode" : "560080",
"countrycode":"in",
"street" : "SADASHIVNAGAR,SANKEY TANK RD"
},
"brand" : "Shell",
"prices" : [
{
"fuelType" : "DIESEL",
"price" : 52.7
},
{
"fuelType" : "PETROL",
"price" : 67.05
}
]
}
I have around 20 docs for brand:Shell with different location in and around Bangalore.
For all those I have to update Diesel and Petrol price.
Say current DIESEL and PETROL prices are 57.9 and 71.4 respectively?
How do I update all document with these latest price using JAVA (using Eclipse IDE)
Code (in complete)
public class UpdateGasstationFuelPrice {
public static void main(String[] args) {
MongoClient client = new MongoClient("localhost",27017);
MongoDatabase db = client.getDatabase( "notes" );
MongoCursor<Document> cursor = db.getCollection( "gasstation" ).find( new BasicDBObject( "address.countrycode","in" )
.append("address.city","Bangalore")
.append("brand","Shell")).iterator();
if (cursor.hasNext()){
Document doc = cursor.next();
}
client.close();
}
}
Update with Query
db.getCollection("gasstation").update({"address.countrycode":"in","address.city":"Bangalore","brand":"Shell"},
//Query to get the position
{
"prices": { $exists: true }
},
// Use the positional $ operator to update specific element (which matches your query
{
$set:
{
//set value specific to elements field/key
"prices" : [
{
"fuelType" : "DIESEL",
"price" : 502.7
},
{
"fuelType" : "PETROL",
"price" : 607.05
}
]
}
}
);
You cannot updated util you know the position of the element which you want to update.
So basically what you can do is:
You need to query to seek the position.
Use the positional operator and update the array.
db.gasstation.update(
//Query to get the position
{
"prices.fuelType": "DIESEL"
},
// Use the positional $ operator to update specific element (which matches your query
{
$set:
{
"prices.$" :
//Element/new value to update
{
"fuelType" : "DIESEL",
"price" : 999.7
}
}
}
);
If you want just to update only specific field inside the json element embedded in the array you can do as follows:
db.gasstation.update(
//Query to get the position
{
"prices.fuelType": "DIESEL"
},
// Use the positional $ operator to update specific element (which matches your query
{
$set:
{
//set value specific to elements field/key
//i.e. Update documents in an Array
"prices.$.price" : 999.7
}
}
);
Updates based on comments:
db.gasstation.update(
//Query to match
{
"address.city":"Bangalore",
"brand":"Shell",
"countrycode":"in",
"prices": { $exists: true }
},
// Use $set operator & overwrite entire array
{
$set:
{
//Overwrite value
"prices" : [
{
"fuelType" : "DIESEL",
"price" : 502.7
},
{
"fuelType" : "PETROL",
"price" : 607.05
}
]
}
}
);

How do I get a specific element of the array in mongoDB?

I want to get a specific element of the array and through the responsaveis.$ (daniela.morais#sofist.com.br) but there is no result, there is problem in my syntax?
{
"_id" : ObjectId("54fa059ce4b01b3e086c83e9"),
"agencia" : "Abc",
"instancia" : "dentsuaegis",
"cliente" : "Samsung",
"nomeCampanha" : "Serie A",
"ativa" : true,
"responsaveis" : [
"daniela.morais#sofist.com.br",
"abc#sofist.com.br"
],
"email" : "daniela.morais#sofist.com.br"
}
Syntax 1
mongoCollection.findAndModify("{'responsaveis.$' : #}", oldUser.get("email"))
.with("{$set : {'responsaveis.$' : # }}", newUser.get("email"))
.returnNew().as(BasicDBObject.class);
Syntax 2
db.getCollection('validatag_campanhas').find({"responsaveis.$" : "daniela.morais#sofist.com.br"})
Result
Fetched 0 record(s) in 1ms
The $ positional operator is only used in update(...) or project calls, you can't use it to return the position within an array.
The correct syntax would be :-
Syntax 1
mongoCollection.findAndModify("{'responsaveis' : #}", oldUser.get("email"))
.with("{$set : {'responsaveis.$' : # }}", newUser.get("email"))
.returnNew().as(BasicDBObject.class);
Syntax 2
db.getCollection('validatag_campanhas').find({"responsaveis" : "daniela.morais#sofist.com.br"})
If you just want to project the specific element, you can use the positional operator $ in projection as
{"responsaveis.$":1}
db.getCollection('validatag_campanhas').find({"responsaveis" : "daniela.morais#sofist.com.br"},{"responsaveis.$":1})
Try with this
db.validatag_campanhas.aggregate(
{ $unwind : "$responsaveis" },
{
$match : {
"responsaveis": "daniela.morais#sofist.com.br"
}
},
{ $project : { responsaveis: 1, _id:0 }}
);
That would give you all documents which meets that conditions
{
"result" : [
{
"responsaveis" : "daniela.morais#sofist.com.br"
}
],
"ok" : 1
}
If you want one document that has in its responsaveis array the element "daniela.morais#sofist.com.br" you can eliminate the project operator like
db.validatag_campanhas.aggregate(
{ $unwind : "$responsaveis" },
{
$match : {
"responsaveis": "daniela.morais#sofist.com.br"
}
}
);
And that will give you
{
"result" : [
{
"_id" : ObjectId("54fa059ce4b01b3e086c83e9"),
"agencia" : "Abc",
"instancia" : "dentsuaegis",
"cliente" : "Samsung",
"nomeCampanha" : "Serie A",
"ativa" : true,
"responsaveis" : "daniela.morais#sofist.com.br",
"email" : "daniela.morais#sofist.com.br"
}
],
"ok" : 1
}
Hope it helps

Gets documents and total count of them in single query include pagination

I'm new in mongo and use mongodb aggregation framework for my queries. I need to retrieve some records which satisfy certain conditions(include pagination+sorting) and also get total count of records.
Now, I perform next steps:
Create $match operator
{ "$match" : { "year" : "2012" , "author.authorName" : { "$regex" :
"au" , "$options" : "i"}}}
Added sorting and pagination
{ "$sort" : { "some_field" : -1}} , { "$limit" : 10} , { "$skip" : 0}
After querying I receive the expected result: 10 documents with all fields.
For pagination I need to know the total count of records which satisfy these conditions, in my case 25.
I use next query to get count : { "$match" : { "year" : "2012" , "author.authorName" : { "$regex" : "au" , "$options" : "i"}}} , { "$group" : { "_id" : "$all" , "reviewsCount" : { "$sum" : 1}}} , { "$sort" : { "some_field" : -1}} , { "$limit" : 10} , { "$skip" : 0}
But I don't want to perform two separate queries: one for retrieving documents and second for total counts of records which satisfy certain conditions.
I want do it in one single query and get result in next format:
{
"result" : [
{
"my_documets": [
{
"_id" : ObjectId("512f1f47a411dc06281d98c0"),
"author" : {
"authorName" : "author name1",
"email" : "email1#email.com"
}
},
{
"_id" : ObjectId("512f1f47a411dc06281d98c0"),
"author" : {
"authorName" : "author name2",
"email" : "email2#email.com"
}
}, .......
],
"total" : 25
}
],
"ok" : 1
}
I tried modify the group operator : { "$group" : { "_id" : "$all" , "author" : "$author" "reviewsCount" : { "$sum" : 1}}}
But in this case I got : "exception: the group aggregate field 'author' must be defined as an expression inside an object". If add all fields in _id then reviewsCount always = 1 because all records are different.
Nobody know how it can be implement in single query ? Maybe mongodb has some features or operators for this case? Implementation with using two separate query reduces performance for querying thousand or millions records. In my application it's very critical performance issue.
I've been working on this all day and haven't been able to find a solution, so thought i'd turn to the stackoverflow community.
Thanks.
You can try using $facet in the aggregation pipeline as
db.name.aggregate([
{$match:{your match criteria}},
{$facet: {
data: [{$sort: sort},{$skip:skip},{$limit: limit}],
count:[{$group: {_id: null, count: {$sum: 1}}}]
}}
])
In data, you'll get your list with pagination and in the count, count variable will have a total count of matched documents.
Ok, I have one example, but I think it's really crazy query, I put it only for fun, but if this example faster than 2 query, tell us about it in the comments please.
For this question i create collection called "so", and put into this collection 25 documents like this:
{
"_id" : ObjectId("512fa86cd99d0adda2a744cd"),
"authorName" : "author name1",
"email" : "email1#email.com",
"c" : 1
}
My query use aggregation framework:
db.so.aggregate([
{ $group:
{
_id: 1,
collection: { $push : { "_id": "$_id", "authorName": "$authorName", "email": "$email", "c": "$c" } },
count: { $sum: 1 }
}
},
{ $unwind:
"$collection"
},
{ $project:
{ "_id": "$collection._id", "authorName": "$collection.authorName", "email": "$collection.email", "c": "$collection.c", "count": "$count" }
},
{ $match:
{ c: { $lte: 10 } }
},
{ $sort :
{ c: -1 }
},
{ $skip:
2
},
{ $limit:
3
},
{ $group:
{
_id: "$count",
my_documets: {
$push: {"_id": "$_id", "authorName":"$authorName", "email":"$email", "c":"$c" }
}
}
},
{ $project:
{ "_id": 0, "my_documets": "$my_documets", "total": "$_id" }
}
])
Result for this query:
{
"result" : [
{
"my_documets" : [
{
"_id" : ObjectId("512fa900d99d0adda2a744d4"),
"authorName" : "author name8",
"email" : "email8#email.com",
"c" : 8
},
{
"_id" : ObjectId("512fa900d99d0adda2a744d3"),
"authorName" : "author name7",
"email" : "email7#email.com",
"c" : 7
},
{
"_id" : ObjectId("512fa900d99d0adda2a744d2"),
"authorName" : "author name6",
"email" : "email6#email.com",
"c" : 6
}
],
"total" : 25
}
],
"ok" : 1
}
By the end, I think that for big collection 2 query (first for data, second for count) works faster. For example, you can count total for collection like this:
db.so.count()
or like this:
db.so.find({},{_id:1}).sort({_id:-1}).count()
I don't fully sure in first example, but in second example we use only cursor, which means higher speed:
db.so.find({},{_id:1}).sort({_id:-1}).explain()
{
"cursor" : "BtreeCursor _id_ reverse",
"isMultiKey" : false,
"n" : 25,
"nscannedObjects" : 25,
"nscanned" : 25,
"nscannedObjectsAllPlans" : 25,
"nscannedAllPlans" : 25,
"scanAndOrder" : false,
!!!!!>>> "indexOnly" : true, <<<!!!!!
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
...
}
For completeness (full discussion was on the MongoDB Google Groups) here is the aggregation you want:
db.collection.aggregate(db.docs.aggregate( [
{
"$match" : {
"year" : "2012"
}
},
{
"$group" : {
"_id" : null,
"my_documents" : {
"$push" : {
"_id" : "$_id",
"year" : "$year",
"author" : "$author"
}
},
"reviewsCount" : {
"$sum" : 1
}
}
},
{
"$project" : {
"_id" : 0,
"my_documents" : 1,
"total" : "$reviewsCount"
}
}
] )
By the way, you don't need aggregation framework here - you can just use a regular find. You can get count() from a cursor without having to re-query.

Categories

Resources