confusion regarding embedded BSONDocument in reactiveMongo - mongodb

I have stored following data in MongoDB
db.users.insert({id: 1,user: {firstname:"John",lastname:"Cena",email:["jc#wwe.com","jc1#wwe.com"],password:"YouCantSeeMe",address:{street:"34 some street", country:"USA"}}})
I queried as follows expecting that the first query will not work but the second will. To my surprise, it was the other way round.
This query worked
val query1 = BSONDocument("user.firstname"->user.firstName)
This didn't
val query2 = BSONDocument("user"-> BSONDocument("firstname"->user.firstName))
I observed that query1 creates following structure (by running mongodb in verbose mode, mongodb -v)
{ user.firstname: "John" }
But query2 creates following structure
{ user: { firstname: "John" } }
Aren't these two the same (firstname is inside user)?

They are not the same.
First query works because you are comparing the fields of embedded document using dot notation.
Second query fails because you are comparing document as a whole against a embedded document.
https://docs.mongodb.com/manual/tutorial/query-embedded-documents/
https://docs.mongodb.com/manual/core/document/#dot-notation

Related

Retrieving a single field from MongoDB using Selenium/Java

I'm just switching from MySQL to MongoDB and it's a little confusing. We have our database stored in MongoDB and using Java-Selenium in the front end. I am trying to retrieve just one single data from the database. The code below retrieves all the data present in the database:
DBCursor cursor = dbCollection.find();
while(cursor.hasNext())
{
int i=1;
System.out.println(cursor.next());
i++;
}
This is my database lets say:
{
"name" : "Su_123",
"email" : "test#gmail.com",
"_id" : ObjectId("12345656565656")
}
I want to retrieve just the email field (test#gmail.com) from the document where _id = ObjectId("12345656565656") and store this in a String field.
How do I go about coding this? find() retrieves the entire row.
For newer drivers, since 3.7.1
To get specific document that matches a filter:
Document doc = collection.find(eq("email", "test#gmail.com")).first();
It can be used to find the first document where the field email has the value test#gmail.com. And pass an eq filter object to specify the equality condition.
By the same logic using id:
Document document = myCollection.find(eq("_id", new ObjectId("12345656565656"))).first();
To get specific value of the field from selected document:
String value = (String) doc.get("email");
For older drivers like 2.14.2 and 2.13.3
To get a single document with a query:
BasicDBObject query = new BasicDBObject("email", "test#gmail.com");
cursor = coll.find(query);
try {
while(cursor.hasNext()) {
//System.out.println(cursor.next());
}
} finally {
cursor.close();
}
To see more details for newer Mongodb Java driver.
To see more details for older Mongodb Java driver.
To see more details from Mongodb Official Docs.

mongo find all with field that is object having a specified key

A mongo db has documents that look like:
{
"_id": : ObjectId("55cb43e8c78b04f43f2eb503"),
<some fields>
"topics": {
"test/23/result": 149823788,
"test/27/result": 147862733,
"input/misc/test": 14672882
}
}
I need to find all documents that have a topics field that contains a particular key. i.e. find all documents that have a topics.key = "test/27/result"
I've tried a number of things but none work yet, neither attempt below work,
they return no records event though some should match:
db.collName.find({"topics.test/27/result": {$exists:true}});
db.collName.find({"topics.test\/27\/result": {$exists:true}});
How can I make the query work?
The slash characters are inserted by another process. They are mqtt topic names.
I found the solution to my problem:
I was building the query wrong in my code. In the example below, evtData.source contains the key name to search for, i.e. 'test/27/result'
The query methodology that works for me is:
var query = {};
query['topics.' + evtData.source] = {$exists: true};
db.collName.find(query)

Include score in Morphia full text search

I am trying to use the MongoDB full text indices in Morphia. I need to return the score for each documents as well as have the results sorted. This is what my query looks like without Morphia:
db.getCollection('disease').find( { $text: { $search: "brain" } },
{ score: { $meta: "textScore" } } )
.sort( { score: { $meta: "textScore" } } )
This works correctly and returns hits sorted by score.
I also can do this using the MongoDB Java driver directly without Morphia.
// search with the Java driver
BasicDBObject textSearch = new BasicDBObject("$search", "brain");
BasicDBObject search = new BasicDBObject("$text", textSearch);
BasicDBObject meta = new BasicDBObject("$meta", "textScore");
BasicDBObject score = new BasicDBObject("score", meta);
List<DBObject> diseases = collection.find(search, score).sort(score).toArray();
Assert.assertEquals(2, diseases.size());
Assert.assertEquals("brain", diseases.get(0).get("name"));
Assert.assertEquals("benign-brain", diseases.get(1).get("name"));
I can't figure out how to accomplish the same thing in Morphia. Here is the example from the Morphia documentation (http://mongodb.github.io/morphia/1.0/guides/querying/#text-searching):
List<Greeting> good = datastore.createQuery(Greeting.class)
.search("good")
.order("_id")
.asList();
Assert.assertEquals(4, good.size());
The example does not return score and is ordering by "_id". I don't see any way to handle the $meta operator in Morphia. Has anyone done something similar?
Because Morphia maps back to your type, the only way to expose the score in this situation would be to add a score field to your entity type and map it back to that field. This isn't awesome because it starts to pollute your business types with database metadata fields. You could always try mapping back out a special value object type containing whatever metadata you'd like. That would at least keep your business objects free of this metadata.
Following the advice from #evanchooly and the OP, I was able to resolve my version of this issue as follows:
first I created the query anlong with the search.
Datastore morphiaDS = ...;
Query<myMorphiaModel> query = morphiaDS.createQuery(myMorphiaModel.class)
.field("helloField").equal("world")
.search("yadayadayada"); // Search performs a text search
Next, I transitioned to using the direct Java Mongo drivers. It seems right now, while this issue by #evanchooly is still open, We'll need to use mongo drivers and can't go pure morphia.
BasicDBObject meta = new BasicDBObject("$meta", "textScore");
BasicDBObject score = new BasicDBObject("score", meta);
List<DBObject> results = query.getCollection()
.find(query.getQueryObject(), score)
.sort(score).toArray();
Finally I converted the list of generalized objects to my original morphia model
Morphia morphia = mongoService.getMorphia();
List<myMorphiaModel> searchDocs = results.stream()
.map((result) -> morphia.fromDBObject(myMorphiaModel.class, result))
.collect(Collectors.toList());
One troubleshooting issue to watch out for is that I had to make sure I had 'score' represented in the find, not just in the sort.
Hope this clarifies the helpful answers that others have presented....
Looks like this issue has been fixed with the commit https://github.com/mongodb/morphia/commit/af4d64f6de3c0b1437dd216f5762d03bf98cdcb0 and now you can just do:
List<Greeting> good = datastore.createQuery(Greeting.class)
.search("good")
.project(Meta.textScore("score"))
.order(Meta.textScore("score"))
The only caveat now is that as the project on score is mandatory to be able to sort by score, if no other projections are added, the result just contains the score field. So, projections for all needed fields have to be added into the query.
Hope this helps.

spring data mongo - mongotemplate count with query hint

The mongo docs specify that you can specify a query hint for count queries using the following syntax:
db.orders.find(
{ ord_dt: { $gt: new Date('01/01/2012') }, status: "D" }
).hint( { status: 1 } ).count()
Can you do this using the mongo template? I have a Query object and am calling the withHint method. I then call mongoTemplate.count(query); However, I'm pretty sure it's not using the hint, though I'm not positive.
Sure, there are a few forms of this including going down to the basic driver, but assuming using your defined classes you can do:
Date date = new DateTime(2012,1,1,0,0).toDate();
Query query = new Query();
query.addCriteria(Criteria.where("ord_dt").gte(date));
query.addCriteria(Criteria.where("status").is("D"));
query.withHint("status_1");
long count = mongoOperation.count(query, Class);
So you basically build up a Query object and use that object passed to your operation, which is .count() in this case.
The "hint" here is the name of the index as a "string" name of the index to use on the collection. Probably something like "status_1" by default, but whatever the actual name is given.

MongoDB: Retrieving the first document in a collection

I'm new to Mongo, and I'm trying to retrieve the first document from a find() query:
> db.scores.save({a: 99});
> var collection = db.scores.find();
[
{ "a" : 99, "_id" : { "$oid" : "51a91ff3cc93742c1607ce28" } }
]
> var document = collection[0];
JS Error: result is undefined
This is a little weird, since a collection looks a lot like an array. I'm aware of retrieving a single document using findOne(), but is it possible to pull one out of a collection?
The find method returns a cursor. This works like an iterator in the result set. If you have too many results and try to display them all in the screen, the shell will display only the first 20 and the cursor will now point to the 20th result of the result set. If you type it the next 20 results will be displayed and so on.
In your example I think that you have hidden from us one line in the shell.
This command
> var collection = db.scores.find();
will just assign the result to the collection variable and will not print anything in the screen. So, that makes me believe that you have also run:
> collection
Now, what is really happening. If you indeed have used the above command to display the content of the collection, then the cursor will have reached the end of the result set (since you have only one document in your collection) and it will automatically close. That's why you get back the error.
There is nothing wrong with your syntax. You can use it any time you want. Just make sure that your cursor is still open and has results. You can use the collection.hasNext() method for that.
Is that the Mongo shell? What version? When I try the commands you type, I don't get any extra output:
MongoDB shell version: 2.4.3
connecting to: test
> db.scores.save({a: 99});
> var collection = db.scores.find();
> var document = collection[0];
In the Mongo shell, find() returns a cursor, not an array. In the docs you can see the methods you can call on a cursor.
findOne() returns a single document and should work for what you're trying to accomplish.
So you can have several options.
Using Java as the language, but one option is to get a db cursor and iterate over the elements that are returned. Or just simply grab the first one and run.
DBCursor cursor = db.getCollection(COLLECTION_NAME).find();
List<DOCUMENT_TYPE> retVal = new ArrayList<DOCUMENT_TYPE>(cursor.count());
while (cursor.hasNext()) {
retVal.add(cursor.next());
}
return retVal;
If you're looking for a particular object within the document, you can write a query and search all the documents for it. You can use the findOne method or simply find and get a list of objects matching your query. See below:
DBObject query = new BasicDBObject();
query.put(SOME_ID, ID);
DBObject result = db.getCollection(COLLECTION_NAME).findOne(query) // for a single object
DBCursor cursor = db.getCollection(COLLECTION_NAME).find(query) // for a cursor of multiple objects