Query for binary data - MongoDB - java

I'm using Java Driver for MongoDB (2.14) in my application.
I have these documents:
{ "_id" : ObjectId("56fb9798e2445ade35effa89"), "b" : BinData(3,"abcdefgh") }
{ "_id" : ObjectId("56fba265e2445ade35effa8c"), "b" : 1 }
I have to retrieve all documents where "b" is a binary data using Java.
To reach my target I use the following query:
DBObject query = new BasicDBObject(b, new BasicDBObject("$type",5));
DBObject projKeys = new BasicDBObject();
projKeys.put("_id", 0);
projKeys.put(b, 1);
DBCursor cursor = coll.find(query,projKeys);
But when I start to iterate over cursor I get an exception:
java.lang.IllegalArgumentException: bad data size subtype 3 len: 6 != 16
When I try to make the same query using mongo shell, that is:
db.coll.find({b:{"$type":5}}, {_id:0,b:1})
I don't have errors.

Binary subtype 3 is reserved for UUID, which has a "strict" 16 byte length ( 32 string elements in hex notation ). Hence the error you are getting in your Java code.
The MongoDB shell does not have this "strict" typing, and as such both allows the creation and reading of the data. Nor is MongoDB itself "strictly typed", so as far as the engine is concerned it's just BSON Type 5, and does not look at it further.
If you inserted documents either use the correct data for the subtype:
{ "b": BinData(3,"ASNFZ4mrze/+3LqYdlQyEA==") }
Or a corrected subtype suiting the data, such a 0:
{ "b": BinDta(0,"abcdefgh") }
Then the Java driver has no problem when marshalling into it's Binary type.
So you get the error because your "data" is "invalid". Correct the data and there is no problem.

Related

How to delete mongodb document with two conditions one with $gt operator?

