Morphia. How to clone query with different collection(kind) - mongodb

The problem is about searching by query in different collections.
There is a method in AdvancedDatastore interface:
<T> Query<T> createQuery(String kind, Class<T> clazz, DBObject q);
But its only create query with given baseQuery and I need full clone of Query but with different DBCollection field.
Any suggestions?
Here is my method to convert query:
public Query<Vacancy> convertQuery(Query<T> query) {
QueryImpl<T> queryImpl = (QueryImpl<T>) query;
DBObject dbO = queryImpl.getQueryObject();
Query<T> our_query = ((AdvancedDatastore)this.getDatastore()).createQuery("AnotherCollectionName", T.class, dbO);
return our_query;
}
Update
Works fine with reflection, but I dont like this dirty way because of perfomance.

That looks like a good solution for now. You cannot change the underlying collection/kind once a query is created -- it is immutable.
If you want this functionality one would need to implement a deep clone operator for the Query/QueryImpl or request that be done in morphia.

Related

How to add default criteria to all the queries by default in mongo spring boot

I have a requirement to soft delete documents in a given MongoDB collection. For that, I use a boolean called deleted. So now when I am retrieving data from the database, I have to always mention taking the data where the deleted=false.
Eg:
public Organization findOrgById(String id) {
Query query = new Query();
Criteria criteria = Criteria.where(Constants.ENTITY_ID).is(id)
.and(Constants.DELETED).is(false);
query.addCriteria(criteria);
Organization res = mongoTemplate.findOne(query, Organization.class);
return res;
}
Is there a way to specify that always all the criteria to add deleted=false by default without mentioning it in the code itself?
In Hibernate core there is an annotation #Where but it is not working with mongo documents.
I think the best way to do this is extend the MongoTemplate class that will add your deleted=false condition to all Find queries.
Here is an example of how to do it with one method used to execute findOne queries:
public class ExtendedMongoTemplate extends MongoTemplate {
private static final Document DELETED_CRITERIA_DOC = Criteria.where(Constants.DELETED).is(false)
.getCriteriaObject();
#Override
protected <T> T doFindOne(
String collectionName,
Document query,
Document fields,
CursorPreparer preparer,
Class<T> entityClass) {
query.putAll(DELETED_CRITERIA_DOC);
return super.doFindOne(collectionName, query, fields, preparer, entityClass);
}
...
}
This method is called in the method doFindOne(Query query, Class<?> entityClass) (the last one delegates executing
Other methods to override are:
protected <S, T> List<T> doFind(String collectionName, Document query, Document fields,
Class<S> entityClass, CursorPreparer preparer);
protected <T> T doFindAndRemove(String collectionName, Document query, Document fields,
Document sort, #Nullable Collation collation, Class<T> entityClass);
protected <T> T doFindAndModify(String collectionName, Document query, Document fields, Document sort,
Class<T> entityClass, UpdateDefinition update, #Nullable FindAndModifyOptions options);
protected <T> T doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
Document mappedSort, com.mongodb.client.model.Collation collation, Class<?> entityType,
Document replacement, FindAndReplaceOptions options, Class<T> resultType);
These methods execute queries at low-level, so they accept BSON-documents with the query criteria, not Spring's criteria. If you do this, the Find-methods will add an additional criteria to all you queries.
You also can override methods findOne, find, findAndModify and so on in a similar manner, but there are a lot of these methods that all use doFind* methods. Thus overriding doFind* will lead to work with all Find-queries. And don't forget override also findById (it also uses doFindOne internally).
By the way, #Where annotation is from Hibernate, but Spring Data Mongo doesn't use them. It requires its own annotations to work with your entities.

Applying query filter like the one in codelab (Cloud Firestore) is not working

I'm using firestore for my project to store data , my issue is that i can not
filter data just the way codelab filter does .The result of that query gives me the whole collections docs which means the filter is not working.
I've tried many workarounds and find out that only when I cascade whereEqualto()
methods in one line I get what I want of specific docs , but this approach (cascading whereEqualTo inline) is not flexible when giving the user a way to filter searches.I just want to know why that code is not working for me.
// Does not work
Query query = mFirestore.collection("restaurants");
// Category (equality filter)
if (filters.hasCategory()) {
query = query.whereEqualTo("category", filters.getCategory());
}
// City (equality filter)
if (filters.hasCity()) {
query = query.whereEqualTo("city", filters.getCity());
}
This works:
query.whereEqualTo("realEstateType", realEstateType).whereEqualTo("propertyStatus", propertyStatus).whereEqualTo("propertyPhysicalStatus", propertyPhysicalConditionS).addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
}
}
});
I figured it out ,It seems that I was not assigning to query object the filter as follows:
query = query.whereEqualTo("fieldPath" , ...);

spring-data mongodb exclude fields from update

