How can i directly acess embedded document object mongodb - java

Here is my document
{
"_id":"1000",
"event_name":"Some name",
"tracks":
[
{
"id":"100"
"title":"Test title",
},
{
"id":"101"
"title":"Test title 2",
}
]
}
I want to directly acess track with id 101
So far i am doing this
Query query = new Query(Criteria.where("tracks.$._id").is(id));
// Execute the query and find one matching entry
Event event= mongoTemplate.findOne(query, Event.class,MongoDBCollections.EVENT);
I want some thing like this
Track track = event.getTrackById(id); how can i get track from that event object?

You are using the positional operator in the wrong place, it is a "projection" modifier and is not part of the query. You add the fields on the query spec with the .include() method:
Query query = new Query(Criteria.where("tracks._id").is(id);
query.fields().include("tracks.$");
Event event = mongoTemplate.findOne(query,Event.class,MongoDBCollections.EVENT);
But as with all projecton, the list of "fields" is all or nothing. So you either specify all of the fields to match your result class or modify that result class to just the fields you wish to return.

Related

How do you remove an element from an array based on element's field value? (Spring-Boot, Reactive MongoDB)

I have a Room object. It is essentially a DTO. It has a List or MongoDB an array of User Objects.
For the life of me I can't figure out how to remove a user from the array based on the user's id and couple other things (roomId, roomLeaderId).
public Mono<RoomData> findAndTryKickUser(String roomId, String requestUserId,
String kickUserId){
Query query = new Query();
query.addCriteria(Criteria.where("id").is(roomId).and("roomLeaderId").is(requestUserId)
.and("userRoomDatas").elemMatch(Criteria.where("userId").is(kickUserId)));
Update update = new Update();
update.pull("userRoomDatas.$.userId", kickUserId);
FindAndModifyOptions options = FindAndModifyOptions.options();
options.returnNew(true);
return template.findAndModify(query, update, options, RoomData.class);
}
Now I do have the option to make a User object and place the id in and try to remove the user that way (I haven't tried it yet, but I think that would work), but from my understanding of the placeholder "$" I should be able to remove element.
{"_id":"5b45572047f84e3d10c59b8a"
,"userRoomDatas":[{"isInGame":false,"isReady":true,"isSpectator":false,"userId":"5b45572047f84e3d10c59b84"}]
,"selectedScreen":"MATCHMAKING"
,"privacySetting":"PUBLIC"
,"roomLeaderId":"5b45572047f84e3d10c59b84"
,"botHostId":"5b45572047f84e3d10c59b84"
,"matchmakingData": {"selectedQueId":"5b45572047f84e3d10c59b85","matchmakingRating":"M3"}
,"currentRoomStatus":"IN_ROOM"
,"canStartSearch":false
,"password":"AA=="
,"quickJoinEnabled":true
,"roomRating":"MEMERS_ONLY"
,"_class":"room"}
EDIT: I made some edits to the code. In part of experimenting with unset but I realized id is actually userId for UserRoomData. Still erroring out in my JUnit tests.
org.springframework.data.mongodb.UncategorizedMongoDbException: Command failed with error 2: 'Cannot apply $pull to a non-array value' on server localhost:27017. The full response is { "ok" : 0.0, "errmsg" : "Cannot apply $pull to a non-array value", "code" : 2, "codeName" : "BadValue" }; nested exception is com.mongodb.MongoCommandException:
$pull is array operator and takes the query to match and delete the matching rows in embedded array.
Something like
public Mono<RoomData> findAndTryKickUser(String roomId, String requestUserId, String kickUserId){
Query query = new Query();
query.addCriteria(Criteria.where("id").is(roomId).and("roomLeaderId").is(requestUserId)
.and("userRoomDatas").elemMatch(Criteria.where("userId").is(kickUserId)));
Update update = new Update();
update.pull("userRoomDatas", new Query().addCriteria(Criteria.where("userId").is(kickUserId)));
FindAndModifyOptions options = FindAndModifyOptions.options();
options.returnNew(true);
return template.findAndModify(query, update, options, RoomData.class);
}
Reference
https://docs.mongodb.com/manual/reference/operator/update/pull/#remove-items-from-an-array-of-documents

Firebase data retrieval java

{
"Account1" :
{
Push_key(): { Carplate: "ABC1234" }
Push_key(): { Carplate: "ABC" }
Push_key(): { Carplate: "A" }
}
}
This is how the database looks like.
I would like to retrieve the third data which contains "A" alone ONLY.
I am using startAt() and endAt() for data retrieval:
Query query = ref.child("Account1").orderByChild("Carplate").startAt("A").endAt("A"+"\uf8ff");
But it returns all 3 records. (I think its due to they are all started at "A".)
Need help! Please!
You should look at the equalTo() method for this (from the doc):
The equalTo() method allows you to filter based on exact matches. As is the case with the other range queries, it will fire for each matching child node.
To adapt it to your query you might try:
Query query = ref.child("Account1").orderByChild("Carplate").equalTo("A");

How to reduce the length of a multivalued field in Solr

We have a multivalued field in Solr that we want to reduce its length.
A sample result response is as follows:
response": {
"numFound": 1,
"start": 0,
"docs": [
{
"created_date": "2016-11-23T13:47:46.55Z",
"solr_index_date": "2016-12-01T08:21:59.78Z",
"modified_date": "2016-12-13T08:45:44.507Z",
"id": "FEAE38C2-ABFF-4F0C-8AFD-9B8F51036D8A",
"Field1": [
"false",
"true",
"true",
..... <= 1200 items
]
}
]
}
We have big data, a couple of TB and we are looking for an optimized way to alter all documents within Solr and to modify Field1 to contain only the first 100 items.
Can something like this be done without the need to write a script to manually fetch the document, make adjustments and push it back to solr? Has anyone had a similar experience? Thanks
We have faced this problem. But we use Two collections to solve this problem. Use SoleEntityProcessor to move the document from one collection to another.
[SolrEntityProcessor]
<dataConfig>
<document>
<entity name="sep" processor="SolrEntityProcessor" url="http://localhost:8983/solr/db" query="*:*"/>
</document>
</dataConfig>
While moving pass that document through updateRequestProcessorChain where we can write StatelessScriptUpdateProcessorFactory to edit our documents or to truncate the multivalued field.
In StatelessScriptUpdateProcessorFactory you can get the field and apply your operations and then reset that field.
[StatelessScriptUpdateProcessorFactory]
function processAdd(cmd) {
doc = cmd.solrDoc;
multiDate = doc.getFieldValue("multiValueField");
//Apply your operation to above field
//doc.setField("multiValueField",value);
}
function processDelete(cmd) {
// no-op
}
function processMergeIndexes(cmd) {
// no-op
}
function processCommit(cmd) {
// no-op
}
function processRollback(cmd) {
// no-op
}
function finish() {
// no-op
}
For More information on StatelessScriptUpdateProcessorFactory, you can refer to this question
On solr how can i copy selected values only from multi valued field to another multi valued field?
in which they edit the multivalued field using the script.

How to query on inherited classes with querydsl

I use querydsl to query on a mongodb. As allowed by mongodb, in several cases I store objects of different types in the same collection.
For instance, in my data model I have:
interface Notification {
NotificationType getType(); // EMAIL, SMS etc.
}
interface EmailNotification extends Notification {
Set<User> getRecipients();
}
Now I want to query for Notifications of any kind (not only EmailNotification), but in the case I have EmailNotifications, I want to filter on some recipient.
I tried this code (doesn't work):
final QNotification notification = QNotification.notification;
final BooleanBuilder typesPredicate = new BooleanBuilder();
// "recipientEmails" and "recipientPhones" are provided parameters (lists of String)
typesPredicate.or(notification.type.eq(NotificationType.EMAIL)
.and(notification.as(QEmailNotification.class).recipients
.any().email.in(recipientEmails)));
typesPredicate.or(notification.type.eq(NotificationType.SMS)
.and(notification.as(QSMSNotification.class).recipients
.any().phoneNumber.in(recipientPhones)));
notificationPersister.query(Notification.class).where(typesPredicate);
It doesn't throw any error or exception, but I don't get the expected result (actually I don't get any result) because the generated mongo query is wrong, and I can't get how to make it right.
Generated query is like:
{
"$and":[
// ...
{
"$or":[
{
"type":"EMAIL",
"notification.recipients.email":{
"$in":[
"a#b.com"
]
}
},
// ...
]
}
]
}
And the issue lies in the key "notification.recipients.email": it should be just "recipients.email".
Why does "notification.as(QEmailNotification.class).recipients" translates to "notification.recipients", and how can I make it as expected ?
Note that the following would work:
notificationPersister.query(EmailNotification.class).where(
QEmailNotification.eMailNotification.recipients.any().email.in(recipientEmails));
But then I'm forced to run 1 query per inherited class, which is not efficient.
As Timo said: Fixed in querydsl via github.com/querydsl/querydsl/pull/1428

MongoDB extracting values from BasicDBObject (Java)

I am having trouble retrieving values from queried documents in MongoDB.
For example, the doc structure is like:
{
"_id": {
"$oid": "50f93b74f9eccc540b302462"
},
"response": {
"result": {
"code": "1000",
"msg": "Command completed successfully"
},
"resData": {
"domain:infData": {
"domain:name": "ritesh.com",
"domain:crDate": "2007-06-15T12:02:36.0000Z",
"domain:exDate": "2013-06-15T12:02:36.0000Z"
}
}
}
}
And the query code is:
DBCollection collection = db.getCollection("domains");
BasicDBObject p = new BasicDBObject("response.resData.domain:infData.domain:name", "ritesh.com");
DBCursor c = collection.find(p);
while(c.hasNext()) {
DBObject obj = c.next();
Object value = obj.get("response.resData.domain:infData.domain:name");
}
It queries fine and fetches the doc, but I can't seem to figure out how to extract the value of "response.resData.domain:infData.domain:name" or other similarly nested values from the DBObject (or BasicDBObject since c.next() returns type BasicDBObject).
I could fetch the objects one at a time like:
((DBObject)obj.get("response")).get("resData")....
but that seems very cumbersome.
I thought since you can put() a nested field value in BasicDBObject like:
basicDBObject.put("response.resData.domain:infData.domain:name", "ritesh.com");
that I could similarly use get() to fetch from the BasicDBObject result using the same kind of key. Like I attempted to do in the code above with:
Object value = obj.get("response.resData.domain:infData.domain:name");
But that is returning a null value.
It's probably something straightforward, but I can't seem to figure it out. And everywhere I've checked on the net the examples only fetch values that aren't nested, from the result. Like
doc.get("name");
instead of something like:
doc.get("name.lastname.clanname");
Any help would be appreciated. Thanks!
There's no way to chain a property name like you're doing using the Java driver (gets for sure, and according to the this, put isn't supposed to work either).
You'll need to get the objects one at a time like you suggested.
((DBObject)obj.get("response")).get("resData")
See here for a potential future feature that would allow your syntax to possibly work (although, likely with a new method name).
I ran into the same problem and I wrote a small function to fetch chained properties.
private Object getFieldFromCursor(DBObject o, String fieldName) {
final String[] fieldParts = StringUtils.split(fieldName, '.');
int i = 1;
Object val = o.get(fieldParts[0]);
while(i < fieldParts.length && val instanceof DBObject) {
val = ((DBObject)val).get(fieldParts[i]);
i++;
}
return val;
}
I hope it helps.

Categories