Skip to content
Browse files

version 0.0.1

  • Loading branch information...
1 parent b1af77b commit 3d50a386fb502701dad1e37699a9fe7e5f788dff @pricco pricco committed Sep 6, 2011
View
1 .gitignore
@@ -0,0 +1 @@
+*.pyc
View
17 .project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>django-solr</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.python.pydev.PyDevBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.python.pydev.pythonNature</nature>
+ </natures>
+</projectDescription>
View
10 .pydevproject
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?>
+
+<pydev_project>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
+<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
+<path>/django-solr</path>
+</pydev_pathproperty>
+</pydev_project>
View
28 LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2011, Sophilabs
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of the author nor the names of other
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
View
2 MANIFEST.in
@@ -0,0 +1,2 @@
+include LICENSE
+include README
View
28 djangosolr/__init__.py
@@ -0,0 +1,28 @@
+"""
+ Solr Search Engine ORM for Django models*
+
+ Create, save, fetch, update and delete:
+ <code>
+ mv = MovieDocument(title='Jurassic Park', director='Steven Spielberg')
+ mv.save()
+
+ mv = MovieDocument.docuemnts.get(1)
+
+ mv.title = 'Jurassic Park I'
+ mv.save()
+
+ mv.delete()
+ </code>
+
+ Search:
+ <code>
+ MovieDocument.documents.query(Q('jurassic park') & Q('director', 'spielberg'))[:10]
+ </code>
+
+ *Solr Not Included
+"""
+
+__version__ = (0, 0, 1)
+
+from djangosolr.conf import inject_defaults
+inject_defaults()
View
18 djangosolr/conf/__init__.py
@@ -0,0 +1,18 @@
+
+#http://passingcuriosity.com/2010/default-settings-for-django-applications/
+
+def inject_defaults():
+ import default_settings
+ import sys
+
+ _app_settings = sys.modules['djangosolr.conf.default_settings']
+ _def_settings = sys.modules['django.conf.global_settings']
+ _settings = sys.modules['django.conf'].settings
+ for _k in dir(_app_settings):
+ if _k.isupper():
+ # Add the value to the default settings module
+ setattr(_def_settings, _k, getattr(_app_settings, _k))
+
+ # Add the value to the settings, if not already present
+ if not hasattr(_settings, _k):
+ setattr(_settings, _k, getattr(_app_settings, _k))
View
23 djangosolr/conf/default_settings.py
@@ -0,0 +1,23 @@
+
+DJANGOSOLR_ID_FIELD = 'id'
+DJANGOSOLR_TYPE_FIELD = 'type'
+
+DJANGOSOLR_FIELD_MAPPING = {
+ 'django.db.models.fields.AutoField': 'djangosolr.documents.fields.IntegerField',
+ 'django.db.models.fields.IntegerField': 'djangosolr.documents.fields.IntegerField',
+ 'django.db.models.fields.BigIntegerField': 'djangosolr.documents.fields.IntegerField',
+ 'django.db.models.fields.FloatField': 'djangosolr.documents.fields.FloatField',
+ 'django.db.models.fields.DecimalField': 'djangosolr.documents.fields.DecimalField',
+ 'django.db.models.fields.TextField': 'djangosolr.documents.fields.CharField',
+ 'django.db.models.fields.CharField': 'djangosolr.documents.fields.CharField',
+ 'django.db.models.fields.DateField': 'djangosolr.documents.fields.DateField',
+ 'django.db.models.fields.DateTimeField': 'djangosolr.documents.fields.DateField',
+ 'django.db.models.fields.BooleanField': 'djangosolr.documents.fields.BooleanField',
+ 'django.db.models.fields.NullBooleanField': 'djangosolr.documents.fields.BooleanField',
+}
+
+DJANGOSOLR_AUTOCOMMIT = True
+DJANGOSOLR_URL = 'http://localhost:8983/solr'
+DJANGOSOLR_SELECT_PATH = '/select'
+DJANGOSOLR_UPDATE_PATH = '/update/json'
+DJANGOSOLR_DELETE_PATH = '/update/json'
View
3 djangosolr/documents/__init__.py
@@ -0,0 +1,3 @@
+from djangosolr.documents.document import Document
+from djangosolr.documents.manager import Manager
+from djangosolr.documents.fields import Field, CharField, DateTimeField, DecimalField, FloatField, IntegerField, TextField
View
81 djangosolr/documents/document.py
@@ -0,0 +1,81 @@
+from djangosolr.documents.options import Options
+from djangosolr.documents.manager import ensure_default_manager
+from django.conf import settings
+
+class DocumentBase(type):
+
+ def __new__(cls, name, bases, attrs):
+ super_new = super(DocumentBase, cls).__new__
+ new_class = super_new(cls, name, bases, {'__module__': attrs.pop('__module__')})
+
+ attr_meta = attrs.pop('Meta', None)
+ if not attr_meta:
+ meta = getattr(new_class, 'Meta', None)
+ else:
+ meta = attr_meta
+ new_class.add_to_class('_meta', Options(meta))
+
+ if getattr(new_class, '_default_manager', None):
+ new_class._default_manager = None
+ new_class._base_manager = None
+
+ for obj_name, obj in attrs.items():
+ new_class.add_to_class(obj_name, obj)
+
+ new_class._prepare()
+
+ return new_class
+
+ def add_to_class(cls, name, value):
+ if hasattr(value, 'contribute_to_class'):
+ value.contribute_to_class(cls, name)
+ else:
+ setattr(cls, name, value)
+
+ def _prepare(cls):
+ opts = cls._meta
+ opts._prepare(cls)
+ ensure_default_manager(cls)
+
+class Document(object):
+
+ __metaclass__ = DocumentBase
+
+ def __init__(self, **kwargs):
+ for field in self._meta.fields:
+ if field.name in kwargs:
+ setattr(self, field.name, kwargs.pop(field.name))
+ else:
+ setattr(self, field.name, field.get_default())
+ if kwargs:
+ raise KeyError(kwargs.keys()[0])
+
+ @classmethod
+ def create(cls, om):
+ document = cls()
+ if isinstance(om, dict):
+ for field in cls._meta.fields:
+ name = cls._meta.type + '-' + field.name
+ if om.has_key(name):
+ setattr(document, field.name, field.convert(om[name]))
+ else:
+ for field in cls._meta.fields:
+ setattr(document, field.name, getattr(om, field.name))
+ return document
+
+ def save(self):
+ id = getattr(self, self._meta.pk.name)
+ type = self._meta.type
+ doc = {settings.DJANGOSOLR_ID_FIELD: type + '-' + str(id), settings.DJANGOSOLR_TYPE_FIELD: type}
+ for field in self._meta.fields:
+ value = field.prepare(getattr(self, field.name))
+ if value is None:
+ doc[type + '-' + field.name] = [] #BUG: https://issues.apache.org/jira/browse/SOLR-2714
+ else:
+ doc[type + '-' + field.name] = value
+ return self._default_manager._request('POST', settings.DJANGOSOLR_UPDATE_PATH, [('commit', 'true',)], { 'add': { 'overwrite': True, 'doc': doc}, 'commit': {} })
+
+ def delete(self):
+ id = getattr(self, self._meta.pk.name)
+ type = self._meta.type
+ return self._default_manager._request('POST', settings.DJANGOSOLR_DELETE_PATH, None, {'delete': { 'query': settings.DJANGOSOLR_ID_FIELD + ':' + type + '-' + str(id)}, 'commit': {} })
View
76 djangosolr/documents/fields.py
@@ -0,0 +1,76 @@
+import datetime, decimal
+
+class Field():
+
+ def __init__(self, type='string', name=None, stored=True, indexed=True, multivalued=False, primary_key=False):
+ self.type = type
+ self.name = name
+ self.stored = stored
+ self.indexed = indexed
+ self.multivalued = multivalued
+ self.primary_key = primary_key
+
+ def contribute_to_class(self, cls, name):
+ self.name = name
+ self._model = cls
+ cls._meta.add_field(self)
+
+ def get_default(self):
+ return None
+
+ def prepare(self, value):
+ return value
+
+ def convert(self, value):
+ return value
+
+class IntegerField(Field):
+
+ def __init__(self, **kwargs):
+ kwargs.setdefault('type', 'int')
+ Field.__init__(self, **kwargs)
+
+class CharField(Field):
+
+ def __init__(self, **kwargs):
+ kwargs.setdefault('type', 'string')
+ Field.__init__(self, **kwargs)
+
+class DateTimeField(Field):
+
+ def __init__(self, **kwargs):
+ kwargs.setdefault('type', 'date')
+ Field.__init__(self, **kwargs)
+
+ def prepare(self, value):
+ return value.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
+
+ def convert(self, value):
+ try:
+ return datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')
+ except ValueError:
+ return datetime.datetime.strptime(value, '%Y-%m-%d').date()
+
+class FloatField(Field):
+
+ def __init__(self, **kwargs):
+ kwargs.setdefault('type', 'float')
+ Field.__init__(self, **kwargs)
+
+class TextField(Field):
+
+ def __init__(self, **kwargs):
+ kwargs.setdefault('type', 'text')
+ Field.__init__(self, **kwargs)
+
+class DecimalField(Field):
+
+ def __init__(self, **kwargs):
+ kwargs.setdefault('type', 'float')
+ Field.__init__(self, **kwargs)
+
+ def prepare(self, value):
+ return float(value)
+
+ def convert(self, value):
+ return decimal.Decimal(value)
View
62 djangosolr/documents/manager.py
@@ -0,0 +1,62 @@
+import urllib, httplib2, json
+from djangosolr.documents.queryset import QuerySet
+from django.conf import settings
+
+class ManagerDescriptor(object):
+
+ def __init__(self, manager):
+ self.manager = manager
+
+ def __get__(self, instance, type=None):
+ if instance != None:
+ raise AttributeError("Manager isn't accessible via %s instances" % type.__name__)
+ return self.manager
+
+class Manager(object):
+
+ def contribute_to_class(self, model, name):
+ self._model = model
+ setattr(model, name, ManagerDescriptor(self))
+ if not getattr(model, '_default_manager', None):
+ model._default_manager = self
+
+ def _request(self, method, path, query=None, body=None):
+ try:
+ uri = '%s%s?wt=json' % (settings.DJANGOSOLR_URL, path,)
+ if query:
+ uri += '&' + urllib.urlencode(query)
+ if body:
+ body = json.dumps(body)
+ headers, body = httplib2.Http().request(uri=uri, method=method, body=body, headers={'Content-type': 'application/json'})
+ if headers['status'] == '200':
+ return json.loads(body)
+ raise Exception(body)
+ except:
+ raise
+
+ def _get_query_set(self):
+ return QuerySet(self._model)
+
+ def all(self):
+ return self._get_query_set().set('fq', settings.DJANGOSOLR_TYPE_FIELD + ':' + self._model._meta.type)
+
+ def get(self, id):
+ return self._get_query_set().set('q', settings.DJANGOSOLR_ID_FIELD + ':' + str(id))
+
+ def clear(self):
+ return self._request('POST', settings.DJANGOSOLR_DELETE_PATH, None, {'delete': { 'query': settings.DJANGOSOLR_TYPE_FIELD + ':' + self._model._meta.type}})
+
+def ensure_default_manager(cls):
+ if not getattr(cls, '_default_manager', None):
+ cls.add_to_class('documents', Manager())
+ cls._base_manager = cls.documents
+ elif not getattr(cls, '_base_manager', None):
+ default_mgr = cls._default_manager.__class__
+ if (default_mgr is Manager):
+ cls._base_manager = cls._default_manager
+ else:
+ for base_class in default_mgr.mro()[1:]:
+ if (base_class is Manager):
+ cls.add_to_class('_base_manager', base_class())
+ return
+ raise AssertionError("Should never get here. Please report a bug, including your model and model manager setup.")
View
50 djangosolr/documents/options.py
@@ -0,0 +1,50 @@
+from django.utils.importlib import import_module
+
+class Options(object):
+
+ def __init__(self, meta):
+ self.meta = meta
+ self.model = None
+ self.type = None
+ self.fields = []
+ self.pk = None
+
+ def add_field(self, field):
+ self.fields.append(field)
+ if field.primary_key:
+ self.pk = field
+
+ def contribute_to_class(self, cls, name):
+ cls._meta = self
+
+ self.name = cls.__name__.lower()
+
+ if self.meta:
+ meta_attrs = self.meta.__dict__.copy()
+ for name in self.meta.__dict__:
+ if name.startswith('_'):
+ del meta_attrs[name]
+ for attr_name in ['model', 'type']:
+ if attr_name in meta_attrs:
+ setattr(self, attr_name, meta_attrs.pop(attr_name))
+ elif hasattr(self.meta, attr_name):
+ setattr(self, attr_name, getattr(self.meta, attr_name))
+
+ if meta_attrs != {}:
+ raise TypeError("'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys()))
+
+ if not self.type:
+ self.type = self.name
+
+ del self.meta
+
+ def _prepare(self, model):
+ from django.conf import settings
+ mapping = settings.DJANGOSOLR_FIELD_MAPPING
+ if model._meta.model:
+ for df in model._meta.model._meta.local_fields:
+ kwargs = dict(name=df.name, stored=True, indexed=True, multivalued=False, primary_key=df.primary_key)
+ sc = df.__class__.__module__ + '.' + df.__class__.__name__
+ f_module, f_classname = mapping[sc].rsplit('.', 1)
+ f = getattr(import_module(f_module), f_classname)(**kwargs)
+ model.add_to_class(f.name, f)
View
140 djangosolr/documents/queryset.py
@@ -0,0 +1,140 @@
+from django.conf import settings
+
+class Query(object):
+
+ def __init__(self):
+ self._params = {}
+
+ def clone(self):
+ clone = Query()
+ clone._params.update(self._params)
+ return clone
+
+ def set(self, name, value):
+ self._params[name] = value
+
+ def set_limits(self, start, stop):
+ if start is not None:
+ self._params['start'] = start
+ elif 'start' in self._params:
+ del self._params['start']
+ if stop is not None:
+ self._params['rows'] = stop - (start or 0)
+ elif 'rows' in self._params:
+ del self._params['rows']
+
+class QuerySet(object):
+
+ def __init__(self, model, query=None):
+ self._model = model
+ self._query = query or Query()
+ self._response = None
+
+ self._result_cache = None
+ self._iter = None
+
+ def _get_response(self):
+ if self._response is None:
+ self._query._params.setdefault('q', '*:*')
+ self._response = self._model._default_manager._request('GET', settings.DJANGOSOLR_SELECT_PATH, self._query._params)
+ return self._response
+ response = property(_get_response)
+
+ def _clone(self):
+ return QuerySet(self._model, self._query.clone())
+
+ def __len__(self):
+ if self._result_cache is None:
+ if self._iter:
+ self._result_cache = list(self._iter)
+ else:
+ self._result_cache = list(self.iterator())
+ elif self._iter:
+ self._result_cache.extend(self._iter)
+ return len(self._result_cache)
+
+ def __iter__(self):
+ if self._result_cache is None:
+ self._iter = self.iterator()
+ self._result_cache = []
+ if self._iter:
+ return self._result_iter()
+ return iter(self._result_cache)
+
+ def _result_iter(self):
+ pos = 0
+ while 1:
+ upper = len(self._result_cache)
+ while pos < upper:
+ yield self._result_cache[pos]
+ pos = pos + 1
+ if not self._iter:
+ raise StopIteration
+ if len(self._result_cache) <= pos:
+ self._fill_cache()
+
+ def _fill_cache(self, num=None):
+ if self._iter:
+ try:
+ for i in range(num or 100):
+ self._result_cache.append(self._iter.next())
+ except StopIteration:
+ self._iter = None
+
+ def __nonzero__(self):
+ if self._result_cache is not None:
+ return bool(self._result_cache)
+ try:
+ iter(self).next()
+ except StopIteration:
+ return False
+ return True
+
+ def __getitem__(self, k):
+ if not isinstance(k, (slice, int, long)):
+ raise TypeError
+ assert ((not isinstance(k, slice) and (k >= 0))
+ or (isinstance(k, slice) and (k.start is None or k.start >= 0)
+ and (k.stop is None or k.stop >= 0))), \
+ "Negative indexing is not supported."
+
+ if self._result_cache is not None:
+ if self._iter is not None:
+ if isinstance(k, slice):
+ if k.stop is not None:
+ bound = int(k.stop)
+ else:
+ bound = None
+ else:
+ bound = k + 1
+ if len(self._result_cache) < bound:
+ self._fill_cache(bound - len(self._result_cache))
+ return self._result_cache[k]
+
+ if isinstance(k, slice):
+ qs = self._clone()
+ if k.start is not None:
+ start = int(k.start)
+ else:
+ start = None
+ if k.stop is not None:
+ stop = int(k.stop)
+ else:
+ stop = None
+ qs._query.set_limits(start, stop)
+ return k.step and list(qs)[::k.step] or qs
+ try:
+ qs = self._clone()
+ qs._query.set_limits(k, k + 1)
+ return list(qs)[0]
+ except:
+ raise IndexError(0)
+
+ def iterator(self):
+ for doc in self.response['response']['docs']:
+ yield self._model.create(doc)
+
+ def set(self, name, value):
+ clone = self._clone()
+ clone._query.set(name, value)
+ return clone
View
0 djangosolr/managment/__init__.py
No changes.
View
0 djangosolr/managment/commands/__init__.py
No changes.
View
0 example/__init__.py
No changes.
View
14 example/manage.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+from django.core.management import execute_manager
+import sys, os
+
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))
+
+try:
+ import settings # Assumed to be in the same directory.
+except ImportError:
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+ sys.exit(1)
+
+if __name__ == "__main__":
+ execute_manager(settings)
View
0 example/movies/__init__.py
No changes.
View
11 example/movies/documents.py
@@ -0,0 +1,11 @@
+from example.movies.models import Movie
+from djangosolr.documents import Document, TextField
+
+class MovieDocument(Document):
+
+ text = TextField()
+
+ class Meta:
+ model = Movie
+ type = 'movie'
+ #default_search_field = 'text'
View
7 example/movies/models.py
@@ -0,0 +1,7 @@
+from django.db import models
+
+class Movie(models.Model):
+
+ title = models.CharField()
+ director = models.CharField()
+
View
16 example/movies/tests.py
@@ -0,0 +1,16 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+
+class SimpleTest(TestCase):
+ def test_basic_addition(self):
+ """
+ Tests that 1 + 1 always equals 2.
+ """
+ self.assertEqual(1 + 1, 2)
View
1 example/movies/views.py
@@ -0,0 +1 @@
+# Create your views here.
View
78 example/schema.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<schema>
+ <types>
+ <fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
+ <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>
+ <fieldtype name="binary" class="solr.BinaryField"/>
+ <fieldType name="int" class="solr.TrieIntField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+ <fieldType name="float" class="solr.TrieFloatField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+ <fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+ <fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+ <fieldType name="date" class="solr.TrieDateField" omitNorms="true" precisionStep="0" positionIncrementGap="0"/>
+
+ <fieldType name="tint" class="solr.TrieIntField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
+ <fieldType name="tfloat" class="solr.TrieFloatField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
+ <fieldType name="tlong" class="solr.TrieLongField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
+ <fieldType name="tdouble" class="solr.TrieDoubleField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
+ <fieldType name="tdate" class="solr.TrieDateField" omitNorms="true" precisionStep="6" positionIncrementGap="0"/>
+
+ <fieldType name="sint" class="solr.SortableIntField" sortMissingLast="true" omitNorms="true"/>
+ <fieldType name="slong" class="solr.SortableLongField" sortMissingLast="true" omitNorms="true"/>
+ <fieldType name="sfloat" class="solr.SortableFloatField" sortMissingLast="true" omitNorms="true"/>
+ <fieldType name="sdouble" class="solr.SortableDoubleField" sortMissingLast="true" omitNorms="true"/>
+
+ <fieldType name="text" class="solr.TextField" positionIncrementGap="100">
+ <analyzer type="index">
+ <tokenizer class="solr.StandardTokenizerFactory"/>
+ <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+ <filter class="solr.LowerCaseFilterFactory"/>
+ </analyzer>
+ <analyzer type="query">
+ <tokenizer class="solr.StandardTokenizerFactory"/>
+ <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+ <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+ <filter class="solr.LowerCaseFilterFactory"/>
+ </analyzer>
+ </fieldType>
+
+ <fieldType name="text_rev" class="solr.TextField" positionIncrementGap="100">
+ <analyzer type="index">
+ <tokenizer class="solr.StandardTokenizerFactory"/>
+ <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+ <filter class="solr.LowerCaseFilterFactory"/>
+ <filter class="solr.ReversedWildcardFilterFactory" withOriginal="true"
+ maxPosAsterisk="3" maxPosQuestion="2" maxFractionAsterisk="0.33"/>
+ </analyzer>
+ <analyzer type="query">
+ <tokenizer class="solr.StandardTokenizerFactory"/>
+ <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+ <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
+ <filter class="solr.LowerCaseFilterFactory"/>
+ </analyzer>
+ </fieldType>
+
+ <fieldtype name="ignored" stored="false" indexed="false" multiValued="true" class="solr.StrField" />
+
+ </types>
+
+
+ <fields>
+
+ <field name="id" type="string" indexed="true" stored="true" multiValued="false" required="true"/>
+ <field name="type" type="string" indexed="true" stored="true" multiValued="false" required="true"/>
+
+ <field name="movie-title" type="string" indexed="true" stored="true" multivalued="false" />
+ <field name="movie-director" type="string" indexed="true" stored="true" multivalued="false" />
+ <field name="movie-text" type="string" indexed="true" stored="true" multivalued="true" />
+
+ </fields>
+
+ <uniqueKey>id</uniqueKey>
+ <defaultSearchField>id</defaultSearchField>
+
+ <solrQueryParser defaultOperator="AND"/>
+
+ <copyField source="movie-title" dest="movie-text" />
+ <copyField source="movie-director" dest="movie-text" />
+
+</schema>
View
147 example/settings.py
@@ -0,0 +1,147 @@
+# Django settings for example project.
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+ # ('Your Name', 'your_email@example.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+ 'NAME': '', # Or path to database file if using sqlite3.
+ 'USER': '', # Not used with sqlite3.
+ 'PASSWORD': '', # Not used with sqlite3.
+ 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
+ 'PORT': '', # Set to empty string for default. Not used with sqlite3.
+ }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'America/Chicago'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale
+USE_L10N = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/media/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash.
+# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
+MEDIA_URL = ''
+
+# Absolute path to the directory static files should be collected to.
+# Don't put anything in this directory yourself; store your static files
+# in apps' "static/" subdirectories and in STATICFILES_DIRS.
+# Example: "/home/media/media.lawrence.com/static/"
+STATIC_ROOT = ''
+
+# URL prefix for static files.
+# Example: "http://media.lawrence.com/static/"
+STATIC_URL = '/static/'
+
+# URL prefix for admin static files -- CSS, JavaScript and images.
+# Make sure to use a trailing slash.
+# Examples: "http://foo.com/static/admin/", "/static/admin/".
+ADMIN_MEDIA_PREFIX = '/static/admin/'
+
+# Additional locations of static files
+STATICFILES_DIRS = (
+ # Put strings here, like "/home/html/static" or "C:/www/django/static".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+)
+
+# List of finder classes that know how to find static files in
+# various locations.
+STATICFILES_FINDERS = (
+ 'django.contrib.staticfiles.finders.FileSystemFinder',
+ 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
+)
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = '7yh44x!g$1ru4!s-j)!l%$4unqk586b2-i8ub%j49d#6zrt5u7'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+ 'django.template.loaders.filesystem.Loader',
+ 'django.template.loaders.app_directories.Loader',
+# 'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.common.CommonMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+)
+
+ROOT_URLCONF = 'example.urls'
+
+TEMPLATE_DIRS = (
+ # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+)
+
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'djangosolr',
+ 'movies'
+ # Uncomment the next line to enable the admin:
+ # 'django.contrib.admin',
+ # Uncomment the next line to enable admin documentation:
+ # 'django.contrib.admindocs',
+)
+
+# A sample logging configuration. The only tangible logging
+# performed by this configuration is to send an email to
+# the site admins on every HTTP 500 error.
+# See http://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+ 'version': 1,
+ 'disable_existing_loggers': False,
+ 'handlers': {
+ 'mail_admins': {
+ 'level': 'ERROR',
+ 'class': 'django.utils.log.AdminEmailHandler'
+ }
+ },
+ 'loggers': {
+ 'django.request': {
+ 'handlers': ['mail_admins'],
+ 'level': 'ERROR',
+ 'propagate': True,
+ },
+ }
+}
View
11 example/tests.py
@@ -0,0 +1,11 @@
+import sys, os
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))
+
+os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings'
+from example.movies.documents import MovieDocument
+
+MovieDocument.documents.clear()
+
+MovieDocument(title='Jurassic Park I', director='Steven Spielberg').save()
+
+print list(MovieDocument.documents.all())
View
17 example/urls.py
@@ -0,0 +1,17 @@
+from django.conf.urls.defaults import patterns, include, url
+
+# Uncomment the next two lines to enable the admin:
+# from django.contrib import admin
+# admin.autodiscover()
+
+urlpatterns = patterns('',
+ # Examples:
+ # url(r'^$', 'example.views.home', name='home'),
+ # url(r'^example/', include('example.foo.urls')),
+
+ # Uncomment the admin/doc line below to enable admin documentation:
+ # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
+
+ # Uncomment the next line to enable the admin:
+ # url(r'^admin/', include(admin.site.urls)),
+)
View
22 setup.py
@@ -0,0 +1,22 @@
+from setuptools import setup, find_packages
+
+setup(
+ name='django-solr',
+ version='0.0.1',
+ description='Django-Solr search',
+ author='Sophilabs',
+ author_email='contact@sophilabs.com',
+ url='http://github.com/sophilabs/django-solr/',
+ packages=find_packages(),
+ classifiers=[
+ 'Development Status :: 3 - Alpha',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Framework :: Django',
+ ],
+ include_package_data=True,
+ zip_safe=False,
+)

0 comments on commit 3d50a38

Please sign in to comment.
Something went wrong with that request. Please try again.