How MongoClient::save(...) might change the _id field of document parameter - vert.x

I have a class User that embeds a JsonObject to represent the user's fields. This class looks like that:
class User {
private JsonObject data;
public User(...) {
data = new JsonObject();
data.put("...", ...).put(..., ...);
}
public String getID() { return data.getString("_id"); }
// more getters, setters
// DB access methods
public static userSave(MongoClient mc, User user){
// some house keeping
mc.save("users", user.jsonObject(), ar -> {
if(ar.succeeded()) { ... } else { ... }
});
}
}
I've just spent more than half a day trying to figure out why a call to user.getID() sometimes produced the following error: ClassCastException: class io.vertx.core.json.JsonObject cannot be cast to class java.lang.CharSequence. I narrowed down to the userSave() method and more specifically to MongoClient::save() which actually produces a side effect which transforms the data._id from something like
"_id" : "5ceb8ebb9790855fad9be2fc"
into something like
"_id" : {
"$oid" : "5ceb8ebb9790855fad9be2fc"
}
This is confirmed by the vertx documentation which states that "This operation might change _id field of document parameter". This actually is also true for other write methods like inserts.
I came with two solutions and few questions about doing the save() properly while keeping the _id field up to date.
S1 One way to achieve that is to save a copy of the Json Object rather than the object itself, in other words : mc.save("users", user.jsonObject().copy(), ar -> {...});. This might be expensive on the long run.
S2 An other way is to "remember" _id and then to reinsert it into the data object in the if(ar.succeeded()) {data.put("_id", oidValue); ...} section. But as we are asynchronous, I don't think that the interval between save() and the data.put(...) is atomic ?
Q1: Solution S1 make the assumption that the ID doesn't change, i.e., the string 5ceb8ebb9790855fad9be2fc will not change. Do we have a warranty about this ?
Q2: What is the right way to implement the saveUser() properly ?
EDIT: The configuration JSON object user for the creation of the MongoClient is as follows (in case there is something wrong) :
"main_pool" : {
"pool_name" : "mongodb",
"host" : "localhost",
"port" : 27017,
"db_name" : "appdb",
"username" : "xxxxxxxxx",
"password" : "xxxxxxxxx",
"authSource" : "admin",
"maxPoolSize" : 5,
"minPoolSize" : 1,
"useObjectId" : true,
"connectTimeoutMS" : 5000,
"socketTimeoutMS" : 5000,
"serverSelectionTimeoutMS" : 5000
}

Related

how to add multiple fileds of the same name with different data

so what i am doing is sending this data to the the firebase firestore here's my code its working fine,
final data = {
"sent_requests" : {
"sender" : "${loggedInUser.uid}",
"receiver" : args.uid,
"details" : {
"date" : detail_class.datetime,
"total_dishes" : detail_class.total_dishes,
"total_people" : detail_class.total_people,
"meals" : detail_class.meals,
"location" : detail_class.location,
}
}
};
dynamic db = FirebaseFirestore.instance;
db.collection("users").doc("${loggedInUser.uid}").set(data, SetOptions(merge: true));
This result is fine, but this code just overwrites the previous request. I instead want to add more requests in it,
when the sender and receiver are different.
Field names are unique in Firestore, so you can't have (for example) two receiver fields.
What you can have is a single field that is an array of values. I'd typically call that receivers (plural) to indicate that it's a multi-value field, and you'd write that from your code with:
final data = {
"sent_requests" : {
...
"receivers" : [args.uid],
...
}
};
This sets the receivers field to an array with just the value or args.uid. If you want to merge the args.uid with any existing values in the database already, you can use an array-union operator:
final data = {
"sent_requests" : {
...
"receivers" : FieldValue.arrayUnion([args.uid]),
...
}
};
Now the args.uid value will be added to the receivers array, unless it's already in there.

React Component iterate/loop properties of Mongo object

