Update / delete multiple objects using Jongo - java

I have a method which takes in a Collection of Objects that are to be deleted.
This is the way I am deleting them now
public void deleteAll(Collection<Object> objs){
for(Object obj : objs) {
collection.remove("{ _id: # }", obj.getId());
}
}
I am doing something very similar for update where I am looping through the passed collection of Objects. This seems to be very time consuming.
Is there a better way of doing the update/delete?

It's possible to both remove and update multiple documents with a single query.
remove
You need to use a query with a selector using $in, and an array of _id values to match.
With Jongo, you can build the list to match with $in into the query in a couple of different ways
// pass an array of ids
ObjectId[] ids = {id1, id2, id3};
collection.remove("{ _id: { $in: # } }", ids);
// or pass each id separately
collection.remove("{ _id: { $in:[#, #, #] }}", id1, id2, id3);
update
Exact same concept as above using $in to select the objects you want to update, however you also have to set the multi option, so that the update applies to all the documents it matches against, not just the first.
With Jongo this is done like so
ObjectId[] ids = {id1, id2, id3};
collection
.update("{ _id: { $in: # } }", ids)
.multi()
.with({ $set: { foo: "bar" });

Related

Update/add a single value to an array in MongoDB - but not as list?

I want to simply add one value to a tuple from MongoDB.
The key is query, and the variable position should be added as consumer-Variable in the tuple with the following code:
MongoCollection<Document> collection = ...
Bson filter = Filters.eq("query", queryName);
Bson update = Updates.addToSet("consumer", position);
collection.findOneAndUpdate(filter, update);
However, when I look into my database, it looks like a list "consumer" : [ NumberLong(88760) ] has been inserted and not a single value, as shown in the producer field:
{ "_id" : ObjectId(...), "query" : "1000", "consumer" : [ NumberLong(88760) ], "producer" : NumberLong(88760) }
I also tried Update.push() with the same result.
How can I add just a single value, without having it as list?
For example, if you need to update only 1 object from the list inside, you can do it in the format as:
.update_one({"_id": ObjectId(_id)}, {"$set": {"i.5": list[2]}})
where "i.5" - 5 index of the list i.

Upsert many documents based on _id

I need to upsert many documents based on _id.
E.g.
document_1 = {_id:"1", "age":11, "name":"name1"}
document_2 = {_id:"2", "age":22, "name":"name2"}
I wrote the below
db.my_collection.updateMany(
{ _id: {"$in":["1","2"] } },
[
{$set: {_id:"1", "age":11, "name":"name1"}},
{$set: {_id:"2", "age":22, "name":"name2"}}
],
true
)
But no rows gets updated or inserted. Where have I gone wrong?
Instead of true in the 3rd parameter, you have to pass {new: true, upsert: true}.
$merge seems to be a better option for upsert operation. You can store the records you want to upsert in another collection, says to_be_inserted and perform $merge in the aggregation pipeline.
db.to_be_upserted.aggregate([
{
"$merge": {
"into": "my_collection",
"on": "_id",
"whenMatched": "merge",
"whenNotMatched": "insert"
}
}
])
Here is the Mongo Playground for your reference.

Spring MongoDB - update or insert array object in one operation

I have a data structure something like this:
{
"_id": "0123456789",
"myArray": [
{
"name": "Steve",
"data": {
"stuff": "datas"
}
]
}
Using Spring MongoDB, I'd like to have a findAndModify operation that either replaces the entire element (a big, complex object) found by the name field, or if it doesn't exist then insert it.
So far I have this:
private UpdateResult update(String id, String name, MyClass obj) {
Query query = new Query();
query.addCriteria(
Criteria.where("_id").is(id).andOperator(
Criteria.where("myArray.name").is(name)));
Update update = new Update();
update.set("myArray.$[]", obj);
return mongoTemplate.upsert(query, update, MyWrapperClass.class);
}
That updates the field, but it won't push a new one if it's not found
Error:
Cannot apply array updates to non-array element
Is there a way to do this in one operation without querying first?

Firebase query to fetch data in given range

I have a node called quotes in Firebase. I'm facing issues while fetching data in Android for a particular range. I want to fetch 3 continues quotes id starting from 2. Here is my query database:
"quotes" : {
"-L75elQJaD3EYPsd4oWS" : {
"authorName" : "Hellen v",
"famousQuote" : "When one door of happiness closes, another opens; but often we look so long at the closed door that we do not see the one which has been opened for us.",
"id" : "1",
"uploadedBy" : "Admin"
},
"-L7GOvDNI-o_H8RvNwoN" : {
"authorName" : "Rocky Balboa",
"famousQuote" : "It's not about how hard you can hit; it's about how hard you can get hit and keep moving forward.",
"id" : "2",
"uploadedBy" : "Admin"
},
"-L7GP9oBv5NR1T6HlDd4" : {
"authorName" : "African proverb",
"famousQuote" : "If you want to go fast, go alone. If you want to go far, go together.",
"id" : "3",
"uploadedBy" : "Admin"
},
"-L7GPjM1F3_7Orcz0Q1q" : {
"authorName" : "A.P.J Abdul Kalam",
"famousQuote" : "Don’t take rest after your first victory because if you fail in second, more lips are waiting to say that your first victory was just luck.",
"id" : "4",
"uploadedBy" : "Admin"
},
Below is the rule which I'm using for quotes
"quotes": {
".indexOn": ".value"
}
How can I get quotes which has id 2,3 and 4?
If you have more than 4 records in your database, to solve this, you can use a query in which you should combine startAt() and endAt() methods to limit both ends of your query like this:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
Query query = rootRef.child("quotes").orderByChild("id").startAt("2").endAt("4");
query.addListenerForSingleValueEvent(/* ... */);
Please see here more informations about Firebase Query's startAt() method:
Create a query constrained to only return child nodes with a value greater than or equal to the given value, using the given orderBy directive or priority as default.
And here are more informations about Firebase Query's endAt() method:
Create a query constrained to only return child nodes with a value less than or equal to the given value, using the given orderBy directive or priority as default.
Edit: According to your comment, if you only want the items that have the id property set to 2, 3 and 4, you should use nested queries like this:
Query queryTwo = rootRef.child("quotes").orderByChild("id").equalsTo("2");
queryTwo.addListenerForSingleValueEvent(
List<Item> list = new ArrayList();
list.add(itemTwo);
Query queryThree = rootRef.child("quotes").orderByChild("id").equalsTo("3");
queryThree.addListenerForSingleValueEvent(
list.add(itemThree);
Query queryFour = rootRef.child("quotes").orderByChild("id").equalsTo("4");
queryFour.addListenerForSingleValueEvent(
list.add(itemFour);
//Do what you need to do with the list that contains three items
);
);
);

OR and AND Operators in Elasticsearch query

I have few json document with the following format :-
_source: {
userId: "A1A1",
customerId: "C1",
component: "comp_1",
timestamp: 1408986553,
}
I want to query the document based on the following :-
(( userId == currentUserId) OR ( customerId== currentCustomerId) OR (currentRole ==ADMIN) ) AND component= currentComponent)
I tried using the SearchSourceBuilder and QueryBuilders.matchQuery, but I wasnt able to put multiple sub queries with AND and OR operators.
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery("userId",userId)).sort("timestamp", SortOrder.DESC).size(count);
How we query elasticsearch using OR and AND operators?
I think in this case the Bool query is the best shot.
Something like :
{
"bool" : {
"must" : { "term" : { "component" : "comp_1" } },
"should" : [
{ "term" : { "userId" : "A1A1" } },
{ "term" : { "customerId" : "C1" } },
{ "term" : { "currentRole" : "ADMIN" } }
],
"minimum_should_match" : 1
}
}
Which gives in Java:
QueryBuilder qb = QueryBuilders
.boolQuery()
.must(termQuery("component", currentComponent))
.should(termQuery("userId", currentUserId))
.should(termQuery("customerId", currentCustomerId))
.should(termQuery("currentRole", ADMIN))
.minimumNumberShouldMatch(1)
The must parts are ANDs, the should parts are more or less ORs, except that you can specify a minimum number of shoulds to match (using minimum_should_match), this minimum being 1 by default I think (but you could set it to 0, meaning that a document matching no should condition would be returned as well).
If you want to do more complex queries involving nested ANDs and ORs, simply nest other bool queries inside must or should parts.
Also, as you're looking for exact values (ids and so on), maybe you can use term queries instead of match queries, which spare you the analysis phase (if those fields are analyzed at all, which doesn't necessarily make sense for ids). If they are analyzed, you still can do that, but only if you know exactly how your terms are stored (standard analyzer stores them lower cased for instance).
If you use a query_string query, your ANDs and ORs will be interpreted as such by the Lucene library.
This allows you to search for
(currentUserId OR currentCustomerId) AND currentComponent
for instance. By default, the values will be searched for in all fields.

Categories