spring-mongo Given DBObject must be a BasicDBObject - spring-data-mongodb

I have a odd error happening when I upgraded spring-data-mongo from 1.3.2.RELEASE to 1.5.2.RELEASE
I have an object that looks something like this:
#Document(collection = "foos")
public class Foo {
#Id
private String id;
private GeoPoint[] tracks;
}
public class GeoPoint {
GeoPointValue[] points;
}
public class GeoPointValue {
#Field(value = "0")
double lon;
#Field(value = "1")
double lat;
#Field(value = "2")
double value;
}
I have a test tat creates one of these objects, saves it, then reloads it. When it saves it looks like this:
{
"_class" : "com.Foo",
"_id" : ObjectId("53f6630df91f68368b17da91"),
"tracks" : [
{
"points" : [
[
0,
0,
999.9000244140625
],
[
1.8605,
-7.6815,
1
],
[
1.0885,
-0.0001,
1
]
]
},
{
"points" : [
[
-0.0001581075944187447,
-0.003384031509668049,
999.9000244140625
],
[
-0.0003763519887295627,
-0.003578620265780311,
1
],
[
-0.0006024558351500737,
-0.003581886877337006,
1
]
]
}
],
"version" : 0
}
but when it reloads I get the following exception which I have traced to the points array:
java.lang.IllegalArgumentException: Given DBObject must be a BasicDBObject! Object of class [com.mongodb.BasicDBList] must be an instance of class com.mongodb.BasicDBObject
at org.springframework.util.Assert.isInstanceOf(Assert.java:337)
at org.springframework.data.mongodb.core.convert.DBObjectAccessor.<init>(DBObjectAccessor.java:47)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.<init>(MappingMongoConverter.java:1046)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getParameterProvider(MappingMongoConverter.java:230)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:242)

For spring-data-mongodb, GeoPointValue is a Map structure, so what it expects is BasicDBObject type - BasicDBList is for List structure, this is the reason for the exception, I think.
You can try to change values of #Field in GeoPointValue from "0", "1", "2" to "a", "b", "c", maybe the exception will disappear.
I feel weird that "points" value is [ [ ],[ ],[ ] ], it should be [ { }, { }, { } ]. You can revert the mongo-java-driver from 2.12.3 to 2.11.3, if the points value is [ { }, { }, { } ] after saving, then maybe the driver make some special treatment for Number Key but spring-data-mongodb hasn't caught it up yet.

I had The Same Problem
and I Solved it By Adding #DBRef to the field

Related

mongodb - Get one array from two arrays in collection

In my mongodb collection, I have sometimes two, sometimes one and sometimes null arrays on a document. Now I'd like to get one array over the whole collection with the values of these arrays.
The document looks like this:
{
"title" : "myDocument",
"listOne" : [
"valueOne",
"valueTwo"
],
"listTwo" : [
"abc",
"qwer"
]
},
{
"title" : "myDocumentTwo",
"listTwo" : [
"321"
]
},
{
"title" : "myDocumentAlpha",
"listOne" : [
"alpha",
"beta"
]
},
{
"title" : "myDocumentbeta"
}
And I expect the following output:
"combinedList" : [
"valueOne",
"valueTwo",
"abc",
"qwer",
"321",
"alpha",
"beta"
]
It's like every possible value from these twos array out of every document in this collection.
You can do this using aggregate and $concatArrays
db.collection.aggregate([
{
$project: {
combinedList: {
$concatArrays: [{$ifNull: ["$listOne", []]}, {$ifNull: ["$listTwo", []]}]
}
}
},
{ $unwind: "$combinedList" },
{ $group: { _id: null, combinedList: { $addToSet: "$combinedList"}}},
{ $project: { _id: 0, combinedList: 1 }}
])

Mongodb - Find in deeply nested array

