How to store domain reference as a dynamic attribute of another domain in Grails with MongoDB? - mongodb

I would like to be able to store a reference to a domain object as a dynamic attribute of another domain.
Unfortunately, when I try to save such domain object I get:
| Error 2013-02-18 14:03:09,352 [localhost-startStop-1] ERROR context.GrailsContextLoader - Error initializing the application: can't serialize class Employee
Message: can't serialize class Employee
Here's the code [Machine & Employee are domains]:
def m = new Machine(name: "Machine 01")
def e = Employee.findByName("employee name")
m['operator'] = e
m.save(failOnError: true)
I'm wondering if this is possible? I think it should, there are no limitations listed on mongodb plugin [I'm using 1.1.0GA with Grails 2.2.0].
class Machine {
String name
}
class Employee {
String name
}

Related

Django Rest Framework - Serializer error when trying to add model from json with 1 attribute as array

Im trying to create an API with Django RestFramework to save some info of computers.
I have encountered a problem when the json has an attribute that is an array of IPv4 field.
I generated the following code
Model
class Computer(models.Model):
hostname = models.CharField(max_length=32)
os_system = models.CharField(max_length=60)
class ComputerIPAddress(models.Model):
computer = models.ForeignKey(Computer,on_delete=models.CASCADE)
ip_address = models.GenericIPAddressField()
Serializer
class ComputerIPAddressSerializer(serializers.ModelSerializer):
class Meta:
model = ComputerIPAddress
fields = ('__all__')
class ComputerSerializer(serializers.ModelSerializer):
ip_address = ComputerIPAddressSerializer(many=True)
class Meta:
model = Computer
fields = ('__all__')
Viewset
class ComputerViewSet(viewsets.ModelViewSet):
queryset = Computer.objects.all()
serializer_class = ComputerSerializer
class ComputerIPAddressViewSet(viewsets.ModelViewSet):
queryset = ComputerIPAddress.objects.all()
serializer_class = ComputerIPAddressSerializer
The idea is that the IP belongs to the computer (if the computer is deleted, I am not interested in having the IP) and a computer can have several IP assigned to it.
The json that is sent is the following:
{'hostname':'PC-01','os_system':'Windows 10','ip_address':['192.168.1.10','192.168.2.10']}
I would approach this problem by overriding the create method the ComputerSerializer
class ComputerSerializer(serializer.ModelSerializer):
...
def create(self, validated_data):
ip_address = validated_data.pop("ip_address", None)
computer = Computer.objects.create(**validated_data)
if ip_address:
for ip in ip_address:
computer.computeripaddress_set.create(ip)
return computer

Cannot create a reference to an object with a NULL id mongo hibernate-mongo and spring boot

I am using spring data(spring boot ) and mongodb . I have these two
entities
#Document(collection = "users")
public class UserEntity {
private String email;
#DBRef
private DeviceEntity device;
}
#Document(collection = "device")
public class DeviceEntity {
private String name;
}
and I am creating the first object of device then setting it to user
entity. Now i will save user entity.
DeviceEntity Device = new DeviceEntity();
device.setName("demo");
UserEntity user = new UserEntity();
user.setEmail("demo#gmail.com");
user.setDevice( device );
userRepo.save( user );
Then i have got this error :
"Cannot create a reference to an object with a NULL id.] with root
cause org.springframework.data.mapping.model.MappingException: Cannot
create a reference to an object with a NULL id mongo hibernate."
Can anyone explain how to we store userentity with deviceentity. If i save device entity first and set to user entity i works fine but i want to save only user entity and it will save device entity automatically.
This is happening becuase hibernate is not able to get id as it is not yet created so you need to save Device to DB first then set it into user entity.
Something like below.
B b = new B();
mongoOperations.save(b);
A a = new A();
a.setB(b)
mongoOperations.save(a);
The mapping framework does not handle cascading saves. If you change an Account object that is referenced by a Person object, you must save the Account object separately. Calling save on the Person object does not automatically save the Account objects in the accounts property.
Read below document please;
https://docs.spring.io/spring-data/mongodb/docs/2.1.4.RELEASE/reference/html/#mapping-usage-references

Neo4j: MissingIndexException on a Node

