MongoDB - strange behaviour of query - java

I have this two documents in my mongoDB database:
db.DocumentFile.find().pretty()
{
"_id" : ObjectId("587f39910cc0fec092bdb10c"),
"_class" : "com.smartinnotec.legalprojectmanagement.dao.domain.DocumentFile",
"fileName" : "DocumentFile1",
"ending" : "jpg",
"projectId" : "587f39910cc0fec092bdb10b",
"active" : true,
"userIdBlackList" : [
"587f39910cc0fec092bdb10a"
]
}
{
"_id" : ObjectId("587f39910cc0fec092bdb10d"),
"_class" : "com.smartinnotec.legalprojectmanagement.dao.domain.DocumentFile",
"fileName" : "DocumentFile2",
"ending" : "jpg",
"projectId" : "587f39910cc0fec092bdb10b",
"active" : true,
"userIdBlackList" : [ ]
}
I have this code in order to get amount of query:
final Query query = new Query();
query.addCriteria(Criteria.where("‌​userIdBlackList").nin(userId));
final Long amount = mongoTemplate.count(query, DocumentFile.class);
return amount.intValue();
The amount is 2 in this case what is wrong - it should be 1.
The query in Query object looks like this:
Query: { "‌​userIdBlackList" : { "$nin" : [ "587f39910cc0fec092bdb10a"]}}
If I copy this query and made a query for the mongodb console like this:
db.DocumentFile.find({ "‌​userIdBlackList" : { "$nin" : [ "587f39910cc0fec092bdb10a"]}}).pretty()
I get an amount of two, what if wrong because one document includes 587f39910cc0fec092bdb10a in userIdBlackList -> it should be one.
With this query command:
db.DocumentFile.find({userIdBlackList: { "$nin": ["587f39910cc0fec092bdb10a"] } }).pretty();
I get the right result, I am really confused at the moment.
Does anyone have any idea?
Maybe the problem ist that one time userIdBlackList is with quotation mark ("userIdBlackList") and the other time it isn't.

I think the problem is with the unintentional formatting picked up for "userIdBlackList". Your string is interpreted with non printing characters in the "??userIdBlackList" for all your search queries. I see little transparent square boxes when I copy your queries to mongo shell.
That tells me their is some encoding issue. Clear that formatting and see if that helps you.
Both $ne and $nin should work!

Related

scala MongoDB update with $cond and $not (not display the same result)