I am trying to find if type=conv is present in given rs , which is present in d --> ds --> rs --> type.
DOCUMENT
{
"_id" : ObjectId("5ec25873bd796ff191e695b1"),
"c_name" : "c1",
"t_name" : "t1",
"d" : [
{
"name" : "d1",
"ds" : [
{
"name" : "ds1",
"rs" : [
{
"type" : "conv"
}
]
}
]
},
{
"name" : "d2",
"ds" : [
{
"name" : "ds2",
"rs" : []
}
]
}
]
}
QUERY:
filter = {
"$and": [
{"c_name": {'$eq': 'c1'}},
{"t_name": {'$eq': 't1'}},
{"d.name": {'$eq': 'd2'}},
{"d.ds.name": {'$eq': 'ds2'}},
{"d.ds.rs.type": {'$eq': 'conv'}}
]
}
OUTPUT
It is returning me document, i guess it is looking for presence of type = conv in full document, even though it is not present on ds2 ( part of d2), but present on ds1 ( part of d1).
Do we have simpler way to find if it exists or not, I would like to first find and then using array filters , we can update the specific element inside deeply nested array.
Could someone please suggest how should I approach this problem? ( if we have any solution without using aggregation )
You need to use $elemMatch for these type of condition, And you can match strings without $eq operator
filter = {"$and": [
{"c_name": "c1"},
{"t_name": "t1"},
{ "d": { $elemMatch: { "name": "d2", "ds.name": "ds2" , "ds.rs.type": "conv"} } }
]}
With $elemMatch It will search only in the array where all condition will be true
Thanks #puneet , i need to try above. However, i am using now this
filter_query = {
"$and": [
{"c_name": {'$eq': 'c1'}},
{"t_name": {'$eq': 't1'}},
{
'd': {
'$elemMatch': {
'name': 'd2',
'ds': {
'$elemMatch':
{'name': 'ds2',
'rs':
{'$elemMatch':
{
"type": 'conv'}
}
}
}
}
}
}]}
This is also working !!

How can I insert a new entry inside Multilevel structure of mongoDB database?

