Mongodb java spring update nested object - java

Running a java spring project (v2) with mongo v4.
Trying to update a nested object and been reading a lot on SO but havent found any good solution.
I have something like the following:
menu {
id
name
categories {
id
name
products {
id
price
referenceProduct {
id
name
}
}
}
}
When I update a reference product, then I want all my referenceproducts in menus to be updated as well.
I have tried the following:
Query query = Query.query(Criteria
.where("menuProductCategories.menuProducts.refProduct.id").is(refProduct.getId())
);
Update update = new Update().set("menuProductCategories.$.menuProducts.$.refProduct", refProduct);
But then I get the too many positional elements found.
Next idea would be to use arrayfilters using something like the following (just trying to update name prop to 'TEST'):
Bson filter = Filters.eq("menuProductCategories.menuProducts.refProduct.id", new ObjectId(refProduct.getId()));
Bson update = new Document("$set", new Document().append("menuProductCategories.$[i].menuProducts.$[j].refProduct.name", "TEST"));
But then I get stuck on how to specify my arrayfilters since they all assume that you know the index, which I don't since I want to do a full search and update all that matches my initial criteria.
Any ideas? Of course I could just retrieve all the menus and iterate over them, but I would love to find a small neat solution.

You can use below query in 3.6 server version with 3.6.x mongodb driver.
MongoCollection<Document> collection = mongoTemplate.getDb().getCollection("col");
Bson update = Updates.set("menuProductCategories.$[].menuProducts.$[i].refProduct.name", "TEST");
Bson filter = Filters.eq("i.refProduct.id", new ObjectId(refProduct.getId()));
UpdateOptions updateOptions = new UpdateOptions().arrayFilters(Arrays.asList(filter));
collection.updateOne(null, update, updateOptions);

Related

Java Couchbase Querying to find a document's ID?

I'm new to couchbase. I'm using Java for this. I'm trying to remove a document from a bucket by looking up its ID with query parameters(assuming the ID is unknown).
Lets say I have a bucket called test-data. In that bucked I have a document with ID of 555 and Content of {"name":"bob","num":"10"}
I want to be able to remove that document by querying using 'name' and 'num'.
So far I have this (hardcoded):
String statement = "SELECT META(`test-data`).id from `test-data` WHERE name = \"bob\" and num = \"10\"";
N1qlQuery query = N1qlQuery.simple(statement);
N1qlQueryResult result = bucket.query(query);
List<N1qlQueryRow> row = result.allRows();
N1qlQueryRow res1 = row.get(0);
System.out.println(res1);
//output: {"id":"555"}
So I'm getting a json that has the document's ID in it. What would be the best way to extract that ID so that I can then remove the queryed document from the bucket using its ID? Am I doing to many steps? Is there a better way to extract the document's ID?
bucket.remove(docID)
Ideally I'd like to use something like a N1q1QueryResult to get this going but I'm not sure how to set that up.
N1qlQueryResult result = bucket.query(select("META.id").fromCurrentBucket().where((x("num").eq("\""+num+"\"")).and(x("name").eq("\""+name+"\""))));
But that isn't working at the moment.
Any help or direction would be appreciated. Thanks.
There might be a better way which is running this kind of query:
delete from `test-data` use keys '00000874a09e749ab6f199c0622c5cb0' returning raw META(`test-data`).id
or if your fields has index:
delete from `test-data` where name='bob' and num='10' returning raw META(`test-data`).id
This query deletes the specified document with given document key (which is meta.id) and returns document id of deleted document if it deletes any document. Returns empty if no documents deleted.
You can implement this query with couchbase sdk as follows:
Statement statement = deleteFrom("test-data")
.where(x("name").eq(s("bob")).and(x("num").eq(s("10"))))
.returningRaw(meta(i("test-data")).get("id"));
You can make this statement parameterized or just execute like that.

Java method for MongoDB collection.save()

I'm having a problem with MongoDB using Java when I try adding documents with customized _id field. And when I insert new document to that collection, I want to ignore the document if it's _id has already existed.
In Mongo shell, collection.save() can be used in this case but I cannot find the equivalent method to work with MongoDB java driver.
Just to add an example:
I have a collection of documents containing websites' information
with the URLs as _id field (which is unique)
I want to add some more documents. In those new documents, some might be existing in the current collection. So I want to keep adding all the new documents except for the duplicate ones.
This can be achieve by collection.save() in Mongo Shell but using MongoDB Java Driver, I can't find the equivalent method.
Hopefully someone can share the solution. Thanks in advance!
In the MongoDB Java driver, you could try using the BulkWriteOperation object with the initializeOrderedBulkOperation() method of the DBCollection object (the one that contains your collection). This is used as follows:
MongoClient mongo = new MongoClient("localhost", port_number);
DB db = mongo.getDB("db_name");
ArrayList<DBObject> objectList; // Fill this list with your objects to insert
BulkWriteOperation operation = col.initializeOrderedBulkOperation();
for (int i = 0; i < objectList.size(); i++) {
operation.insert(objectList.get(i));
}
BulkWriteResult result = operation.execute();
With this method, your documents will be inserted one at a time with error handling on each insert, so documents that have a duplicated id will throw an error as usual, but the operation will still continue with the rest of the documents. In the end, you can use the getInsertedCount() method of the BulkWriteResult object to know how many documents were really inserted.
This can prove to be a bit ineffective if lots of data is inserted this way, though. This is just sample code (that was found on journaldev.com and edited to fit your situation.). You may need to edit it so it fits your current configuration. It is also untested.
I guess save is doing something like this.
fun save(doc: Document, col: MongoCollection<Document>) {
if (doc.getObjectId("_id") != null) {
doc.put("_id", ObjectId()) // generate a new id
}
col.replaceOne(Document("_id", doc.getObjectId("_id")), doc)
}
Maybe they removed save so you decide how to generate the new id.