I would like to retrieve the following information:
delete from database where name = 'AAA' and age>20;
but for MongoDB in Java. Essentially, it should delete the document that contain the word AAA and age greater than 20 in them. I know that there is the $in operator in MongoDB, but how do I do the same in Java, using the Java driver? I've been trying to look for it everywhere but am getting nothing. I've tried:
query = new BasicDBObject("age", new BasicDBObject("$gt", "20"), new BasicDBObject("name", "AAA"));
JSON which i want to delete is like this.
{"school" : "NewSchool" , "name" : "AAA" , "age" : "50"}
What you want is the find-term:
{
"name" : "AAA",
"age" : { $gt : 20 }
}
Construct this as your basic DB object, or simply use the new 3.x Filters to create the Bson for you. (As I personally only use 3.x, here's the appropriate example):
MongoClient client = ...
MongoDatabase db = client.getDatabase(...);
MongoCollection<Document> coll = db.getCollection(...);
coll.deleteMany(Filters.and(Filters.eq("name", "AAA"), Filters.gt("age", 20)));

MongoDB querying with Java API

Here is the sample document of my MongoDB:
user:{
_id:1,
name:'xyz',
age:12,
mobile:21321312,
transaction:[{
trans_id:1,
prod:'a',
purchasedAt:ISODate("2015-02-01"),
},
{
trans_id:2,
prod:'b',
purchasedAt:ISODate("2015-02-01")
},
{
trans_id:3,
prod:'c',
purchasedAt:ISODate("2014-11-24")
}]
,...
}
My query looks like:
db.user.find({transaction:{$elemMatch:{prod:'a', purchasedAt:ISODate("2015-02-01")}}, transaction:{$elemMatch:{prod:{$nin:['b','c']}, purchasedAt:ISODate("2015-02-01")}}}).count()
I am trying to get the user count who have purchased product 'a' on date "2015-02-01" but not have purchased product b & c on same day.
So while trying to do this in Java with the query:
coll.find(new BasicDBObject().append("transaction", new BasicDBObject("$elemMatch", new BasicDBObject("prod", 'a').append("purchasedAt", Date))).append("transaction", new BasicDBObject("$elemMatch", new BasicDBObject("prod", new BasicDBObject("$nin",['b','c'])).append("purchasedAt", Date)));
I have also tried:
coll.find(new BasicDBObject("transaction", new BasicDBObject("$elemMatch", new BasicDBObject("prod", 'a').append("purchasedAt", Date))).append("transaction", new BasicDBObject("$elemMatch", new BasicDBObject("prod", new BasicDBObject("$nin",['b','c'])).append("purchasedAt", Date)));
where Date is "2015-02-01" in util.Date object.
I found out that Java ignores the $in part of the query, i.e. it ignores {transaction:{$elemMatch:{prod:'a', purchasedAt:ISODate("2015-02-01")}} & performs only $nin part.
I found out it by DBCursor object.
Here's the output of the cursor:
Cursor: Cursor id=0, ns=mydb.user, query={ "transaction" : { "$elemMatch" : { "prod" : { "$nin" : [ "b" , "c"]} , "purchasedAt" : { "$date" : "2015-02-01T00:00:00.000Z"}}}}, numIterated=0, readPreference=primary
Because of this my result is inaccurate. I wonder why the exact same query works well in Mongo shell but doesn't with Java API. Is there anything wrong with my query structure?
My guess is that this question is now moot, but, if you still do not consider it answered, are you looking for the "$not" operator, which can check for non-existance sort of.

How to bind an hstore[] value in Java

I'm trying to store people's telephone numbers and addresses in a database table. I would like to support multiple phone numbers and addresses and expect the format to be different in different countries. I've decided to use hstore to allow that flexibility and allow efficient querying by specific fields. As it stands I can receive the values from database, but could not find a way to insert them from Java. The table (simplified) looks like that:
CREATE TABLE IF NOT EXISTS contacts ( "
+ "id uuid NOT NULL, "
+ "title character varying NOT NULL DEFAULT '', "
+ "first_name character varying NOT NULL, "
+ "last_name character varying NOT NULL, "
+ "phones hstore[] NOT NULL DEFAULT '{}', "
+ "addresses hstore[] NOT NULL DEFAULT '{}')"
I have created a custom JDBI Binder to bind the values, but however I try I can't get the statement to execute. Currently Binder code snippet looks like this:
#Override
public void bind(SQLStatement<?> q, BindContactBean bind, ContactBean bean) {
q.bind("phones",
getHstoreArray(q, PhoneDetailMapper.toMapArray(bean.phones.get())));
q.bind("addresses",
getHstoreArray(q, AddressDetailMapper.toMapArray(bean.addresses.get())));
The getHstoreArray function is a helper that converts java Array into SQL array and looks like this:
private Array getHstoreArray(SQLStatement<?> q, Map<String, String>[] map) {
try {
return q.getContext().getConnection().createArrayOf("hstore", map);
} catch (SQLException e) {
throw new IllegalArgumentException(e);
}
}
I think the problem is in encoding of the data. For example, for data (in JSON notation for simplicity)
{
"firstName": "Maximum",
"lastName": "Details",
"status": "active",
"phones": [{
"type": "mobile",
"number": "0777 66 55 44"
}]
}
the query is expanded to:
INSERT INTO contacts (
id, first_name, last_name, status, phones )
VALUES ( '9be1a040-b408-11e3-bb43-00231832fa86', 'Maximum', 'Details', 4,
'{"{type=mobile, extracted_number=extracted, number=07777 66 55 44}"}'
)
and if I try to run it from PGAdmin's SQL editor the error returned is:
ERROR: Syntax error near 'm' at position 6
LINE 5: '{"{type=mobile, extracted_number=extracted, number=07777 66...
^
********** Error **********
ERROR: Syntax error near 'm' at position 6
SQL state: XX000
Character: 179
I have considered using JSON instead of hstore[], but that would make querying by specific fields slower and less accurate (essentially a text search) and I'd rather avoid it.
Another option I tried before hstore is array of UDT, but couldn't even get it to read from database without writing parser for PGobject which doesn't look like a simple task.
EDIT
I had a look at the data in the database and when escaped in the following way:
'{"\"type\"=>\"mobile\", \"number\"=>\"07777 66 55 44\", \"extracted_number\"=>\"777665544\""}'
I can run query manually from SQL editor, but still no luck in Java.
I have found a solution, there is a class available in Postgres driver called HStoreConverter which can convert Map to String literal. Not sure this is the best approach, but it seems to work, modified helper function below.
private Array getHstoreArray(SQLStatement<?> q, Map<String, String>[] maps) {
try {
String[] hstores = new String[maps.length];
for (int i = 0; i < maps.length; i++)
hstores[i] = HStoreConverter.toString(maps[i]);
return q.getContext().getConnection().createArrayOf("hstore", hstores);
} catch (SQLException e) {
throw new IllegalArgumentException(e);
}
}

MongoDB Date query JAVA

Im trying to do a query to get all the values from my DB wich each have a date. One example:
leadTime: [
{
date: ISODate("2014-03-19T23:00:00Z"),
value: 25.8
},
{
date: ISODate("2014-03-20T23:00:00Z"),
value: 31.299999999999997
},
{
date: ISODate("2014-03-21T23:00:00Z"),
value: 34.4
}
]
enter code here
My code is:
DBObject query=new BasicDBObject("group",group);
DBObject serieData= col.findOne(query,new BasicDBObject(serie, 1));
if (serieData != null) {
List<DBObject> data = (List<DBObject>) serieData.get(serie);
for (DBObject item : data) {
result.add(new HistoryData((Date) item.get("date"),(Double) item.get("value")));
}
Now I want to get the values that the date is bigger than a date that I pass by parameter. The query I did is this:
DBObject query=new BasicDBObject("group",group)
.append("date", new BasicDBObject("$gte", parameterDate))
;
But I always receive the result empty, can you help me? sorry for my english and than
Assuming that leadTime is a field in your documents, your query has to look like this
DBObject query=new BasicDBObject("group",group)
.append("leadTime.date", new BasicDBObject("$gte", parameterDate))
;
The way you did it, MongoDB would have searched for a date field in your document root:
{ _id: "Foo",
date: ISODate("2014-03-19T23:00:00Z"),
[...]
}
Queries in MongoDB don't make a difference if the queried field is a single value or an array, so using the dot notation on a field which holds an array of subdocuments is perfectly valid.
What you want to do is not possible with a simple query.
But if you still want to do it in mongodb you need to use the aggregation framework, with something like that :
db.<col_name>.aggregate( [ { $unwind : "$group" }, { $match : {'group.date': { $gte : parameterDate } } ] )
this a js command, but you should be able to translate it easly in Java Driver (you can also add a $project operation to just return needed fields).

MongoDB nested documents searching

How do I search through mongodb documents where documents have nested documents. For example I have a collection of private messages. Each private message has two nested documents - one representing the sending user and the other representing the receiving use. Both nested documents have the form -
userID: 34343,
name: Joe Bloggs
I would like to be able to search for all mail messages sent by a user (e.g. search the sender user nested document).
I am using the java driver. Do I need to create a DBObject which represents the nested document?
Thanks
As i understand u have document structure like this:
{
"someProperty" : 1,
"sendingUser" : {
userID : 34343,
name : "Joe Bloggs"
},
"recivingUser" : {
userID : 34345,
name : "Joe Bloggs"
}
}
So if you need find sending user with userID = 34345 you just need do following(i just think that is so, because actually i am working with c# driver for mongo):
DBCollection coll = db.getCollection("privateMessages")
query = new BasicDBObject();
query.put("sendingUser.userID", new BasicDBObject("$eq", 34345));
cur = coll.find(query); // all documents with sendingUser.userID = 34345 will be //returned by cursor
Also check tutorial for java driver
For MongoDB Java Driver v3.2.2. You can do something like this:
FindIterable<Document> iterable = collection.find(Document.parse("{\"sendingUser.userID\": \"34343\"}"));
FindIterable<Document> iterable = collection.find(Document.parse("{\"sendingUser.name\": \"Joe Bloggs\"}"));
You can put the $eq inside the JSON style query string. Like { <field>: { $eq: <value> } }.

Categories