If you can help me, I have an update in mongo with $cond , this update is only done if the field is empty, otherwise it updates the field with another value. Example in mongo db
I want to update the field camp1
if camp1 no exits = values
if camp1 exits = value2
db.getCollection('prueba').update(
{"cdAccount": "ES3100810348150001326934"},
[{$set:{camp1 :{"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}}]);
Result:
{
"_id" : ObjectId("62dd08c3f9869303b79b323b"),
"cdAccount" : "ES3100810348150001326934",
"camp1" : "value2"
}
Now I do the same in scala with this code
def appendIfNotNull(key: String,value :Object) = {
var eq2Array = new util.ArrayList[Object]()
eq2Array.add("$"+key)
val eq2Op = new Document("$not", eq2Array)
var condList = new util.ArrayList[Object]()
condList.add(eq2Op)
condList.add(value.asInstanceOf[AnyRef])
//condList.add("$"+key)
condList.add("value2")
val availDoc =
new Document("$cond",
new Document("$cond", condList)).toBsonDocument(classOf[BsonDocument],getCodecRegistry).get("$cond")
println("availDoc : " + availDoc)
documentGrab.append(key,availDoc)
}
val finalVar = appendIfNotNull("camp1","values")
println("finalVar : " + finalVar)
availDoc : {"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}
finalVar : Document{{camp1={"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}}
val updateDocument = new Document("$set" , finalVar )
println("updateDocument : " + updateDocument)
collectionA.updateMany(Filters.eq("cdAccount", "ES3100810348150001326934"),updateDocument)
The only difference I see is that in mongodb the "[" is added at the beginning of the $set and it does it well
MongoDB
[ {$set:{camp1 :{"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}} ] --> Ok Update
Scale
{$set:{camp1 :{"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}} --> Ok in scala , but I get the result II
I am using mongodb 5.0.9
Now in mongodb I execute the statement made in scala
db.getCollection('prueba').update(
{"cdAccount": "ES3100810348150001326934"},
{$set :{camp1 :{"$cond": [{"$not": ["$camp1"]}, "values", "value2"]}}});
When I run it in scala the same thing happens
Result II :
{
"cdAccount" : "ES3100810348150001326934",
"camp1" : {
"$cond" : [
{
"$not" : [
"$camp1"
]
},
"values",
"value2"
]
}
}
Can someone tell me how to fix it?
Thank you so much
You see the very important difference when priting the queries.
$cond is an aggregation pipeline operator. It is processed only when aggregation pipeline is used to update the data. When a simple (non-pipelined) update is used, the operator has no special meaning and this is exactly what you see in the output.
You indicate "pipeline update" by passing an array instead of simple object as update description in javascript API (and mongo console). In Scala/Java you have to use one of the updateMany overloads that takes update description as List, not Bson. I.e. you need something like
collectionA.updateMany(
Filters.eq("cdAccount", "ES3100810348150001326934"),
Collections.singletonList(updateDocument)
)

Mongodb Java query for date range

I needed to find all the records in mongo db within two date ranges using Mongo Driver[3.4.0] for Java.
Example:
I have books Collection.
{
"_id" : ObjectId("5acb40d27d63b61cb002bafe"),
"title" : "WingsOfFire",
"pub-date" : ISODate("2013-10-02T00:00:00.000Z"),
"rel-date" : ISODate("2013-11-02T00:00:00.000Z")
}
Like above I have 100s of documents.
I need to find all records wherein pub-date > rel-date.
I am using Mongo DB version 3.2.6
I tried to use $expr operator but it seems to work with only Mongo 3.6+
Not able find cleaner solutions for above requirement.
Please clarify.
The MongoDB (prior to v3.4) shell command for your use case is:
db.collection.aggregate([
{
"$redact": {
"$cond": [
{ "$gt": [ "$pub-date", "$rel-date" ] },
"$$KEEP",
"$$PRUNE"
]
}
}
])
Translating this command into Java you'll get:
MongoClient mongoClient = ...;
MongoCollection<Document> collection = mongoClient.getDatabase("...").getCollection("...");
List<Document> documents = collection.aggregate(Arrays.asList(
new Document("$redact", new Document("$cond",
Arrays.asList(new Document("$gt", Arrays.asList("$pub-date", "$rel-date")), "$$KEEP", "$$PRUNE"))
))).into(new ArrayList<>());
for (Document document : documents) {
System.out.println(document.toJson());
}
Given a collection with these documents ...
{
"_id" : ObjectId("5acb40d27d63b61cb002bafe"),
"title" : "WingsOfFire",
"pub-date" : ISODate("2013-10-02T00:00:00.000Z"),
"rel-date" : ISODate("2013-11-02T00:00:00.000Z")
}
{
"_id" : ObjectId("5acb662756539a6734e64e4a"),
"title" : "WingsOfSmoke",
"pub-date" : ISODate("2013-11-02T00:00:00.000Z"),
"rel-date" : ISODate("2013-10-02T00:00:00.000Z")
}
.. the above Java code will print ...
{ "_id" : { "$oid" : "5acb662756539a6734e64e4a" }, "title" : "WingsOfSmoke", "pub-date" : { "$date" : 1383350400000 }, "rel-date" : { "$date" : 1380672000000 } }
... because this document's pub-date (2013-11-02T00:00:00.000Z) is after its rel-date (2013-10-02T00:00:00.000Z).
Note: the $where operator is functionally equivalent but use of that operator comes with some limitations:
$where evaluates JavaScript and cannot take advantage of indexes. Therefore, query performance improves when you express your query using the standard MongoDB operators (e.g., $gt, $in).
In general, you should use $where only when you can’t express your query using another operator. If you must use $where, try to include at least one other standard query operator to filter the result set. Using $where alone requires a collection scan.
You might want to try $where-Operator:
db.books.find({ "$where": "this.pub-date > this.rel-date"});

How to fetch all the records using the like query in mongodb using java

I want to fetch all the records whose employeeId starts with 123.For this, I thought of using like query with regex.
Sample Records in MongoDB:
============================
{name:"XYZ",employeeID : 123456}
{name:"ABC",employeeID : 123789}
{name:"DEF",employeeID : 214356}
After query, it should retrieve only first two records.
Can anyone please help me out regarding this issue.
Try this
Write Query like this
db.getCollection('employee').find({ $where: "/^123.*/.test(this.employeeID)" })
Result :
/* 1 */
{
"_id" : ObjectId("57d15ab13f239d775c5cc667"),
"name" : "XYZ",
"employeeID" : 123456
}
/* 2 */
{
"_id" : ObjectId("57d15ab13f239d775c5cc668"),
"name" : "ABC",
"employeeID" : 123789
}
Try this:
db.getCollection('test').find({ employeeID: { $regex: /^123.*$/ } })

MongoDB Java Driver: Multiple Date query

I am trying to create a query using MongoDB Java Driver as part of an aggregation command. Currently I allow a date range or an array of specific dates as an argument. eg
<date>
<start>2013-12-10 00:00:00.000</start>
<end>2013-12-12 23:59:59.999</end>
</date>
or
<date>
<specificDates>2013-12-10 00:00:00.000,2013-12-13 00:00:00.000</specificDates>
</date>
The date range query works fine, I parse and convert the xml into a DBObject that produces the following query in mongo;
{ "$match" : { "d" : { "$gte" : { "$date" : "2013-10-01T00:00:00.000Z"} , "$lt" : { "$date" : "2013-10-04T00:00:00.000Z"}}}}
For the specificDates I want to return only results that occur between 00:00:00.000 on the given day and 00:00:00.000 of the next day. From my pretty basic knowledge of mongo querys i had hoped to do a similar $match as the date range, but have it use $in on an array of date ranges similar to the following;
{ "$match" : { "d" : { "$in" : [ { "$gte" : { "$date" : "2013-10-01T00:00:00.000Z"} , "$lt" : { "$date" : "2013-10-02T00:00:00.000Z"}} , { "$gte" : { "$date" : "2013-10-03T00:00:00.000Z"} , "$lt" : { "$date" : "2013-10-04T00:00:00.000Z"}}]}}}
The above query fails to return any results. I have noticed that $in is not listed in the mongodb manual under the Mongo Aggregation Framework section, but its not throwing any kind of errors that I would have expected for an unsupported operation.
I think the issue may come from this line in the MongoDB Manual;
If the field holds an array, then the $in operator selects the documents whose field holds an array that contains at least one element that matches a value in the specified array (e.g. , , etc.)
In my collection the date isn't stored in an array, I suppose I could store it in the collections in an single element array? (Actually, decided to try this quickly before I posted, no documents returned when the date entry in the document is stored in a single element array)
Document entry example
{ "_id" : ObjectId("52aea5b0065991de1a56d5b0"), "d" : ISODate("2013-12-15T00:00:11.088Z"), "t" : 1501824, "s" : 0, "e" : 601, "tld" : "uk", "y" : "domain:check", "n" : "removed.co.uk" }
Is anyone able to give me some advice as to how I should do this query? Thank you.
EDIT: I left the Java tag here in case anyone needs my DBObject creation code, though it shouldn't be necessary as the queries posted have been generated by my build.
EDIT2: So as Alan Spencer pointed out I should be using $or rather than $in, a working $or function is below (ignore the different formatting like the use of ISODate(), its just copy pasted from the mongo shell rather than getting output from my program)
{ $match : { $or : [ { d : { $gte : ISODate("2013-10-01T00:00:00.000Z"), $lt : ISODate("2013-10-02T00:00:00.000Z") } }, { d : { $gte : ISODate("2013-10-03T00:00:00.000Z"), $lt : ISODate("2013-10-04T00:00:00.000Z") } } ] } }
I think you're inverting the meaning of the $in.
$in is used to match exactly against a list of possible values, like
{"color":{"$in": ["red","green","blue"]}}
For your use case, you are trying to match if it satisfies the first or second, etc. So, you can use $or - http://docs.mongodb.org/manual/reference/operator/query/or/
{ "$match" : { "d" : { "$or" : [ { "$gte" : { "$date" : "2013-10-01T00:00:00.000Z"} , "$lt" : { "$date" : "2013-10-02T00:00:00.000Z"}} , { "$gte" : { "$date" : "2013-10-03T00:00:00.000Z"} , "$lt" : { "$date" : "2013-10-04T00:00:00.000Z"}}]}}}

Mongodb: Can't save a new element added into BasicDBList

I am trying to add an element to a BasicDBList array and save it back to Mongodb, but when I check the result, it is not written. What did I do wrong? I use java driver version 2.7.2.
DBObject dbObject = coll.findOne(dbQuery);
BasicDBList unreadMsgs = (BasicDBList) dbObject.get("unreadMsgs");
Logger.debug("before incrementing unreadMsgs" + dbObject.toString());
unreadMsgs.add(new BasicDBObject("id", 1).append("unreadMsg", 1));
Logger.debug("after incrementing unreadMsgs : " + dbObject.toString());
coll.save(dbObject);
Logger.debug("check result: " + coll.findOne(dbQuery).toString());
before incrementing unreadMsgs{ "_id" : { "$oid" : "515c5eb88e3278e9c9d55867"} , "unreadMsgs" : [ ]}
after incrementing unreadMsgs : { "_id" : { "$oid" : "515c5eb88e3278e9c9d55867"} , "unreadMsgs" : [ { "id" : 1 , "unreadMsg" : 1}]}
check result: { "_id" : { "$oid" : "515c5eb88e3278e9c9d55867"} , "unreadMsgs" : [ ]}
The problem is that the coll.save(dbObject) is not updating anything.
It works as an insert and, since the _id already exists in the collection, you are getting a duplicateKey exception (you are just not seeing it because of configuration).
You have to use an update, here is how
The save call should work on that case, but I suggest you use an update with $addToSet operation.
Here's the code:
DBObject addToSetObj = BasicDBObjectBuilder.start()
.push("$addToSet")
.push("unreadMsgs")
.add("id", 1)
.add("unreadMsg", 1)
.pop()
.pop()
.get();
// addToSetObj will be { "$addToSet" : { "unreadMsgs" : { "id" : 1 , "unreadMsg" : 1}}}
coll.update(dbQuery, addToSetObj);
Logger.debug("check result: " + coll.findOne(dbQuery).toString());
Any doubts on how to use addToSet, check this out: http://docs.mongodb.org/manual/reference/operator/addToSet/
Thanks for everybody's answer. I found the real problem. It turns out that my collection is capped, and I am not allowed to insert more data into an existing document in a capped collection. I saw the exception after I changed WriteConcern to FSYNC_SAFE. I changed all my collections to uncapped, and the code works now.

Categories