I'm have a solr schema with dynamic field of different types in. Eg in the schema.xml there are:
<dynamicField name="*_s" type="string" indexed="true" stored="true"/>
<dynamicField name="*_i" type="int" indexed="true" stored="true"/>
<dynamicField name="*_l" type="long" indexed="true" stored="true"/>
<dynamicField name="*_f" type="float" indexed="true" stored="true"/>
<dynamicField name="*_d" type="double" indexed="true" stored="true"/>
And I want to access these field using a SolrJ annotated POJO. I know I can have different Map references for each data type in the POJO like this:
...
#Field("*_s")
public Map<String, String> strings;
#Field("*_i")
public Map<String, Integer> integers;
...
But is it possible to have all dynamic fields stored in the same map? I was thinking something like:
...
#Field("*_s")
#Field("*_i")
public Map<String, Object> dynamicFields;
...
The only documentation I can find about SolrJ, POJOs and dynamic fields is an old feature request:
https://issues.apache.org/jira/browse/SOLR-1129
I worked out the matching of the 'pattern' value in the #Field annotation doesn't have to match what's in your schema.xml. So, I defined a map in my doc class:
#Field("*DF")
private Map<String, Object> dynamicFields;
and then in the schema.xml the dynamicFields have patterns postfixed by 'DF':
<dynamicField name="*_sDF" type="string" indexed="true" stored="true"/>
<dynamicField name="*_siDF" type="sint" indexed="true" stored="true"/>
<dynamicField name="*_tDF" type="date" indexed="true" stored="true"/>
Now all the dynamicField with different value types get stored and retrieved using solrServer.addBean(doc) and solrResponse.getBeans(Doc.class). This is with Solr 3.2.0 It wasn't working with 1.4..
Related
Asked in Liferay Forums
I've created a permission helper class for my Question entity following the steps explained here:
package net.carlosduran.nomedes.web.internal.security.permission.resource;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.security.permission.PermissionChecker;
import com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import net.carlosduran.nomedes.db.model.Question;
#Component(immediate = true)
public class QuestionPermission {
public static boolean contains(
PermissionChecker permissionChecker, Question question, String actionId) throws PortalException {
return _questionModelResourcePermission.contains(permissionChecker, question, actionId);
}
public static boolean contains(
PermissionChecker permissionChecker, long entryId, String actionId) throws PortalException {
return _questionModelResourcePermission.contains(permissionChecker, entryId, actionId);
}
#Reference(
target = "(model.class.name=net.carlosduran.nomedes.db.model.Question)",
unbind = "-")
protected void setEntryModelPermission(ModelResourcePermission<Question> modelResourcePermission) {
_questionModelResourcePermission = modelResourcePermission;
}
private static ModelResourcePermission<Question> _questionModelResourcePermission;
}
In a MVCRenderCommand class I reference it this way:
#Reference
protected QuestionPermission _questionPermission;
If I include this reference, the render class doesn't work (I've tried it with different render classes).
In the moment I delete it, the render class works without a problem.
The code for the service.xml file is this:
<?xml version="1.0"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 7.4.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_7_4_0.dtd">
<service-builder dependency-injector="ds" package-path="net.carlosduran.nomedes.db">
<namespace>Nomedes</namespace>
<entity name="Question" local-service="true" uuid="true">
<!-- PK fields -->
<column name="questionId" primary="true" type="long"></column>
<!-- Group instance -->
<column name="groupId" type="long"></column>
<!-- Audit fields -->
<column name="companyId" type="long"></column>
<column name="userId" type="long"></column>
<column name="userName" type="String"></column>
<column name="createDate" type="Date"></column>
<column name="modifiedDate" type="Date"></column>
<column name="title" type="String"></column>
<column name="summary" type="String"></column>
<column name="description" type="String"></column>
<column name="status" type="int" />
<column name="statusByUserId" type="long" />
<column name="statusByUserName" type="String" />
<column name="statusDate" type="Date" />
<finder name="GroupId" return-type="Collection">
<finder-column name="groupId"></finder-column>
</finder>
<reference entity="Group" package-path="com.liferay.portal"></reference>
</entity>
<exceptions>
<exception>QuestionValidation</exception>
</exceptions>
</service-builder>
Can anyone tell me what's wrong? Thanks
The problem was that I miss to add a service attribute to the component annotation in the QuestionPermission class.
service = QuestionPermission.class
After adding it, it works fine. It would look like this:
#Component(
immediate = true,
service = QuestionPermission.class
)
In a module that I do NOT own is the following act_window.
<record id="act_465" model="ir.actions.act_window">
<field name="name">Act 465</field>
<field name="res_model">stock.move</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('available','=',True)]</field>
</record>
I would like to add the following AND criteria to the domain in my separate module:
('canceled', '!=', True)
I know I could OVERRIDE the act_window and the value of domain by [('available','=',True),('canceled', '!=', True)]. However doing so would totally remove what was already in place ('available','=',True). Meaning that if the owner of the base module change it's domain, I'll override his changes anyway.
Question
How could I EXTEND the domain by saying "I would like to add ('available','=',True) to the existing domain" ?
You can't, the action domain is a char field and there is no attribute to specify how to extend a specific record value.
It is possible to use the function tag in the XML data file to call the write method and update the domain or create a function to update the action domain with a given domain list as a parameter.
Use the function tag to update the domain as a string (we suppose that the domain is set, list of tuples provided as a string)
Example:
<function model="ir.actions.act_window" name="write">
<value eval="[ref('sale.action_quotations_with_onboarding')]"/>
<value model="ir.actions.act_window" eval="{'domain': obj().env['ir.actions.act_window'].browse(ref('sale.action_quotations_with_onboarding')).domain[:-1] + ', (\'invoice_status\', \'=\', \'to invoice\')]'}"/>
</function>
Create a function to update the domain as a list with a given domain as a parameter
Example:
class IrActionsActWindow(models.Model):
_inherit = 'ir.actions.act_window'
def update_domain(self, new_domain=None):
if new_domain:
self.write({'domain': safe_eval(self.domain) + new_domain})
Use the function to call the update_domain method:
<function model="ir.actions.act_window"
name="update_domain"
eval="[ref('sale.action_quotations_with_onboarding'), [('invoice_status', '=', 'to invoice')]]"/>
I like to reduce the loading time when running the Default.aspx in the browser because it is fetching a huge data from the Table in the database.
When I use criteria.SetMaxResults(200) the grid load only 200 datas but I should load all the data's in the RadGrid from the table in the database .When I use criteria.SetFetchSize(200) it takes also a huge time to Load can anyone help me to find a solution for this.
Here is the code
ICriteria criteria = session.CreateCriteria(typeof(Ttable1));
criteria.SetMaxResults(200);
IList result = criteria.List();
if (result.Count > 0)
{
grid1.DataSource = result;
grid1.DataBind();
}
else
{
grid1.DataSource = new string[] { };
grid1.DataBind();
}
Here the Mapping is
<class name="Ttable1" table="Ttable1" lazy="false" mutable="true">
<cache usage="read-only"/>
<id name="ID" column="ID" >
</id>
<property name="CustNumber" column="CustNumber" type="String" />
<property name="CustName" column="CustName" type="String"/>
<property name="PNo" column="PNor" type="String"/>
<property name="ONo" column="ONo" type="String"/>
<property name="Ln" column="Lns" type="String"/>
<property name="Comments" column="Comments" type="String"/>
<property name="size" column="size" type="String"/>
<property name="Qty" column="Qty" type="String"/>
</class>
</hibernate-mapping>
Here is the class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Business.entities
{
public class Ttable1
{
public virtual string displayobj { get; set; }
public virtual Ttable1 TLQDataObj { get; set; }
public virtual int? ID { get; set; }
public virtual string CustNumber { get; set; }
public virtual string CustName { get; set; }
public virtual string PNo { get; set; }
public virtual string ONo { get; set; }
public virtual string Ln { get; set; }
public virtual string Comments { get; set; }
public virtual string size { get; set; }
public virtual string Qty { get; set; }
}
}
Are you show all data in grid with paging? if yes you can create custom paging for your grid by load 10 rows for every page you entered you can first retrieve paged data by using query.SetFirstResult(firstrowinpage); and query.SetMaxResults(pageSize); (HQL)
, and set Radgrid property grid.AllowCustomPaging = true; to allow custom paging and set VirtualItemCount to know grid how number of rows to allow paging.
grdItems.VirtualItemCount =numberofrows();
It is impossible to tell what is wrong from what you have given us. We don't know your mappings, how many columns does the table have, nothing.
Problems could be caused either by:
N+1 selects because all your associations are eager
You have some column in the database with huge data (maybe some attachments or something)
You should also try to paginate your results.
You must debug NHibernate queries and see what is going on. You can do that either by using show_sql, by logging with some logger, or with some profiler like NHProf or even a SQL profiler.
You will probably see many queries executed because there is no lazy load enabled.
EDIT:
If you have some columns in the table that you don't want to load unless you access them, you can set their property mapping with lazy=true, or you can map these columns with components and set these components as lazy=true. This way when you access these columns for the first time, additional queries will be performed.
If you need to fetch data for display purposes you should create a light object from the table data.
In any case, if fetching speed is your only concern you could set the object as mutable=false and set the cache <cache usage="read-only"/>.
You could do this in code also:
ICriteria crit = session.CreateCriteria(typeof(Ttable1));
crit.SetCacheable(true);
crit.SetCacheMode(CacheMode.Get);
crit.SetMaxResults(200);
IList result = crit.List();
Mapping -
<class name="Ttable1" table="Ttable1" lazy="false" mutable="false">
<cache usage="read-only"/>
<id name="ID">
<column name="ID"/>
<generator class="identity"/>
</id>
<property name="CustNumber" column="CustNumber" type="String" />
<property name="CustName" column="CustName" type="String"/>
<property name="PNo" column="PNor" type="String"/>
<property name="ONo" column="ONo" type="String"/>
<property name="Ln" column="Lns" type="String"/>
<property name="Comments" column="Comments" type="String"/>
<property name="size" column="size" type="String"/>
<property name="Qty" column="Qty" type="String"/>
</class>
I've been wrecking my mind on how to get my tagging of entities to
work. I'll get right into some database structuring:
tblTag
TagId - int32 - PK
Name
tblTagEntity
TagId - PK
EntityId - PK
EntityType - string - PK
tblImage
ImageId - int32 - PK
tblBlog
BlogId - int32 - PK
class Image
Id
EntityType { get { return "MyNamespace.Entities.Image"; }
IList<Tag> Tags;
class Blog
Id
EntityType { get { return "MyNamespace.Entities.Blog"; }
IList<Tag> Tags;
The obvious problem I have here is that EntityType is an identifer but
doesn't exist in the database. If anyone could help with the this
mapping I'd be very grateful.
You don't need the entity type. Take a look at any-type mapping (it stores the type name in the database in the relation table, but you don't need it in the entity model).
See this blog post by ayende.
Edit: tried to write an example.
You could have an own table for each tagged object, this is easy and straight forward, you don't even need any types:
<class name="Tag">
<!-- ... -->
<property name="Name"/>
</class>
<class name="Image">
<!-- ... -->
<bag name="Tags" table="Image_Tags">
<key column="Image_FK"/>
<many-to-many class="Tag" column="TagId "/>
</bag>
</class>
Tried to use some advanced features to map it into a single table, but I think it doesn't work this way:
<class name="Tag">
<!-- ... -->
<property name="Name"/>
<bag name="Objects" table="tblTagEntity" access="noop">
<key column="TagId"/>
<many-to-any id-type="System.Int64" meta-type="System.String">
<meta-value
value="IMAGE"
class="Image"/>
<meta-value
value="BLOG"
class="Blog"/>
<column name="EntityType"/>
<column name="EntityId"/>
</many-to-any>
</bag>
</class>
<class name="Image">
<!-- ... -->
<bag name="Tags" table="tblTagEntity" where="EntityType='IMAGE'">
<key column="EntityId"/>
<many-to-many class="Tag" column="TagId "/>
</bag>
</class>
The tricks here are:
access="noop" to specify the foreign key without having a property in the entity model, see this post.
where="EntityType='IMAGE'" to filter the loaded data.
The problem is that most probably the EntityType is not set to any useful value. This could be fixed somewhere, but I don't think that it is worth the effort.
Someone else has probably a better idea.
Edit 2: another (working) solution
make the association table an entity:
in short:
Tag => TagEntity: not mapped or one-to-many inverse (noop)
TagEntity => Tag: many-to-one
TagEntity => Object: any
Object => TagEntity: one-to-many inverse
This should work straight forward.
classes:
class Tag
{
string Name { get; set; }
}
class TagEntity
{
Tag Tag { get; set; }
object Entity { get; set; }
}
class Image
{
IList<TagEntity> tags { get; private set; }
}
The only drawback seems to be that you have to make sure that the bidirectional associations are consistent without loading to much data. Note that inverse collections are not stored.
Edit 2: Performance notes
When you add / remove tags, you could do a trick. TagEntity has a reference to the tagged entity. The Entity also has a list of TagEntities, but this is marked as inverse. (This means, they are loaded, but not stored.)
You can add and remove tags without loading the Entity an without loading all the tags.
Adding:
Get Tag to add (or load proxy if you have the id of the tag)
Load Entity (just proxy, using session.Load, no db access here)
create new TagEntity, assign tag and entity-proxy
save TagEntity
Removing:
Get TagEntity to remove
delete TagEntity.
Within the session, you don't have this tag assigned to/removed from the TagEntity. This works fine assumed that you only add or remove tags within this transaction.
I you define a list of TagEntities on the Tag, you can do the same, without loading all the TagEntities just to add or remove one.
You could make EntityType an Enum in your code. And/or, you could try making EntityType an actual entity in your database (tblEntityType).
Got Stefans final solution to work! Here's my final mappings:
Image
<bag name="TagEntites" table="tblTagEntity" cascade="all" fetch="join" inverse="true" where="EntityType='EntityImage'">
<key column="EntityId"></key>
<one-to-many class="TagEntity" />
</bag>
TagEntity
<id name="Id">
<column name="TagEntityId"></column>
<generator class="identity" />
</id>
<any name="Entity" id-type="System.Int32" meta-type="System.String">
<meta-value value="EntityImage" class="Image" />
<column name="EntityType"></column>
<column name="EntityId"></column>
</any>
<many-to-one name="Tag" class="Tag" cascade="all" fetch="join">
<column name="TagId"></column>
</many-to-one>
I'm using nhibernate to store some user settings for an app in a SQL Server Compact Edition table.
This is an excerpt the mapping file:
<property name="Name" type="string" />
<property name="Value" type="string" />
Name is a regular string/nvarchar(50), and Value is set as ntext in the DB
I'm trying to write a large amount of xml to the "Value" property. I get an exception every time:
#p1 : String truncation: max=4000, len=35287, value='<lots of xml..../>'
I've googled it quite a bit, and tried a number of different mapping configurations:
<property name="Name" type="string" />
<property name="Value" type="string" >
<column name="Value" sql-type="StringClob" />
</property>
That's one example. Other configurations include "ntext" instead of "StringClob". Those configurations that don't throw mapping exceptions still throw the string truncation exception.
Is this a problem ("feature") with SQL CE? Is it possible to put more than 4000 characters into a SQL CE database with nhibernate? If so, can anyone tell me how?
Many thanks!
Okay, with many thanks to Artur in this thread, here's the solution:
Inherit from the SqlServerCeDriver with a new one, and override the InitializeParamter method:
using System.Data;
using System.Data.SqlServerCe;
using NHibernate.Driver;
using NHibernate.SqlTypes;
namespace MySqlServerCeDriverNamespace
{
/// <summary>
/// Overridden Nhibernate SQL CE Driver,
/// so that ntext fields are not truncated at 4000 characters
/// </summary>
public class MySqlServerCeDriver : SqlServerCeDriver
{
protected override void InitializeParameter(
IDbDataParameter dbParam,
string name,
SqlType sqlType)
{
base.InitializeParameter(dbParam, name, sqlType);
if (sqlType is StringClobSqlType)
{
var parameter = (SqlCeParameter)dbParam;
parameter.SqlDbType = SqlDbType.NText;
}
}
}
}
Then, use this driver instead of NHibernate's in your app.config
<nhibernateDriver>MySqlServerCeDriverNamespace.MySqlServerCeDriver , MySqlServerCeDriverNamespace</nhibernateDriver>
I saw a lot of other posts where people had this problem, and solved it by just changing the sql-type attribute to "StringClob" - as attempted in this thread.
I'm not sure why it wouldn't work for me, but I suspect it is the fact that I'm using SQL CE and not some other DB. But, there you have it!
<property name="Value" type="string" />
<column name="Value" sql-type="StringClob" />
</property>
I'm assuming this is a small typo, since you've closed the property tag twice. Just pointing this out, in case it wasn't a typo.
Try <property name="Value" type="string" length="4001" />
Tried:
<property name="Value" type="string" length="4001" />
and
<property name="Value" type="string" >
<column name="Value" sql-type="StringClob" length="5000"/>
</property>
Neither worked, I'm afraid... Same exception - it still says that the max value is 4000.
Why are you using the sub-element syntax?
try:
<property name='Value' type='StringClob' />
On my current deplyoment of SQL CE and NHibernate I use a length of 4001. Then NHibernate generates the stuff as NTEXT instead of NVARCHAR.
Try that.
Another thing to use with NHibernate and SQL CE is:
<session-factory>
...
<property name="connection.release_mode">on_close</property>
</session-factory>
That solves some other problems for me atleast.
After reading your post this modification got it working in my code
protected override void InitializeParameter(IDbDataParameter dbParam,string name,SqlType sqlType)
{
base.InitializeParameter(dbParam, name, sqlType);
var stringType = sqlType as StringSqlType;
if (stringType != null && stringType.LengthDefined && stringType.Length > 4000)
{
var parameter = (SqlCeParameter)dbParam;
parameter.SqlDbType = SqlDbType.NText;
}
}