I do have a Mongo collection that stores albums with predefined "slot" for its images and feel a bit stuck if there a way to loop over the properties of the collection in order to display images in separated divs.
I did used this code for mapping over the album covers and it worked great:
albums() {
return Albums.find().fetch();
}
{this.albums().map( (album) => {
return <div key={album._id}><img src={album.cover} /></div>
})}
But now I ask you to help, is it possible to loop over photoOne, photoTwo, etc... and skip/don't display data if it is empty like in photoThree for example.
{
"_id" : "CHMHbNWWwZGaLGvB6",
"title" : "Text",
"cover" : "link",
"createdAt" : date,
"photoOne" : {
"titleOne" : "Text",
"coverOne" : "link"
}
"photoTwo" : {
"titleTwo" : "Text",
"coverTwo" : "link"
}
"photoThree" : {
"titleThree" : "",
"coverThree" : ""
}
}
I'm not a Mongo user, but in the map function you can check for the existing values, and handle it there. Something like (there is surely a cleaner way, though):
this.albums().map( (album) => {
for (key in album){
if (key.startsWith('photo')){
var title = Object.keys(album[key])[0];
if (album[key][title].length != 0){
console.log("Can use: " + Object.keys(album[key])[0])
}
}
}
})
Results in:
Can Use This: titleOne
Can Use This: titleTwo
Hope that helps, but it seems like having the photos with photoOne, photoTwo you are limiting the number of photos to use and requiring the need to use Object.keys to get the values out (without specifically using album.photoOne, album.photoTwo, etc.
If the album photos were stored in an embedded document, you could just include the photos and titles that existed and avoid having to check for empty ones. You would just loop through the photos that are present....if that makes sense.

What am I doing wrong when manipulating data in Meteor/MongoDB?

I have this helper
myClub: function(){
var currentUserId = Meteor.userId();
var user = Meteor.users.findOne({_id: currentUserId});
return user;
}
I want it to return user.role
Here is my user in MongoDB
{
"_id" : "RdirmrLG3t8qBk4js",
"createdAt" : ISODate("2016-04-17T19:40:56.877Z"),
"services" : {
"password" : {
"bcrypt" : "$2a$10$cPe92XR9DT238bH/RanYEu.J6K2ImvAEbWOcVq6j9luI0BH08Qdly"
},
"resume" : {
"loginTokens" : [
{
"when" : ISODate("2016-04-17T19:51:49.474Z"),
"hashedToken" : "uVKUj/7JEkkOuizXhjl212Z38E47HXCex+D4zRikQ1k="
}
]
}
},
"username" : "worker",
"role" : "worker",
"club" : "hzSKAJfPXo7hSpTYS"
}
The code above works just fine. So it finds the current user and outputs info about it. But when I change user to user.role I get the following errormessage.
TypeError: Cannot read property 'role' of undefined
at Object.myClub
How can it be undefined? Is my syntax incorrect?
Template helpers are reactive, which means they update themselves as the app state changes or new data appears. In your case, the helper is called immediately when the template is rendered and before the Meteor.users collection is filled. Therefore, the .findOne() method returns undefined. It will be corrected in the second pass after new data arrives.
The simple fix here is to check whether the data is present inside the helper:
myClub: function(){
var currenUserId = Meteor.userId();
var user = Meteor.users.findOne({_id: currenUserId});
if(!user) return 'NO DATA';
return user.role;
},
In real life you'll probably want to wait for the basic data to be loaded before you render the template. That is usually done on the controller level.
Try:
myClub: function(){
return Meteor.user() && Meteor.user().role;
}
This is shorthand for return the role if there's a user.
As far as the role field not showing up, make sure that you are publishing that key from the server and subscribing to it. For example:
Meteor.publish('me',function(){
return Meteor.users.find(this.userId,{fields: {role: 1, username: 1, profile: 1, emails: 1}});
});
And on the client:
var me = Meteor.subscribe('me');
if ( me.ready() ) console.log("Ta-da! The role is: "+Meteor.user().role);
make sure that you subscribed to all data you need.
By the way, you can try following:
role: function(){ return (Meteor.user() || {}).role; }
Cheers

Defining Query for OrientDB - using JSON data

I have a question concerning using orientDB for JSON data. In my scenario,
I receive entities which are serialized into JSON. This JSON data should
be stored in orientDB. The relevant code part to create documents
in orientDB looks as follows:
//JSON string created by using Jackson
String jsonStr = "...";
//here some dummy name "test"
ODocument doc = new ODocument("test");
doc.fromJSON(jsonStr);
doc.save();
In the following, I give an example for classes I'm working with
(I left out constructors, getters and setters and other fields which
are not relevant for the example):
class AbstractEntity {
private String oidString;
}
class A extends AbstractEntity {
private List<B> bs;
}
class B extends AbstractEntity {
private List<C> cs;
}
class C extends AbstractEntity {
private List<D> ds;
}
class D extends AbstractEntity {
private int type;
}
As the classes use type List, Jackson needs to store
additional type information in the JSON representation, to be able
to deserialize the data properly.
{
"oidString" : "AAA_oid1",
"bs" : [ "java.util.ArrayList", [ {
"oidString" : "b_oid1",
"cs" : null
}, {
"oidString" : "b_oid2",
"cs" : [ "java.util.ArrayList", [ {
"oidString" : "c_oid1",
"ds" : [ "java.util.ArrayList", [ ] ]
}, {
"oidString" : "c_oid2",
"ds" : [ "java.util.ArrayList", [ {
"oidString" : "d_oid1",
"type" : 2
} ] ]
} ] ]
} ] ]
}
However, I have problems querying such a document if I try to e.g. find all D instances that contain a certain type. I tried to simplify my query by first listing all D instances that can be found for a specific A:
OQuery<?> query = new OSQLSynchQuery<ODocument>(
"select bs.cs.ds from test where oidString = 'AAA_oid1'"
);
This returns: {"#type":"d","#rid":"#-2:0","#version":0,"bs":[null,null]}
The additional type information ("java.util.ArrayList") seems to cause problems for orientDB. If I rewrite my example and only use ArrayList directly, hence, the additional type information is omitted, the query above shows something as a result.
Is there a general solution to this problem? I have to work with JSON data and that JSON data will contain additional type information (it has to).
Can't deal orientDB with this situation?

GORM get/find resource by ID using MongoDB in Grails

Grails makes it easy to get a domain object by ID (handy for building a REST API).
A controller to retrieve a resource can be as simple as:
MetricController.groovy
import grails.converters.JSON
class MetricController {
def index() {
def resource = Metric.get(params.id)
render resource as JSON
}
}
When using the Grails plugin for MongoDB GORM (compile ":mongodb:1.2.0"), the default id type of Long needs to be changed to type String or ObjectId.
Metric.groovy
import org.bson.types.ObjectId
class Metric {
static mapWith = "mongo"
ObjectId id
String title
}
However, doing a .get(1) will now result in:
Error 500: Internal Server Error
URI
/bow/rest/metric/1
Class
java.lang.IllegalArgumentException
Message
invalid ObjectId [1]
I took a guess and changed the controller to use findById:
def resource = Metric.findById(new ObjectId(new Date(), params.id.toInteger()))
That fixed the error, but it fails to find the object (always returns null).
For example, using the id of "-1387348672" does not find this test object:
{ "class" : "Metric",
"id" : { "class" : "org.bson.types.ObjectId",
"inc" : -1387348672,
"machine" : 805582250,
"new" : false,
"time" : 1371329632000,
"timeSecond" : 1371329632
},
"title" : "Test"
}
The ObjectId.inc field may not even be the correct field to use for the resource ID.
So, what is the simplest way to retrieve a domain object by ID when using MongoDB?
When a domain object is persisted in MongoDB, it is stored as a document with an ObjectId as a unique 12 bytes BSON primary key. For example, if you have a domain object Product like
import org.bson.types.ObjectId
class Product {
ObjectId id
String name
static mapWith = "mongo"
}
then the persisted entity in MongoDB would look like below if you save with a name as "TestProduct".
//db.product.find() in mongodb
{
"_id" : ObjectId("51bd047c892c8bf0b3a58b21"),
"name" : "TestProduct",
"version" : 0
}
The _id becomes your primary key for that document. To get this document you need the primary key ObjectId. Speaking from a RESTful context, you atleast need the 12 bytes hexcode 51bd047c892c8bf0b3a58b21 as part of the request.
So in the above case, you can fetch that particular document by doing something like
Product.get(new ObjectId("51bd047c892c8bf0b3a58b21"))
Product.findById(new ObjectId("51bd047c892c8bf0b3a58b21"))
Have a look at the API for ObjectId which would make clear how to retrieve a document.
When you retrieve the document as JSON, then it just shows the ObjectId class with its elements.
{
"class": "com.example.Product",
"id": {
"class": "org.bson.types.ObjectId",
"inc": -1280996575,
"machine": -1993569296,
"new": false,
"time": 1371341948000,
"timeSecond": 1371341948
},
"name": "TestProduct"
}
For completeness, here's the domain with a controller to get a resource by ID string (instead of ObjectID).
Example:
Metric.get("51bf8ccc30040460f5a05579")
Domain
import org.bson.types.ObjectId
class Metric {
ObjectId id
String title
static mapWith = "mongo"
def out() {
return [
id: id as String, //convert Mongo ObjectId to 12-byte hex BSON
title: title
]
}
}
The out() method is used to render the resource showing its ID string (not its ObjectID).
Controller
import grails.converters.JSON
class MetricController {
def index() {
def resource = Metric.read(params.id)
render resource.out() as JSON
}
}
Example JSON response for /rest/metric/51bf8ccc30040460f5a05579
{ "id": "51bf8ccc30040460f5a05579", "title": "Accounts" }