Add a FULLTEXT index in Doctrine 2 using annotations? - mysql

I know that Doctrine 2 doesn't support FULLTEXT indexes. I'm actually using a result set mapping and native queries to FULLTEXT search innodb tables (MySQL 5.6). But I still need to mark one or more entity fields as part of the index.
Is there any way to add the index using annotations? It seems that #Index annotation doesn't specify the type of...

According to DDC-3014 in the issue tracker of Doctrine, the possibility to specify full-text indices using annotations was implemented on April 14 and will be available in release 2.5. If you don't like to wait, you could try to use the unstable, development version or to backport the commit implementing the feature.
Here is a usage example:
/**
* #Entity
* #Table(indexes={#Index(columns={"content"}, flags={"fulltext"})})
*/
class Comment
{
/**
* #Column(type="text")
*/
private $content;
...
}

Here is an example how to make a fulltext index with the yaml mapping driver.
Doctrine\Tests\ORM\Mapping\Comment:
type: entity
fields:
content:
type: text
indexes:
xy_fulltext:
columns: ["name", "content", "keywords"]
flags: fulltext

Related

JPA Hibernate - Multiple Database Dialects and nvarchar(length) data type

I have to do a project using JPA + Hibernate in which I'm using 3 dialects: MySQL5InnoDBDialect, MSSQL2012Dialect and Oracle12cDialect.
Right now I have a specification which is telling me that for some column from:
Oracle database, I have to use NVARCHAR2(LENGTH) data type
MySql database, I have to use VARCHAR(LENGTH) data type
MSSQL database, I have to use NVARCHAR(LENGTH) data type
... and here is my problem..
If I use:
#Column(name="columnName" length = 255)
private String columnName;
hibernate generates varchar(255) and this is good just for MySQL
If I use:
#Column(name="columnName", columnDefinition="nvarchar2(255)")
private String columnName;
it's not possible in MySQL, i get error because of columnDefinition, but in oracle is okay
I tried to customize MySQL dialect creating
public class CustomMySQL5InnoDBDialect extends MySQL5InnoDBDialect{
public CustomMySQL5InnoDBDialect() {
super();
registerColumnType(Types.NVARCHAR, "nvarchar2($l)");//$l not $1
registerHibernateType(Types.NVARCHAR, StandardBasicTypes.STRING.getName());
}
}
and giving this class in hibernate configuration for MySQL dialect.
I have the same problem in MySQL if I'm using columnDefinition property.
Can you help with this problem please?
The solution is to make use of the feature that the JPA API spec provides you with for just this situation. Define a file orm.xml for each datastore that you need to support, and enable the requisite one when using each database. See this link for details of the file format. That way you don't need to think about hacking the internal features of whichever JPA provider you are using, and you also retain JPA provider portability, as well as database portability
The idea of putting schema specific information info (static) Java annotations is an odd one, even more so when wanting database portability.

Doctrine: Use "WITH PARSER" clause for fulltext index definition

I'm using Symfony 3.3.10 and MySQL 5.7.18, and trying to use a fulltext index like:
/**
* #ORM\Table(name="article", indexes={#ORM\Index(name="body_idx", columns={"body"}, flags={"fulltext"})})
* #ORM\Entity(repositoryClass="AppBundle\Repository\ArticleRepository")
*/
class Article
{
With this entity definition, bin/console doctrine:schema:update --dump-sql gives
CREATE FULLTEXT INDEX body_idx ON article (body);
but I want to use WITH PARSER like
CREATE FULLTEXT INDEX body_idx ON article (body) WITH PARSER mecab;
Question: How can I achieve this? My intuitive is any of the following would be the strategy, but I've not come to the answer: 1) configuring ORM\Index's option to add an extra string to the query or 2) somehow using raw SQL to build the query.

Schema name in Create index statement while generating datanucleus JDO schema

