Skip to content

Commit accf883

Browse files
committed
Add full text search feature and boosting features.fix some bugs and added more tests
1 parent 82164ae commit accf883

File tree

8 files changed

+124
-14
lines changed

8 files changed

+124
-14
lines changed

pyravendb/custom_exceptions/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,7 @@ class NonUniqueObjectException(Exception):
1616

1717
class FetchConcurrencyException(Exception):
1818
pass
19+
20+
21+
class ArgumentOutOfRangeException(Exception):
22+
pass

pyravendb/data/document_convention.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def json_default(o):
7373
return Utils.datetime_to_string(o)
7474
elif isinstance(o, timedelta):
7575
return Utils.timedelta_to_str(o)
76-
elif getattr(o, "__dict__"):
76+
elif getattr(o, "__dict__", None):
7777
return o.__dict__
7878
else:
7979
raise TypeError(repr(o) + " is not JSON serializable (Try add a json default method to store convention)")

pyravendb/data/indexes.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class FieldIndexing(Enum):
3333
# Index the field's value without using an Analyzer, so it can be searched.
3434
not_analyzed = "NotAnalyzed"
3535

36+
def __str__(self):
37+
return self.value
38+
3639

3740
class IndexDefinition(object):
3841
def __init__(self, index_map, name=None, **kwargs):
@@ -88,7 +91,8 @@ def map(self, value):
8891