I'm trying to setup a skeleton of Spring-Data-Neo4j project in Scala.
When I run the JUnit test, and I get a MissingIndexException, I really don't have any clue why. I previously ran the same test successfully using the same configuration (and dependencies) in pure Java instead of scala entities/test.
Any help would do.
The (scala) persistent entity:
#NodeEntity
class User extends Identifiable {
def this(email: String = null, name: String = null) = {
this()
this.email = email
this.name = name
}
#Indexed
var name: String = _
#Indexed (unique = true)
var email: String = _
#Fetch
#RelatedTo(`type` = "IS_A", direction = Direction.BOTH)
var agent: Agent = new Agent
}
Here is the (still java) repository interface:
public interface UserRepository extends GraphRepository<User> {
#Query("start n=node:User(email={0}) return count(*)")
int count(String email);
User findByEmail(String email);
}
The JUnit test I run:
#Test
#Transactional
def testCountEmails = {
assertEquals(0, userRepository.count("mail"))
userRepository.save(new User(email = "mail"))
assertEquals(1, userRepository.count("mail"))
}
An excerpt of the log:
[main] DEBUG org.springframework.data.neo4j.support.query.CypherQueryEngineImpl - Executing cypher query: MATCH (ref:ReferenceNode {name:{name}}) RETURN ref params {name=root}
[main] DEBUG org.springframework.data.neo4j.support.query.CypherQueryEngineImpl - Executing cypher query: match (n) with n limit 1 set n:`SDN_LABEL_STRATEGY` remove n:`SDN_LABEL_STRATEGY` return count(*) params {}
[main] DEBUG org.springframework.data.neo4j.support.schema.SchemaIndexProvider - CREATE CONSTRAINT ON (n:`User`) ASSERT n.`email` IS UNIQUE
[main] DEBUG org.springframework.data.neo4j.support.query.CypherQueryEngineImpl - Executing cypher query: CREATE CONSTRAINT ON (n:`User`) ASSERT n.`email` IS UNIQUE params {}
[main] DEBUG org.springframework.data.neo4j.support.schema.SchemaIndexProvider - CREATE INDEX ON :`User`(`name`)
[main] DEBUG org.springframework.data.neo4j.support.query.CypherQueryEngineImpl - Executing cypher query: CREATE INDEX ON :`User`(`name`) params {}
And the error I get:
org.springframework.dao.InvalidDataAccessResourceUsageException: Error executing statement start n=node:User(email={0}) return count(*); nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: Error executing statement start n=node:User(email={0}) return count(*); nested exception is org.neo4j.cypher.MissingIndexException: Index `User` does not exist
So you're likely using Cypher 2.0, and looking at your query, you don't have a MATCH--just a START and a RETURN. So, first off, I'm not even sure that's legal, but you say it ran before...I've never seen that. :)
That said, I'm pretty sure that the START clause makes use of legacy indices, and it looks like you're attempting to treat :User as a label (which is new to Neo4j 2.x). So, when SDN creates the schema indices (with "name" and "email" as your keys), the START statement is attempting to access a legacy index for "User" which does not exist.
Maybe try this as a query and let us know how it goes:
MATCH (n:User {email: <whatever>}) RETURN count(*);
Also make sure that your parameterization is being taken care of.
(If I'm off on any of this, someone please feel free to correct me.)
HTH

Grails with MongoDB, Object id, and scaffold

I have data writing to a mongoDB database with issues using integration tests and the Grails scaffolding. When trying to select a domain instance from the 'list' type page, I get the error "[domain name] not found with id null".
I am sure it is because of the Grails url [controller]/[action]/[id]. This id is a string and needs to be converted to an ObjectId for Grails queries.
Is there a way to do this so that it affects a specified domain or even better yet, all of the domains at once?
I guess as I'm writing my app, I can convert it to an ObjectId from within the action method, but I'd like to have the scaffolding work or provide a global solution.
I believe this is happening because the show() method (that the Grails scaffolding functionality generates as an action) accepts an id parameter of type Long ie.
def show(Long id) {
def suiteInstance = Suite.get(id)
if (!suiteInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'suite.label', default: 'MyDomainClass'), id])
redirect(action: "list")
return
}
[suiteInstance: suiteInstance]
}
which binds the id parameter to the argument. Because the ObjectId can't be converted to a Long, it ends up being null, hence the call to MyDomainClass.get(id) fails with the error message.
You can get around this by overriding the show() action in your scaffolded controller so that it expects an ObjectId or String, but I would say the proper fix for this is to update the Grails scaffolding plugin so it is a little more liberal in the types of IDs it accepts.
I had this problem as well. You can keep the domain object id as an ObjectId and update the controller as follows:
domain Object:
import org.bson.types.ObjectId;
class DomainObject {
ObjectId id
// Add other member variables...
}
Controller:
def show(String id) {
def domainObjectInstance = domainObject.get(new ObjectId(id))
if (!domainObjectInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'domainObject.label', default: 'DomainObject'), id])
redirect(action: "list")
return
}
[domainObjectInstance: domainObjectInstance]
}
You would also need to update your other controller methods that use id as well such as edit, update etc.
Additionally, if you want the grails default controller generation to work like this for all your domain objects you can update the template as coderLMN suggests.
The get(params.id) call in show() method will NOT convert params.id String to an ObjectId object, so the domain instance will be null, then the following code takes you to list action with an error message:
if (!exampleInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'example.label', default: 'Example'), params.id])
redirect(action: "list")
return
}
Possible solutions:
you can run "grails install-template" command, so that the scaffolding templates in src/templates/scaffolding/ directory can be modified. Then you have new scaffold ready to generate customized controllers, views, tests for all your Domain classes.
A simpler solution is to define the id property as String instead of ObjectId. A String id will be equal to objectId.toString(), in this case your scaffold will work.
In domain classes keep you id type as ObjectId and keep scaffold = true for all respective controllers.
In Domain class :
ObjectId id
In respective controller :
static scaffold = true
Clear all existing collections from Mongo
I guess that's sufficient to have Grails-Mongo app up & running, considering you have correctly configured mongo-plugin

ObjectDisposedException when getting foreign key field

I created my model using Db Context Generator, using EF 4.
My model is like this:
Program Table:
ID
Name
Group Table:
ID
Name
ProgramID (Associated to Program.ID)
I want to display these columns in my grid:
Program.Name - Group.Name
But grdGroups.DataSource = db.Groups.ToList()
doesn't return Program.Name
When I try to this I get ObjectDisposedException.
Partial Public Class Group
Public ReadOnly Property ProgramName() As String
Get
Return Program.Name
End Get
End Property
End Class
What's the best way to return the Program.Name to include it in the grid datasource?
When I try to this I get
ObjectDisposedException
The problem is lazy loading - EF did not materialize the related Program entity, hence when you try to access Program.Name it will try and re-query the DB, but the context has been disposed at this point, so you get an exception.
You can use an Include() query when you retrieve your Group entity, to specify that you also want to load the related Program entity, i.e. :
var groups = context.Groups.Include(x => x.Program);