I am trying to generate schema from the DataNucleus SchemaTool for a mysql database, that will store countries and states. Here is a sample of that code:
#PersistenceCapable
Public class State{
private String shortCode;
private String fullName;
#Column(allowsNull = "true",name="country_id")
private Country countryId;
}
The following are my schemaGeneration properties:
datanucleus.ConnectionDriverName=com.mysql.jdbc.Driver
datanucleus.ConnectionURL=jdbc:mysql://localhost:3306/geog
datanucleus.ConnectionUserName=geog
datanucleus.ConnectionPassword=geogPass
datanucleus.schema.validateTables=true
datanucleus.mapping.Catalog=geog
datanucleus.mapping.Schema=geog
In my Country class as well, I have a mapping from a Collection, so that the FK reference for States to the Country table is built correctly.
But there is one problem. In the SQL script generated, the Index part has the Schema name as part of the index name itself, which fails the entire script. Here is that piece:
CREATE INDEX `GEOG`.`MST_STATE_N49` ON `GEOG`.`MST_STATE` (`COUNTRY_ID`);
Notice the schema name in the GEOG.MST_STATE_N49 part of the index' name.
I tried setting the schema and catalog name to blank but that yields a ''.MST_STATE_N49 which still fails.
I am using MySQL Server 5.7.17 using the 5.1.42 version of the JDBC driver (yes, not the latest) on Data nucleus JDO 3.1
Any hints on how I can get rid of the schema/catalog name in the generated DDL?
Why are you putting "datanucleus.mapping.Schema" when using MySQL ? MySQL doesnt use schema last I looked. Similarly the "datanucleus.mapping.Catalog" is effectively defined by your URL! MySQL only actually supports JDBC catalog, mapping on to "database", as per this post. Since DataNucleus simply uses the JDBC driver then catalog is the only useful input.
Consequently removal of both schema and catalog properties will DEFAULT to the right place.
After the comment above from Neil Stockton, I commented out both the properties and it worked. Effectively, this is what is needed:
datanucleus.ConnectionDriverName=com.mysql.jdbc.Driver
datanucleus.ConnectionURL=jdbc:mysql://localhost:3306/geog
datanucleus.ConnectionUserName=geog
datanucleus.ConnectionPassword=geogPass
datanucleus.schema.validateTables=true
Hopefully, I can get the answer to the other question (Pt. 2 in my reply-comment above) as well.

How to create indexes on multiple columns

We have the following entity relationships where a User belongs to a particular Organization. My queries either look like "select * from User where org=:org" or "select * from User where org=:org and type=:type"
I have separate indexes on the User class. The first query will be fine, because of the Index on the foreign key element. Does the second query mandate a multi columnindex on org and type columns. If so how should I annotate to create one such index.
#Entity
class User {
...
#ManyToOne
#ForeignKey
#Index
Organization org;
#Index
Type type;
...
}
This is doable using the Hibernate specific #Table annotation. From the documentation:
2.4.1 Entity
...
#Table(appliesTo="tableName", indexes = { #Index( name="index1", columnNames={"column1", "column2"} ) } ) creates the defined indexes on the columns of table tableName. This can be applied on the primary table or any secondary table. The #Tables annotation allows your to apply indexes on different tables. This annotation is expected where #javax.persistence.Table or #javax.persistence.SecondaryTable(s) occurs.
Reference
Hibernate Annotations Reference Guide
2.4. Hibernate Annotation Extensions
As you can read in JSR-000338 Java Persistence 2.1 Proposed Final Draft Specification:
11.1.23 Index Annotation
The Index annotation is used in schema generation. Note that it is not necessary to specify an index for a primary key, as the primary key index will be created automatically, however, the Index annotation may be used to specify the ordering of the columns in the index for the primary key.
#Target({}) #Retention(RUNTIME)
public #interface Index {
String name() default "";
String columnList();
boolean unique() default false;
}
The syntax of the columnList element is a column_list, as follows:
column::= index_column [,index_column]*
index_column::= column_name [ASC | DESC]
The persistence provider must observe the specified ordering of the
columns.
If ASC or DESC is not specified, ASC (ascending order) is
assumed.
Usage example:
#Table(indexes = {
#Index(columnList = "org,type"),
#Index(columnList = "another_column")})
Yes, it is possible using JPA 2.1 as seen in the specification here:
http://download.oracle.com/otndocs/jcp/persistence-2_1-pfd-spec/index.html
on page 445 it states that
The Index annotation is used in schema generation
columnList (Required) The names of the columns to be included in the index.
An example of usage can be seen here:
http://java-persistence-performance.blogspot.co.uk/2013/03/but-what-if-im-not-querying-by-id.html
It seems that the syntax is the same or very similar to Hibernate.