8992
def to_json(self):
9093
return {"Analyzers": self.analyzers, "DisableInMemoryIndexing": self.disable_in_memory_indexing,
91-
"Fields": self.fields, "Indexes": self.indexes, "IndexId": self.index_id,
94+
"Fields": self.fields, "Indexes": {key: str(self.indexes[key]) for key in self.indexes},
95+
"IndexId": self.index_id,
9296
"InternalFieldsMapping": self.internal_fields_mapping, "IsCompiled": self._is_compiled,
9397
"IsMapReduce": self.is_map_reduce, "IsSideBySideIndex": self.is_side_by_side_index,
9498
"IsTestIndex": self.is_test_index, "LockMode": str(self.lock_mod), "Map": self.map,

pyravendb/store/session_query.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from enum import Enum
2-
from pyravendb.custom_exceptions.exceptions import ErrorResponseException
2+
from pyravendb.custom_exceptions.exceptions import *
33
from pyravendb.data.indexes import IndexQuery
44
from pyravendb.tools.utils import Utils
55
from datetime import timedelta
@@ -72,7 +72,6 @@ def where_equals(self, field_name, value):
7272
if value is not None and not isinstance(value, str) and field_name is not None:
7373
sort_hint = self.session.conventions.get_default_sort_option(type(value).__name__)
7474
if sort_hint:
75-
field_name = "{0}_Range".format(field_name)
7675
if sys.version_info.major > 2:
7776
if value > sys.maxsize:
7877
sort_hint = self.session.conventions.get_default_sort_option("long")
@@ -97,6 +96,20 @@ def where(self, **kwargs):
9796
self.where_equals(field_name, kwargs[field_name])
9897
return self
9998

99+
def search(self, field_name, search_terms):
100+
"""
101+
for more complex text searching
102+
103+
@param field_name:The field name in the index you want to query.
104+
:type str
105+
@param search_terms: the terms you want to query
106+
:type str
107+
"""
108+
search_terms = Utils.quote_key(str(search_terms))
109+
search_terms = self._lucene_builder(search_terms, "search")
110+
self.query_builder += "{0}:{1}".format(field_name, search_terms)
111+
return self
112+
100113
def where_ends_with(self, field_name, value):
101114
"""
102115
To get all the document that ends with the value in the giving field_name
@@ -255,6 +268,16 @@ def add_not(self):
255268
self.negate = True
256269
return self
257270

271+
def boost(self, value):
272+
if len(self.query_builder) < 1:
273+
raise InvalidOperationException("Missing where clause")
274+
if value < 0:
275+
raise ArgumentOutOfRangeException("boost", "boost factor must be a positive number")
276+
if value != 1:
277+
# 1 is the default
278+
self.query_builder += "^{0}".format(value)
279+
return self
280+
258281
def _execute_query(self):
259282
self.session.increment_requests_count()
260283
conventions = self.session.conventions
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from pyravendb.tests.test_base import TestBase
2+
from pyravendb.data.indexes import IndexDefinition
3+
from pyravendb.data.indexes import FieldIndexing
4+
from pyravendb.store.document_store import documentstore
5+
from datetime import datetime
6+
7+
8+
class LastFm(object):
9+
def __init__(self, artist="", track_id="", title="", datetime_time=None, tags=None):
10+
self.artist = artist
11+
self.track_id = track_id
12+
self.title = title
13+
self.datetime_time = datetime_time if datetime_time is not None else datetime.now()
14+
self.tags = tags if tags is not None else []
15+
16+
17+
class LastFmAnalyzed(object):
18+
class Result(object):
19+
def __init__(self, query):
20+
self.query = query
21+
22+
def __init__(self):
23+
index_map = ("from song in docs.LastFms "
24+
"select new {"
25+
"query = new object[] {"
26+
"song.artist,"
27+
"((object)song.datetime_time),"
28+
"song.tags,"
29+
"song.title,"
30+
"song.track_id}}")
31+
32+
indexes = {"query": FieldIndexing.analyzed}
33+
34+
self.index_definition = IndexDefinition(index_map=index_map, indexes=indexes)
35+
36+
def execute(self, store):
37+
store.database_commands.put_index(LastFmAnalyzed.__name__, self.index_definition, True)
38+
39+
40+
class FullTextSearchTest(TestBase):
41+
@classmethod
42+
def setUpClass(cls):
43+
super(FullTextSearchTest, cls).setUpClass()
44+
cls.db.put("LastFms/1", {"artist": "Tania Maria", "track_id": "TRALPJJ128F9311763", "title": "Come With Me",
45+
"datetime_time": datetime.now(), "tags": None},
46+
{"Raven-Entity-Name": "LastFms", "Raven-Python-Type": "full_text_search_test.LastFm"})
47+
cls.db.put("LastFms/2", {"artist": "Meghan Trainor", "track_id": "TRBCNGI128F42597B4", "title": "Me Too",
48+
"datetime_time": datetime.now(), "tags": None},
49+
{"Raven-Entity-Name": "LastFms", "Raven-Python-Type": "full_text_search_test.LastFm"})
50+
cls.db.put("LastFms/3", {"artist": "Willie Bobo", "track_id": "TRAACNS128F14A2DF5", "title": "Spanish Grease",
51+
"datetime_time": datetime.now(), "tags": None},
52+
{"Raven-Entity-Name": "LastFms", "Raven-Python-Type": "full_text_search_test.LastFm"})
53+
cls.document_store = documentstore(cls.default_url, cls.default_database)
54+
cls.document_store.initialize()
55+
LastFmAnalyzed().execute(cls.document_store)
56+
57+
def test_full_text_search_one(self):
58+
with self.document_store.open_session() as session:
59+
query = list(session.query(object_type=LastFm, wait_for_non_stale_results=True,
60+
index_name=LastFmAnalyzed.__name__).search("query", search_terms="Me"))
61+
self.assertTrue(str(query[0].title) == "Come With Me" and str(query[1].title) == "Me Too")
62+
63+
def test_full_text_search_two(self):
64+
with self.document_store.open_session() as session:
65+
query = list(session.query(object_type=LastFm, wait_for_non_stale_results=True,
66+
index_name=LastFmAnalyzed.__name__).search("query", search_terms="Me").search(
67+
"query", search_terms="Bobo"))
68+
self.assertEqual(len(query), 3)
69+
70+
def test_full_text_search_witg_boost(self):
71+
with self.document_store.open_session() as session:
72+
query = list(session.query(object_type=LastFm, wait_for_non_stale_results=True,
73+
index_name=LastFmAnalyzed.__name__).search("query", search_terms="Me").boost(10).
74+
search("query", search_terms="Bobo").boost(2))
75+
self.assertTrue(str(query[0].title) == "Come With Me" and str(query[1].title) == "Me Too" and str(
76+
query[2].title) == "Spanish Grease")

pyravendb/tests/session_tests/query_test.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66

77

88
class Product(object):
9-
def __init__(self, name, key):
9+
def __init__(self, name, key, order):
1010
self.name = name
1111
self.key = key
12+
self.order = order
1213

1314

1415
class Company(object):
@@ -35,11 +36,12 @@ def setUpClass(cls):
3536
{"Raven-Entity-Name": "Products", "Raven-Python-Type": "query_test.Product"})
3637
cls.db.put("products/103", {"name": "test107", "key": 6},
3738
{"Raven-Entity-Name": "Products", "Raven-Python-Type": "query_test.Product"})
38-
cls.db.put("products/108", {"name": "new_testing", "key": 90},
39+
cls.db.put("products/108", {"name": "new_testing", "key": 90, "order": "d"},
3940
{"Raven-Entity-Name": "Products", "Raven-Python-Type": "query_test.Product"})
4041
cls.db.put("orders/105", {"name": "testing_order", "key": 92, "product": "products/108"},
4142
{"Raven-Entity-Name": "Orders"})
42-
cls.db.put("company/1", {"name": "withNesting", "product": {"name": "testing_order", "key": 150}},
43+
cls.db.put("company/1",
44+
{"name": "withNesting", "product": {"name": "testing_order", "key": 4, "order": None}},
4345
{"Raven-Entity-Name": "Companies"})
4446
cls.document_store = documentstore(cls.default_url, cls.default_database)
4547
cls.document_store.initialize()
@@ -115,7 +117,7 @@ def test_query_with_order_by_descending(self):
115117
with self.document_store.open_session() as session:
116118
query_results = list(
117119
session.query(wait_for_non_stale_results=True).where_not_none("order").order_by_descending("order"))
118-
self.assertEqual(query_results[0].order, "c")
120+
self.assertEqual(query_results[0].order, "d")
119121

120122
def test_where_not_None(self):
121123
found_none = False

pyravendb/tools/utils.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def make_initialize_dict(document, entity_init):
147147
for i in range(remainder, len(args)):
148148
entity_initialize_dict[args[i]] = document.get(args[i], defaults[i - remainder])
149149
else:
150-
if keywords == "kwargs":
150+
if keywords:
151151
entity_initialize_dict = document
152152
else:
153153
for key in document:
@@ -184,7 +184,8 @@ def to_lucene(value, action):
184184
query_text = "[{0} TO {1}]".format(
185185
Utils.numeric_to_lucene_syntax(value[0]) if value[0] is not None else "*",
186186
Utils.numeric_to_lucene_syntax(value[1]) if value[1] is not None else "NULL")
187-
187+
elif action == "search":
188+
query_text = "({0})".format(value)
188189
else:
189190
query_text = value
190191
if value is None:

setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33
setup(
44
name='pyravendb',
55
packages=find_packages(),
6-
version='1.1.1',
6+
version='1.2.0',
77
description='This is the official python client for RavenDB document database',
88
author='Idan Haim Shalom',
99
author_email='[email protected]',
1010
url='https://github.com/IdanHaim/RavenDB-Python-Client',
1111
license='GNU',
12-
keywords='pyravendb',
12+
keywords='pyravendb',
1313
install_requires=
1414
[
1515
"pycrypto >= 2.6.1",
1616
"requests >= 2.9.1",
1717
"inflector >= 2.0.11",
18-
"enum >= 0.4.6",
18+
"enum >= 0.4.6",
1919
],
20-
zip_safe=False
20+
zip_safe=False
2121
)

0 commit comments

Comments
 (0)