Spring Java Mongodb Aggregation check userId in array - java

I have a MongoDB database with users and questions.
My Question document:
{
"_id" : "b943d57a-f4c3-4394-86f7-dd1d1b5e2563",
"_class" : "org.company.app.model.Question",
"statement" : "First question for testing purposes",
"userMail" : "themail#mail.com",
"responses" : [
{
"responseId" : "6b60e900-0fec-47d2-8853-e1ea3508abe6",
"responseType" : "TEXT",
"mail" : "themail2#mail.com",
"responseTime" : ISODate("2017-11-22T11:23:10.848Z"),
"data" : "New Response 1",
"likes" : [
"themail3#mail.com",
"themail5#mail.com"
],
"dislikes" : [],
"state" : "PUBLISHED",
"stateTime" : ISODate("2017-11-22T11:23:10.848Z")
}
],
"categories" : [
"Category1",
"Category2",
"Category3"
],
"creationTimestamp" : ISODate("2017-10-26T14:50:12.717Z"),
"lastUpdateTimestamp" : ISODate("2017-11-02T15:35:20.818Z"),
"deleted" : false,
"active" : true,
"state" : "PUBLISHED"
}
and the user document:
{
"_id" : "1f4b5091-c755-4083-880e-e1c4696f7236",
"_class" : "org.company.app.model.User",
"allowsComms" : true,
"registrationConfirmed" : false,
"placeOfWork" : "Urban records",
"name" : "John",
"surname" : "Doe",
"mail" : "themail3#mail.com",
"phone" : "[ \"1111111111\" , \"2222222222222\"]",
"birthDate" : ISODate("1971-12-10T23:00:00.000Z"),
"gender" : "MALE",
"nationality" : "US",
"language" : "en",
"socialNetworks" : {
"INSTAGRAM" : "#instagram",
"TWITTER" : "#twitter"
},
"specialty" : "alternative rock",
"subspecialty" : "sub_3",
"creationTimestamp" : ISODate("2017-10-31T10:13:12.131Z"),
"lastUpdateTimestamp" : ISODate("2017-11-13T10:23:37.637Z"),
"deleted" : false,
"active" : true,
"profileImage" : "image"
}
From that point, I'm creating a new aggregation to join both collections, get user information and count likes and dislikes instead of return all the user ids at that array.
public List<JoinedResponse> getResponses(String userId, String questionId, long itemsPerPage, long requestedPage) {
Assert.hasLength(questionId, "questionId cannot be null or empty");
MatchOperation questionIdMatch = Aggregation.match(new Criteria("_id").is(questionId));
MatchOperation responsesStateMatch = Aggregation.match(Criteria.where("responses.state")
.in(QuestionState.PUBLISHED.toString(), QuestionState.APPROVED.toString()));
LookupOperation userInfoLookUpOperation = LookupOperation.newLookup().from("User").localField("$responses.mail").foreignField("mail")
.as("userInfo");
ProjectionOperation fieldsProjectionOperation = Aggregation.project(Fields.from(
Fields.field("responseId", "$responses.responseId"),
Fields.field("responseType", "$responses.responseType"),
Fields.field("mail", "$responses.mail"),
Fields.field("responseTime", "$responses.responseTime"),
Fields.field("data", "$responses.data"),
Fields.field("state", "$responses.state"),
Fields.field("userName", "$userInfo.name"),
Fields.field("userSurname", "$userInfo.surname"),
Fields.field("profileImage", "$userInfo.profileImage")))
.and("$responses.likes").size().as("likes")
.and("$responses.dislikes").size().as("dislikes");
// Creates the Aggregation: THE ORDER IS IMPORTANT!!!
Aggregation ag = Aggregation.newAggregation(
questionIdMatch,
Aggregation.unwind("responses"),
responsesStateMatch,
userInfoLookUpOperation,
Aggregation.unwind("userInfo"),
fieldsProjectionOperation,
Aggregation.project("responseId", "userName", "userSurname", "mail", "responseType", "responseTime",
"data", "likes", "dislikes", "profileImage", "state"/*, "isLiked"*/),
Aggregation.skip((long) ((requestedPage - 1) * itemsPerPage)),
Aggregation.limit(itemsPerPage));
logger.debug(ag.toString());
AggregationResults< JoinedResponse > output = mongoTemplate.aggregate(ag, "Question",
JoinedResponse);
Well, at this point, I need to know if the userId is in the liked or disliked list and I have spend a lot of hours investigating with no result. Of course, I would like to do it with aggregation.
Any help will be appreciated.
Thank you!

Related

How to query an array in array of dictionaries in MongoDB?

I have a following mongoDB document structure -
db.menus.findOne()
{
"_id" : ObjectId("5cf25412326c3f4f26df039b"),
"restaurantId" : "301728",
"items" : [
{
"itemId" : "CEBM4H41JR",
"name" : "Crun Chicken",
"imageUrl" : "",
"price" : 572,
"attributes" : [
"Tasty",
"Spicy"
]
},
{
"itemId" : "53Q0XS3HPR",
"name" : "Devils Chicken",
"imageUrl" : "",
"price" : 595,
"attributes" : [
"Gravy",
"Salty"
]
}
]
}
I am trying to write a query to get all the menus based on the "attributes" field under "items" in the document.
I have done the following to get the menus if "name" of "items" is given and I am getting a result -
db.menus.find({ 'items' : {$elemMatch : {'name' : {$regex : "Chicken Thali", $options: 'i' }}}}).pretty()
I have tried this for getting the result for attributes but this is not working -
db.menus.find({'items' : {$elemMatch : {'attributes' : {$all : [{$regex : "Tasty", $options: 'i' }]}}}})
How do I get the list and I also want to write this query for mongoRepository in a spring boot application?
Further, based on the restaurantId's obtained, I have to query restaurant collection in order to find all the restaurants in restaurants collection having the following structure -
{
"_id" : ObjectId("5cf2540e326c3f4f26de93dd"),
"restaurantId" : "301728",
"name" : "Desire Foods",
"imageUrl" : "https://b.zmtcdn.com/data/pictures/8/301728/d690ccb500d746530f56e1d637949da2_featured_v2.jpg",
"latitude" : 28.4900591,
"longitude" : 77.3066401,
"attributes" : [
"Chinese",
" Fast Food",
" Bakery"
],
"opensAt" : "09:30",
"closesAt" : "22:30"
}
Is the whole operation possible in a single query?
I think you can modify your query to use $in instead of $all.
To achieve your intended result, you can try:
db.collection.aggregate([
{
"$match": {
"items": {
"$elemMatch": {
"attributes": {
"$in": [
"Tasty"
]
}
}
}
}
},
{
"$lookup": {
"from": "restaurant",
"localField": "restaurantId",
"foreignField": "restaurantId",
"as": "restaurants"
}
},
{
"$unwind": "restaurants"
},
{
"$replaceRoot": { "newRoot": "$restaurants" }
}
])
Use $match at appropriate stages as needed to limit the documents pulled in memory

How to get a String inside an Object Array using MongoDB Java?

I'm trying to learn MongoDB and I want to get a String inside an Object Array, my MongoDB document is here:
{
"_id" : ObjectId("5f1507fed91e81246c409a59"),
"identification" : "punishment",
"lastID" : 2,
"punishmentsTypes" : [
{
"category" : "OFENSA_JOGADOR",
"reason" : "Ofensa a Jogador",
"group" : [
"HELPER",
"MODERATOR",
"ADMINISTRATOR",
"MANAGER",
"MASTER"
],
"description" : "Ofender algum jogador",
"cases" : [
{
"1" : {
"type" : "MUTE",
"duration" : 604800000
}
},
{
"2" : {
"type" : "BAN",
"duration" : 0
}
}
]
},
{
"category" : "FALSIFICACAO",
"reason" : "Falsificação de provas",
"group" : [
"ADMINISTRATOR",
"MANAGER",
"MASTER"
],
"description" : "Falsicar provas ao denunciar um jogador em nosso fórum",
"cases" : [
{
"1" : {
"type" : "BAN",
"duration" : 0
}
}
]
},
{
"category" : "HACK",
"reason" : "Hack",
"group" : [
"MODERATOR",
"ADMINISTRATOR",
"MANAGER",
"MASTER"
],
"description" : "Uso de cheats ou programas ilícitos",
"cases" : [
{
"1" : {
"type" : "BAN",
"duration" : 7776000000.0
}
},
{
"2" : {
"type" : "BAN",
"duration" : 0
}
}
]
}
],
"unpunishmentTypes" : [
{}
]
}
I've tried this:
MongoCollection<Document> collection = mongoDatabase.getCollection("settings");
BasicDBObject query = new BasicDBObject();
BasicDBObject field = new BasicDBObject();
query.put("id", "punish");
field.put("punishmentsTypes", 1);
field.put("_id", 0);
Document test = collection.find(query).projection(field).first();
assert test != null;
Object object = test.get("punishmentsTypes");
System.out.println(object);
And that's the output:
[Document{{category=OFENSA_JOGADOR}},
Document{{category=FALSIFICACAO}}, Document{{category=HACK}}]
How can I get only the category string, to the output be: OFENSA_JOGADOR, FALSIFICACAO, HACK?
I'm not sure how did you get that result with your query but here is how I get your result:
MongoCollection<Document> collection = mongoDatabase.getCollection("settings");
Document query = new Document();
query.put("identification", "punishment");
Document test = collection.find(query).first();
List<Document> punishmentsTypes = test.getList("punishmentsTypes", Document.class);
for (Document document : punishmentsTypes) {
String category = document.getString("category");
System.out.println(category);
}

How to map two datasets in spark Java

Hi I'm reading data from mongodb into spark application.
My mongodb contains 2 collections.
One is profile_data(actual data with field names)
(Which holds all the input data including some unique fields)
{
"MessageStatus" : 2,
"Origin" : 1,
"_id" : ObjectId("596340fe8b0fa35d2880db1a"),
"accerlation" : 19.4,
"cylinders" : 4,
"displacement" : 119,
"file_id" : ObjectId("59633e48b760e7c8071a6c1c"),
"horsepower" : 82,
"modelyear" : 82,
"modified_date" : ISODate("2017-07-10T08:47:01.641Z"),
"mpg" : 31,
"snet_id" : "new_project",
"unique_id" : "784",
"username" : "chevy s-10",
"weight" : 2720
}
And another collection is : predictive_model_details(Which holds the ML model details like model name, feature fields and prediction field just like metadata)
{
"_id" : ObjectId("56b4351be4b064bb19a90324"),
"algorithm_id" : "55d717a53d9e22022ff2a1e9",
"algorithm_name" : "K- Nearest Neighbours (IBK)",
"client_id" : "562e1d51b760d0e408151b91",
"feature_fields" : [
{
"name" : "Origin",
"type" : "int"
},
{
"name" : "accerlation",
"type" : "Double"
},
{
"name" : "displacement",
"type" : "Int"
},
{
"name" : "horsepower",
"type" : "Int"
},
{
"name" : "modelyear",
"type" : "Int"
}
],
,
"makeActiveStatus" : "0",
"model_name" : "test1",
"parameter_type" : "system_defined",
"parameters" : [
{
"symbol" : "-K",
"value" : "1"
}
],
"predictor" : {
"name" : "mpg"
"type" : "Int"
},
"result_exists" : true,
"snet_id" : "new_project"
}
So I've created 2 datasets in spark for two collections in MongoDB. Now I want to map these 2 Datasets with all feature fields together and prediction field together.
And common field in 2 datasets is snet_id.
Could anyone please help?

java mongodb 3.0: find value in internal array of documents

I'm using java and mongodb v.3.0.7.
I have a list of player with internal array of games with scores. This is a test that insert a document:
public void insertPlayer(String id_device) throws ParseException{
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH);
db.getCollection("player").insertOne(
new Document()
.append("nikname", "Guest")
.append("password", "Guest")
.append("my_id", "")
.append("id_device", id_device)
.append("language", "Italian")
.append("games", asList(
new Document()
.append("gamename", "PPA")
.append("banned", false)
.append("date", format.parse("2014-10-01T00:00:00Z"))
.append("score", 11),
new Document()
.append("gamename", "Test2game")
.append("banned", false)
.append("date", format.parse("2014-01-16T00:00:00Z"))
.append("score", 17)))
);
}
To find if a player is banned from a particular game I'm doiing this:
public boolean isBanned(String id_device){
FindIterable<Document> iterable = db.getCollection("player").find(eq("id_device", "machine1"));
System.out.println(iterable.first());
List<Document> dl = (List<Document>)iterable.first().get("games");
for(int i=0;i<dl.size();i++){
Document d = dl.get(i);
System.out.println(d.getString("gamename"));
if(d.getString("gamename").equals("PPA")){
boolean ban = d.getBoolean("banned");
return ban;
}
}
There is a faster way using embedded mongodb methods that find the document:
new Document()
.append("gamename", "PPA")
.append("banned", false)
.append("date", format.parse("2014-10-01T00:00:00Z"))
.append("score", 11),
giving id_device and gamename?
thanks
To achieve that, you need to aggregate your data.
https://docs.mongodb.org/manual/aggregation/
Depending on your usecase, aggregation may change (more query, more pipeline steps).
Here is your data:
{
"_id" : ObjectId("569a30a30586bcb40f7d2531"),
"my_id" : "",
"id_device" : "machine1",
"language" : "Italian",
"games" : [
{
"gamename" : "PPA",
"banned" : true,
"date" : ISODate("2014-10-01T00:00:00.000Z"),
"score" : 11
},
{
"gamename" : "Test2game",
"banned" : false,
"date" : ISODate("2014-01-16T00:00:00.000Z"),
"score" : 17
}
]
}
I assume:
You have a list of unique players. Each of them play unique games.
(Example: player1 never plays PPA twice)
So, you need to search a game where the player is banned and return
all information for that game.
The aggregation would be:
db.player.aggregate([
{$match:{ "id_device" : "machine1"}},
{$unwind: "$games"},
{$match:{ "games.gamename" : "PPA", "games.banned" : true}}
])
Result
[
{
"_id" : ObjectId("569a30a30586bcb40f7d2531"),
"my_id" : "",
"id_device" : "machine1",
"language" : "Italian",
"games" : {
"gamename" : "PPA",
"banned" : true,
"date" : ISODate("2014-10-01T00:00:00.000Z"),
"score" : 11
}
}
]
Some difference, if your players may play same game more than once (different date), you can change your aggregation pipelines.
{
"_id" : ObjectId("569a30a30586bcb40f7d2531"),
"my_id" : "",
"id_device" : "machine1",
"language" : "Italian",
"games" : [
{
"gamename" : "PPA",
"banned" : false,
"date" : ISODate("2014-10-01T00:00:00.000Z"),
"score" : 11
},
{
"gamename" : "Test2game",
"banned" : false,
"date" : ISODate("2014-01-16T00:00:00.000Z"),
"score" : 17
},
{
"gamename" : "PPA",
"banned" : true,
"date" : ISODate("2014-04-18T00:00:00.000Z"),
"score" : 23
},
{
"gamename" : "Foo",
"banned" : true,
"date" : ISODate("2015-03-03T00:00:00.000Z"),
"score" : 2
},
{
"gamename" : "Foo",
"banned" : false,
"date" : ISODate("2015-04-28T00:00:00.000Z"),
"score" : 2
}
]
}
So to query id_device and gamename "PPA", we define our aggregation this way:
db.player.aggregate([
{$match:{ "id_device" : "machine1"}},
{$unwind: "$games"},
{$match:{ "games.gamename" : "PPA"}},
{$group: {_id:{"_id":"$_id", "my_id":"$my_id", "id_device":"$id_device","language":"$language"}, "games" : {$push:"$games"}}},
{$project: {"_id":"$_id._id", "my_id":"$_id.my_id", "id_device": "$_id.id_device", "language":"$_id.language", "games":"$games"}}
])
Result:
[
{
"_id" : ObjectId("569a30a30586bcb40f7d2531"),
"games" : [
{
"gamename" : "PPA",
"banned" : false,
"date" : ISODate("2014-10-01T00:00:00.000Z"),
"score" : 11
},
{
"gamename" : "PPA",
"banned" : true,
"date" : ISODate("2014-04-18T00:00:00.000Z"),
"score" : 23
}
],
"my_id" : "",
"id_device" : "machine1",
"language" : "Italian"
}
]
As you see, you can add/modify pipeline steps and get desired result.

Representing Abstract JSON Objects as models in Java

Ok so I am making API requests to retrieve certain things like movies, songs, or to ping the server. However all of these responses are contained within the same response JSON object that has varying fields depending on the response. Below are three examples.
ping
{
"response" : {
"status" : "ok",
"version" : "0.9.1"
}
}
getIndexes
{
"response" : {
"status" : "ok",
"version" : "0.9.1",
"indexes" : {
"index" : [ {
"name" : "A",
"movie" : [ {
"id" : "150",
"name" : "A Movie"
}, {
"id" : "2400",
"name" : "Another Movie"
} ]
}, {
"name" : "S",
"movie" : [ {
"id" : "439",
"name" : "Some Movie"
}, {
"id" : "209",
"name" : "Some Movie Part 2"
} ]
} ]
}
}
}
getRandomSongs
{
"response" : {
"status" : "ok"
"version" : "0.9.1"
"randomSongs" : {
"song": [ {
"id" : "72",
"parent" : "58",
"isDir" : false,
"title" : "Letter From Yokosuka",
"album" : "Metaphorical Music",
"artist" : "Nujabes",
"track" : 7,
"year" : 2003,
"genre" : "Hip-Hop",
"coverArt" : "58",
"size" : 20407325,
"contentType" : "audio/flac",
"suffix" : "flac",
"transcodedContentType" : "audio/mpeg",
"transcodedSuffix" : "mp3",
"duration" : 190,
"bitRate" : 858,
"path" : "Nujabes/Metaphorical Music/07 - Letter From Yokosuka.flac",
"isVideo" : false,
"created" : "2015-06-06T01:18:05.000Z",
"albumId" : "2",
"artistId" : "0",
"type" : "music"
}, {
"id" : "3135",
"parent" : "3109",
"isDir" : false,
"title" : "Forty One Mosquitoes Flying In Formation",
"album" : "Tame Impala",
"artist" : "Tame Impala",
"track" : 4,
"year" : 2008,
"genre" : "Rock",
"coverArt" : "3109",
"size" : 10359844,
"contentType" : "audio/mpeg",
"suffix" : "mp3",
"duration" : 258,
"bitRate" : 320,
"path" : "Tame Impala/Tame Impala/04 - Forty One Mosquitoes Flying In Formation.mp3",
"isVideo" : false,
"created" : "2015-06-29T21:50:16.000Z",
"albumId" : "101",
"artistId" : "30",
"type" : "music"
} ]
}
}
}
So basically my question is, how should I structure my model classes to use for parsing these responses? At the moment I have an abstract response object that only contains fields for the status and version. However, by using this approach I will need a response class that extends this abstract class for ever request I make (e.g. AbstractResponse, IndexesResponse, RandomSongsResponse). Also, some models with the same name may have different fields depending on the API request made. I would prefer to avoid making a model class for every possible scenario.
And as an extra note, I am using GSON for JSON serialization/deserialization and Retrofit to communicate with the API.

Categories

Resources