MongoCollection updateMany, bulkWrite or something else?

I am trying to insert / update many records in a MongoCollection. I have a list of Documents to be updated.
List<Document> Documents;
The list contains some new records that are to be inserted and others are already existing ones which need to be updated. I was looking at the method
updateMany() in MongoCollection class
but the description says it updates one record. I am confused as to which method should be used.
Reference
Version : 3.0.0
I believe it is a bug in javadoc and updateMany() should update multiple records.
I've investigated source code of Mongo, just in case, and it sets "multi" parameter to true, so everything should work ok:
public UpdateResult updateMany(final Bson filter, final Bson update, final UpdateOptions updateOptions) {
return update(filter, update, updateOptions, true); // that true means "multi" is used
}

MongoDB : How to make findAndModify returns the Last Updated Record

I am new to Mongo DB and having trouble as it is behaving differently in different environments ( Dev , QA and Production)
I am using findAndModify to Update the Records in my MongoDB .
There is a Job that runs daily which Updates /Inserts Data to Mongo DB , and i am using findAndModify to Update the Record .
But what i observed is that the first record that is returned by findAndModify is different in Dev , QA and Production environemnts although the three environments are having the same Data ??
As per the Mongo DB document , it states that findAndModify will modify the first document
Currently this is my code :
BasicDBObject update = new BasicDBObject();
update.append("$set", new BasicDBObject(dataformed));
coll.findAndModify(query, update);
Please let me know how can i make sure that , the findAndModify returns the Last Updated Record , rather than depending upon un predictable behaviour ??
Edited Part
I am trying to use sort for my code but it is giving me compilation errors
coll.findAndModify(query, sort: { rating: 1 }, update);
I have a field called as lastUpdated which is created using System.currentTimeMilis
So can i use this lastUpdated as shown this way to get the Last Updated Record
coll.findAndModify( query,
new BasicDBObject("sort", new BasicDBObject("lastUpdated ", -1)),
update);
It appears you are using Java, so you have to construct the sort parameter as a DBObject, just like the other parameters:
coll.findAndModify(
query,
new BasicDBObject("sort", new BasicDBObject("rating", 1)),
update);
As we already explained to you in your other question, you have to add a field to the document which contains the date it was changed and then sort by that field or you have to use a capped collection, because they guarantee that the insertion order is preserved.

Order by another table with Liferay's DynamicQuery

I have a problem while I'm making a Dynamic Query in Liferay 6. I'm trying to make a query to order JournalArticles based on their view count. The view count is specified in another table (AssetEntry).
I'm stuck with this:
DynamicQuery query = DynamicQueryFactoryUtil.forClass(
JournalArticle.class, "articleParent", PortalClassLoaderUtil.getClassLoader());
//adding criterions
query.add(...);
DynamicQuery dq0 = DynamicQueryFactoryUtil.forClass(AssetEntry.class, "asset",
PortalClassLoaderUtil.getClassLoader())
.setProjection(ProjectionFactoryUtil.property("asset.classPK"))
.add(PropertyFactoryUtil.forName("asset.companyId")
.eqProperty("articleParent.companyId"))
.add(PropertyFactoryUtil.forName("asset.groupId")
.eqProperty("articleParent.groupId"));
query.add(PropertyFactoryUtil.forName("articleParent.resourcePrimKey").in(dq0))
.addOrder(OrderFactoryUtil.desc("asset.viewCount"));
With this I get an error message saying: could not resolve property: asset of: com.liferay.portlet.journal.model.impl.JournalArticleImpl.
If I remove the addOrder-call, this error disappears. How should I add the order statement so the main query is aware of asset.viewCount?
AssetEntryQuery assetEntryQuery = new AssetEntryQuery();
assetEntryQuery.setClassName(JournalArticle.class.getName());
assetEntryQuery.setXXX //adding criterions
assetEntryQuery.setOrderByCol1("viewCount");
List<AssetEntry> assetEntries = AssetEntryServiceUtil.getEntries(assetEntryQuery);
I am afraid that there is no direct way to do this with the DynamicQuery API.
I think you would need to use Service builder Finders i.e. Custom Query with Service builder.
You can't use dynamic query because there is no direct reference from JournalArticle entity to AssetEntry entity.
One possibility is to retrieve ordered ids of articles from the AssetEntry table (basically you dq0), then do another query and do the sorting programatically (you have the ids ordered).
Finally I think that this line
query.add(PropertyFactoryUtil.forName("articleParent.resourcePrimKey").in(dq0))
doesn't do what you think it does. resoucePrimKey is reference to resource table for permissions. You need to use id column.

Categories

Resources