I am working on an app that contain details of an academic year in Multilevel structure of mongoDB database.
For this I've created structure like this:
[
{
"_id" : ObjectId("5a1519a71fe8cc4df5888ff5"),
"academicyear" : "2016-2017",
"departments" : [
{
"deptname" : "computer",
"semesters" : [
{
"sem" : "2",
"classes" : [
{
"class" : "1",
"theory" : [
{
"subname" : "DS",
"faculty" : [
{
"facultyname" : "KSS",
"facultydept" : "COMP"
}
]
}
],
"practical" : [
{
"subname" : "DS",
"batch" : [
{
"bname" : "C",
"facultyname" : "KSS",
"facultydept" : "COMP"
}
]
}
]
}
]
}
]
}
]
}
]
Now for the same academic year and for the same department I would like to create a new semester node inside the array of semesters. For that I've tried syntax of $ and use of . operator but it did not worked well. How can I add a new semester node inside that semesters array? Also after creating a new semester node, how can I add a new class in an classes array?
it could be done by means of the positional operator $[],
db.collection.update(
{ "academicyear": "2016-2017", "departments.deptname": "computer"},
{ "$push":
{"departments.$[].semesters":
{
"sem" : "2",
"classes" : [ ...
}
}
}
)
It could also be done by indicating the position in the array if known. "departments.1.semesters", where 1 is the position of 'departments' in the array.
db.collection.update(
{ "academicyear": "2016-2017"},
{ "$push":
{"departments.1.semesters":
{
"sem" : "2",
"classes" : [ ...
}
}
}
)
One way of doing it would be:
var toInsert = {"sem" : "2", "classes" : [ ... }
db.collection.update(
{ "academicyear": "2016-2017", departments: { $elemMatch: { deptname: "computer" } } },
{ $addToSet: { 'departments.$.semesters': toInsert } }
)
The query part will find the document from the list array with id = 2. Then we use $ positional element to add a new element at that array index.
and the same would apply for classes.
db.collection.update(
{ "academicyear": "2016-2017"},
{ "$push":
{"departments.1.semesters.0.classes":
{
"class" : "1",
"theory" : [{ ...
}
}
}
)
Another alternative would be to extract the document, update it on the returned object and save it again.
db.collection.findOne({"academicyear": "2016-2017", "departments.deptname": "computer"}, {'departments.semesters': 1}, (err, doc) => {
if(err) return handleError(res)(err);
if(!doc) return res.send(404).end();
//I guess semester is 0, but you can find the one that you are going to update by some filter
doc.departments[0].semesters[0].classes.push({...});
doc.save( (err) => {
if(err) return handleError(res)(err)
return res.status(200).end();
});
});

Push elements from arrays into a single array in mongodb

My Document Structure:
{
"_id" : ObjectId("59edc58af33e9b5988b875fa"),
"Agent" : {
"Name" : "NomanAgent",
"Location" : "Lahore",
"AgentId" : 66,
"Reward" : "Thumb Up",
"Suggestion" : [
"Knowledge",
"Professionalisn"
]
}
}
What I want to achieve in this query:
I want to find the count of each suggestion given by a customer to every agent, it should look something like,
{
"AgentName": "Xyz",
"SuggestionCounts": {
"Knowledge": 2,
"Professionalism": 3,
"Friendliness": 1
}
}
What I have done so far,
db.getCollection('_survey.response').aggregate([
{
$group:{
_id: "$Agent.Name",
Suggestions: {$push:"$Agent.Suggestion"}
}
}
]);
Output:
/* 1 */
{
"_id" : "GhazanferAgent",
"Suggestions" : [
[
"Clarity",
"Effort"
],
[
"Friendliness"
]
]
}
/* 2 */
{
"_id" : "NomanAgent",
"Suggestions" : [
[
"Knowledge",
"Professionalisn"
]
]
}
How I want it to be(As Suggestion in the document is an array and when when i group documents by Agent.Name so it become array of arrays as shown in my output, it want to merge all arrays into single with duplication and then i will find the count of each element in array):
/* 1 */
{
"_id" : "GhazanferAgent",
"SuggestionsCombined" : [
[
"Clarity",
"Effort",
"Friendliness"
]
]
}
/* 2 */
{
"_id" : "NomanAgent",
"SuggestionsCombined" : [
[
"Knowledge",
"Professionalisn"
]
]
}
Thanks in advance!!
One way would be like this - the output structure is not identical to what you suggested but probably close enough:
db.getCollection('_survey.response').aggregate([
{
$unwind: "$Agent.Suggestion" // flatten "Suggestion" array
}, {
$group:{ // group by agent and suggestion
_id: { "AgentName": "$Agent.Name", "Suggestion": "$Agent.Suggestion" },
"Count": { $sum: 1} // calculate count of occurrencs
}
}, {
$group:{
_id: "$_id.AgentName", // group by agent only
"Suggestions": { $push: { "Suggestion": "$_id.Suggestion", "Count": "$Count" } } // create array of "Suggestion"/"Count" pairs per agent
}
}
]);

MongoDB Update Array element

I have a document structure like
{
"_id" : ObjectId("52263922f5ebf05115bf550e"),
"Fields" : [
{
"Field" : "Lot No",
"Rules" : [ ]
},
{
"Field" : "RMA No",
"Rules" : [ ]
}
]
}
I have tried to update by using the following code to push into the Rules Array which will hold objects.
db.test.update({
"Fields.Field":{$in:["Lot No"]}
}, {
$addToSet: {
"Fields.Field.$.Rules": {
"item_name": "my_item_two",
"price": 1
}
}
}, false, true);
But I get the following error:
can't append to array using string field name [Field]
How do I do the update?
You gone too deep with that wildcard $. You match for an item in the Fields array, so you get a access on that, with: Fields.$. This expression returns the first match in your Fields array, so you reach its fields by Fields.$.Field or Fields.$.Result.
Now, lets update the update:
db.test.update({
"Fields.Field": "Lot No"
}, {
$addToSet: {
"Fields.$.Rules": {
'item_name': "my_item_two",
'price':1
}
}
}, false, true);
Please note that I've shortened the query as it is equal to your expression.

Resources