MongoDB - find where BinData is equal to UUID value - mongodb

I try to write application in Java and Spring Data MongoDB.
My document looks:
#Data
#ToString
public class SomeDocument {
private UUID id;
private String name;
}
Repository:
#Repository
public interface SomeDocumentMongoRepository extends MongoRepository<SomeDocument, UUID> {
}
It's very simple. And I saved the document:
{
"id": "5f4ac46b-55f7-4be4-b26f-2ca041334bec",
"name": "some name"
}
Then I tried to read it from database using simple query db.someDocument.find() and I've got the result:
{ "_id" : BinData(3,"5Ev3VWvESl/sSzNBoCxvsg=="), "name" : "some name", "_class" : "org.springmongodemo.repo.SomeDocument" }
So my questions are:
How to find in mongo shell document by _id using given UUID?
What does mean number 3 in BinData(3,...)?

To find the document you'd have to encode your UUID into base64. Please see this question.
"3" is the identifier for legacy UUID data format. For more please see here.
If you want to query from in-app you should use Spring Data's features, but from your question I assume you want to know how to query using mongo native tools.

Related

mongodb pojo codec conversion missing id field

I have a following document structure in mongo
{
"_id" : 4771902,
"upc" : "test-upc-v1",
"reportingCategory" : {
"id" : 14,
"department" : "Footwear"
}
}
My java class looks like
public class Product {
private Long _id;
private String upc;
private ReportingCategory reportingCategory;
}
public class ReportingCategory {
private Long id;
private String department;
}
I am using mongo pojo codec for conversion. "id" field under ReportingCategory is being returned as null.
Rest every other data is available. I can see that data when I convert it into RawBsonDocument, but seems like it gets lost in pojo conversion.
"id" field has no index on it, and is not used to uniquely identify this document.
Has anyone faced something similar and any work around for it ?
P.S. I am using mongo 3.6, with 3.6 async driver.
This is indeed a feature/bug in mongodb java driver.
Anyone looking for a reason and a solution for this can find one here https://jira.mongodb.org/browse/JAVA-2750

How to update document in mongo to get performance?

I am new to Spring Data Mongo. I've a scenario where I want to create a Study if already not present in mongo db. If its already present, then I've to update it with the new values.
I tried in the following way, which works fine in my case, but I'm not sure this is the correct/Best/Advisable way to update etc as far as performance is concerned.
Could anyone please guide on this?
public void saveStudy(List<Study> studies) {
for (Study study : studies) {
String id = study.getId();
Study presentInDBStudy = studyRepository.findOne(id);
//find the document, modify and update it with save() method.
if(presentInDBStudy != null) {
presentInDBStudy.setTitle(task.getTitle());
presentInDBStudy.setDescription(study.getDescription());
presentInDBStudy.setStart(study.getStart());
presentInDBStudy.setEnd(study.getEnd());
repository.save(presentInDBStudy);
}
else
repository.save(study);
}
}
You will have to use the MongoTemplate.upsert() to achieve this.
You will need to add two more classes: StudyRepositoryCustom which is an interface and a class that extends this interface, say StudyRepositoryImpl
interface StudyRepositoryCustom {
public WriteResult updateStudy(Study study);
}
Update your current StudyRepository to extend this interface
#Repository
public interface StudyRepository extends MongoRepository<Study, String>, StudyRepositoryCustom {
// ... Your code as before
}
And add a class that implements the StudyRepositoryCustom. This is where we will #Autowire our MongoTemplate and provide the implementation for updating a Study or saving it if it does not exist. We use the MongoTemplate.upsert() method.
class StudyRepositoryImpl implements StudyRepositoryCustom {
#Autowired
MongoTemplate mongoTemplate;
public WriteResult updateStudy(Study study) {
Query searchQuery = new Query(Criteria.where("id").is(study.getId());
WriteResult update = mongoTemplate.upsert(searchQuery, Update.update("title", study.getTitle).set("description", study.getDescription()).set(...)), Study.class);
return update;
}
}
Kindly note that StudyRepositoryImpl will automatically be picked up by the Spring Data infrastructure as we've followed the naming convention of extending the core repository interface's name with Impl
Check this example on github, for #Autowire-ing a MongoTemplate and using custom repository as above.
I have not tested the code but it will guide you :-)
You can use upsert functionality for this as described in mongo documentation.
https://docs.mongodb.com/v3.2/reference/method/db.collection.update/
You can update your code to use <S extends T> List<S> save(Iterable<S> entites); to save all the entities. Spring's MongoRepository will take care of all possible cases based on the presence of _id field and its value.
More information here https://docs.mongodb.com/manual/reference/method/db.collection.save/
This will work just fine for basic save operations. You don't have to load the document for update. Just set the id and make sure to include all the fields for update as it updates by replacing the existing document.
Simplified Domain Object:
#Document(collection = "study")
public class Study {
#Id
private String id;
private String name;
private String value;
}
Repository:
public interface StudyRepository extends MongoRepository<Study, String> {}
Imagine you've existing record with _id = 1
Collection state before:
{
"_id" : 1,
"_class" : "com.mongo.Study",
"name" : "saveType",
"value" : "insert"
}
Run all the possible cases:
public void saveStudies() {
List<Study> studies = new ArrayList<Study>();
--Updates the existing record by replacing with the below values.
Study update = new Study();
update.setId(1);
update.setName("saveType");
update.setValue("update");
studies.add(update);
--Inserts a new record.
Study insert = new Study();
insert.setName("saveType");
insert.setValue("insert");
studies.add(insert);
--Upserts a record.
Study upsert = new Study();
upsert.setId(2);
upsert.setName("saveType");
upsert.setValue("upsert");
studies.add(upsert);
studyRepository.save(studies);
}
Collection state after:
{
"_id" : 1,
"_class" : "com.mongo.Study",
"name" : "saveType",
"value" : "update"
}
{
"_id" : 3,
"_class" : "com.mongo.Study",
"name" : "saveType",
"value" : "insert"
}
{
"_id" : 2,
"_class" : "com.mongo.Study",
"name" : "saveType",
"value" : "upsert"
}