Django MySQL full text search

I need to implement full text search for my Django application, running MySQL as backend.
Let's say I've got a model as follows:
class MyItem(models.Model):
title = models.CharField()
short_description = models.TextField()
description = models.TextField()
I would like to have results first for search term occurences in title, then in short_description and at the end in description field. I'll be happier if I don't have to use additional modules/applications for this task.
The previously highest rated answer is deprecated. As of Django 1.10 there is no more search field lookup for MySQL databases (see the search section in the 1.10 documentation).
The release notes for 1.10 also propose a solution to this, by defining a custom lookup:
###__search query lookup
The search lookup, which supports MySQL only and is extremely limited in
features, is deprecated. Replace it with a custom lookup:
from django.db import models
class Search(models.Lookup):
lookup_name = 'search'
def as_mysql(self, compiler, connection):
lhs, lhs_params = self.process_lhs(compiler, connection)
rhs, rhs_params = self.process_rhs(compiler, connection)
params = lhs_params + rhs_params
return 'MATCH (%s) AGAINST (%s IN BOOLEAN MODE)' % (lhs, rhs), params
models.CharField.register_lookup(Search)
models.TextField.register_lookup(Search)
You can use full text search in django
MyItem.objects.filter(title__search="some search text")
One thing is - you can't define a fulltext index from a Django model, you need to do in directly in a database (using PHPMyAdmin or SQL query)
See Django documentation for field lookup called search
I don't know if it helps now but I've created a new Python Library for Django which supports MySQLs' and MariaDBs' native full-text search engine over one or multiple columns:
You can have a look at it on the GitHub repository
There's also a description how to install it, use it and how to create the FULLTEXT INDEX stuff via Django migrations (Django 1.7+).
If you've configured the indexes and set the SearchManager for your model you should be able to run something like:
Mymodel.objects.search('Something')
Mymodel.objects.search('Somet*')
Mymodel.objects.search('+Something -Awesome')
Just wanted to update this topic because I didn't find an acceptable solution so far and it might help someone out there as well :)
Cheers
Domi
If you are looking for a beefy solution I recommend http://haystacksearch.org/
It is very well thought out.
Django provides full-text functionality for PostgreSQL's only.
From django docs regarding full-text search:
Example:
Entry.objects.filter(headline__search="+Django -jazz Python")
SQL equivalent:
SELECT ... WHERE MATCH(tablename, headline) AGAINST (+Django -jazz Python IN BOOLEAN MODE);
Note this is only available in MySQL and requires direct manipulation of the database to add the full-text index. By default Django uses BOOLEAN MODE for full text searches. See the MySQL documentation for additional details.
Now to the direct manipulation of the database. In MySQL you can create full-text index by following these steps (source article):
Open command prompt, and enter mysql -u root -p. On prompt enter the root password.
Enter use your_db_name to switch to your django database.
Enter CREATE FULLTEXT INDEX index_name ON table_name (column_names).
That's it! FTS indexing in enabled in your django database. Now you can use the django's rich QuerySet API to do full-text searches.
Edit: The above quote no longer exists in the django version >1.9.