How to make sure that specific fields can be inserted upon creation, but can optionally be excluded when updating the object.
I'm essentially looking for something like the following:
mongoOperations.save(theObject, <fields to ignore>)
From what I see, recently introduced #ReadOnlyProperty will ignore the property both for inserts and updates.
I was able to get the desired behavior by implementing my Custom MongoTemplate and overriding its doUpdate method as follows:
#Override
protected WriteResult doUpdate(String collectionName, Query query,
Update originalUpdate, Class<?> entityClass, boolean upsert, boolean multi) {
Update updateViaSet = new Update();
DBObject dbObject = originalUpdate.getUpdateObject();
Update filteredUpdate = Update.fromDBObject(dbObject, "<fields to ignore>");
for(String key : filteredUpdate.getUpdateObject().keySet()){
Object val = filteredUpdate.getUpdateObject().get(key);
System.out.println(key + "::" + val);
updateViaSet.set(key, filteredUpdate.getUpdateObject().get(key));
}
return super
.doUpdate(collectionName, query, updateViaSet, entityClass, upsert, multi);
}
But the issue is that now it will use Mongo $set form of updates for everything, not just for specific cases.
Please advise if there is any simpler (and correct) way to achieve this.
While creating an update object, use $setOnInsert instead of $set. It is available in spring mongo as well.
If an update operation with upsert: true results in an insert of a document, then $setOnInsert assigns the specified values to the fields in the document. If the update operation does not result in an insert, $setOnInsert does nothing.

TypedQuery<x> returns vector of Object[] instead of list of x-type object

I have a method:
public List<Timetable> getTimetableTableForRegion(String id) {
List<Timetable> timetables;
TypedQuery<Timetable> query = em_read.createQuery("SELECT ..stuff.. where R.id = :id", Timetable.class).setParameter("id", Long.parseLong(id));
timetables = query.getResultList();
return timetables;
}
which returns this:
so, what am I missing in order to return a list of Timetable's?
ok, so, ..stuff.. part of my JPQL contained an inner join to other table. Even through in SELECT there were selected fields just from one table, which was used as type - Timetable, Eclipslink was unable to determine if this fields are part of that entity and instead of returning list of defined entity returned list of Object[].
So in conclusion: Use #OneToMany/#ManyToOne mappings (or flat table design) and query just for ONE table in your JPQL to be able to typize returned entities.
Not sure it might be something is looking for, but I had similar problem and converted Vector to ArrayList like this:
final ArrayList<YourClazz> results = new ArrayList<YourClazz>();;
for ( YourClazzkey : (Vector<YourClazz>) query.getResultList() )
{
results.add(key);
}
i have faced the same problem. and my entity has no one to one or one to many relationship. then also jpql was giving me queryresult as vector of objects. i changed my solution to query to criteria builder. and that worked for me.
code snippet is as below:
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Timetable> criteria = builder.createQuery(Timetable.class);
Root<Enumeration> root = criteria.from(Timetable.class);
criteria.where(builder.equal(root.get("id"), id));
List<Timetable> topics = this.entityManager.createQuery(criteria) .getResultList();
return topics;

How do I get the date a MongoDB collection was created using MongoDB C# driver?

I need to iterate through all of the collections in my MongoDB database and get the time when each of the collections was created (I understand that I could get the timestamp of each object in the collection, but I would rather not go that route if a simpler/faster method exists).
This should give you an idea of what I'm trying to do:
MongoDatabase _database;
// code elided
var result = _database.GetAllCollectionNames().Select(collectionName =>
{
_database.GetCollection( collectionName ) //.{GetCreatedDate())
});
As far as I know, MongoDB doesn't keep track of collection creation dates. However, it's really easy to do this yourself. Add a simple method, something like this, and use it whenever you create a new collection:
public static void CreateCollectionWithMetadata(string collectionName)
{
var result = _db.CreateCollection(collectionName);
if (result.Ok)
{
var collectionMetadata = _db.GetCollection("collectionMetadata");
collectionMetadata.Insert(new { Id = collectionName, Created = DateTime.Now });
}
}
Then whenever you need the information just query the collectionMetadata collection. Or, if you want to use an extension method like in your example, do something like this:
public static DateTime GetCreatedDate(this MongoCollection collection)
{
var collectionMetadata = _db.GetCollection("collectionMetadata");
var metadata = collectionMetadata.FindOneById(collection.Name);
var created = metadata["Created"].AsDateTime;
return created;
}
The "creation date" is not part of the collection's metadata. A collection does not "know" when it was created. Some indexes have an ObjectId() which implies a timestamp, but this is not consistent and not reliable.
Therefore, I don't believe this can be done.
Like Mr. Gates VP say, there is no way using the metadata... but you can get the oldest document in the collection and get it from the _id.
Moreover, you can insert an "empty" document in the collection for that purpose without recurring to maintain another collection.
And it's very easy get the oldest document:
old = db.collection.find({}, {_id}).sort({_id: 1}).limit(1)
dat = old._id.getTimestamp()
By default, all collection has an index over _id field, making the find efficient.
(I using MongoDb 3.6)
Seems like it's some necroposting but anyway: I tried to find an answer and got it:
Checked it in Mongo shell, don't know how to use in C#:
// db.payload_metadata.find().limit(1)
ObjectId("60379be2bec7a3c17e6b662b").getTimestamp()
ISODate("2021-02-25T12:45:22Z")