Spring Data MongoDB field projection problems

I use spring data mongodb repository like this
public interface StringFieldSelectRepository extends MongoRepository<String, ObjectId> {
#Query(value="{}", fields="{'key':1}")
Collection<String> allKeys();
}
My Document spec like this...
{
_id : 1236f9391239,
key : "aa77mykeys",
name : "myname",
num : 389
}
I intend to extract only key field. I want return like this when I call allKeys();
["myKey1","myKey2","etc"...]
Is it impossible? Thanks for your help

Morphia problems during filter arrays by date

I have those classes:
#Entity
public class Event implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private ObjectId id;
#Embedded
private List<Edition> editions;
...
}
and
public class Edition implements Serializable {
private static final long serialVersionUID = 1L;
private Date schedule;
...
}
I am trying search an Event and filter by an date less than edition schedule.
For instance, if I have an collection such:
{
id : <ObjectID>,
editions : [
{schedule : 2014-1-1},
{schedule : 2014-1-2}
]
}
If I search by 2014-1-3 I expect no results.
If I search by 2014-1-1 the result that I am expecting is:
{
id : <ObjectID>,
editions : [
{schedule : 2014-1-2}
]
}
If I search by 2013-12-31 the result I am expecting is:
{
id : <ObjectID>,
editions : [
{schedule : 2014-1-1},
{schedule : 2014-1-2}
]
}
My problem is with the last case.
Query<Event> query = this.basicDAO.createQuery();
query = query.filter("editions.schedule >", <start date>);
return this.basicDAO.find(query).retrievedFields(true, "editions.$").asList();
When I try that last query I receive just one edition, however, those two match with clause. There is some clause to tell that I want all registries which match and not only the first one?
As stated in the documentation, the projection operator only finds the first element matching.
In general, is your document structure really the best fit for your requirements? Specifically, do you need to embed the Editions? MongoDB will always read the full Event document (fetch it from RAM or disk). So if you are frequently fetching the Edition only, embedding is probably the wrong approach.
In case embedding is required, because you are frequently requiring the full Event, I'm afraid the easiest approach is to fetch the full document and put together the right pieces of information in your application. This adds some network and application overhead.

MongoDB schema design - finding the last X comments across all blog posts filtered by user

I am trying to reproduce the classic blog schema of one Post to many Comments using Morphia and the Play Framework.
My schema in Mongo is:
{ "_id" : ObjectId("4d941c960c68c4e20d6a9abf"),
"className" : "models.Post",
"title" : "An amazing blog post",
"comments" : [
{
"commentDate" : NumberLong("1301552278491"),
"commenter" : {
"$ref" : "SiteUser",
"$id" : ObjectId("4d941c960c68c4e20c6a9abf")
},
"comment" : "What a blog post!"
},
{
"commentDate" : NumberLong("1301552278492"),
"commenter" : {
"$ref" : "SiteUser",
"$id" : ObjectId("4d941c960c68c4e20c6a9abf")
},
"comment" : "This is another comment"
}
]}
I am trying to introduce a social networking aspect to the blog, so I would like to be able to provide on a SiteUser's homepage the last X comments by that SiteUser's friends, across all posts.
My models are as follows:
#Entity
public class Post extends Model {
public String title;
#Embedded
public List<Comment> comments;
}
#Embedded
public class Comment extends Model {
public long commentDate;
public String comment;
#Reference
public SiteUser commenter;
}
From what I have read elsewhere, I think I need to run the following against the database (where [a, b, c] represents the SiteUsers) :
db.posts.find( { "comments.commenter" : {$in: [a, b, c]}} )
I have a List<SiteUser> to pass in to Morphia for the filtering, but I don't know how to
set up an index on Post for Comments.commenter from within Morphia
actually build the above query
Either put #Indexes(#Index("comments.commenter")) on the Post class, or #Indexed on the commenter field of the Comment class (Morphia's Datastore.ensureIndexes() will recurse in the classes and correctly create the comments.commenter index on the Post collection)
I think ds.find(Post.class, "comments.commenter in", users) would work, ds being a Datastore and users your List<SiteUser> (I don't use #Reference though, so I can't confirm; you might have to first extract the list of their Keys).