Multi-Map Indexes
-
Multi-Map indexes allow you to index data from multiple collections, like polymorphic data or any data common to different types.
-
Learn how to index polymorphic data
Learn how to create Multi-Map-Reduce indexes -
In this page:
_add_map
The _add_map
method is used to map fields from a single collection, e.g. Dogs
.
Let's assume that we have Dog
and Cat
classes, both inheriting from the class Animal
:
class Dog(Animal): ...
class Cat(Animal): ...
class Animal(ABC):
def __init__(self, name: str = None):
self.name = name
We can define our index using _add_map
and query it as follows:
class Animals_ByName(AbstractMultiMapIndexCreationTask):
def __init__(self):
super().__init__()
self._add_map("from c in docs.Cats select new { c.name }")
self._add_map("from d in docs.Dogs select new { d.name }")
class Animals_ByName(AbstractJavaScriptIndexCreationTask):
def __init__(self):
super().__init__()
self.maps = {
"map('cats', function (c){ return {Name: c.Name}})",
"map('dogs', function (d){ return {Name: d.Name}})",
}
results = list(session.query_index_type(Animals_ByName, Animal).where_equals("name", "Mitzy"))
from index 'Animals/ByName'
where Name = 'Mitzy'
Searching across multiple collections
Another great usage of Multi-Map indexes is smart-search.
To search for products, companies, or employees by their name, you need to define the following index:
class Smart_Search(AbstractMultiMapIndexCreationTask):
class Result:
def __init__(
self, Id: str = None, display_name: str = None, collection: object = None, content: List[str] = None
):
self.Id = Id
self.display_name = display_name
self.collection = collection
self.content = content
class Projection:
def __init__(self, Id: str = None, display_name: str = None, collection: str = None):
self.Id = Id
self.display_name = display_name
self.collection = collection
def __init__(self):
super().__init__()
self._add_map(
"from c in docs.Companies select new {"
"Id = c.Id,"
"content = new[]"
"{"
" c.name"
"},"
"display_name= c.name, "
'collection = MetadataFor(c)["@collection"]'
"}"
)
self._add_map(
"from p in docs.Products select new {"
"Id = p.Id,"
"content = new[]"
"{"
" p.name"
"},"
"display_name = p.name,"
'collection = MetadataFor(p)["@collection"]'
"}"
)
self._add_map(
"from e in docs.Employees select new {"
"Id = e.Id,"
"content = new[]"
"{"
" e.first_name,"
" e.last_name"
"},"
'display_name = e.first_name + " " + e.last_name,'
'collection = MetadataFor(e)["@collection"]'
"}"
)
# mark 'content' field as analyzed which enables full text search operations
self._index("content", FieldIndexing.SEARCH)
# storing fields so when projection (e.g. ProjectInto) requests only those fields,
# data will come from index only, not from storage
self._store("Id", FieldStorage.YES)
self._store("display_name", FieldStorage.YES)
self._store("collection", FieldStorage.YES)
and query it using:
results = list(
session.query_index_type(Smart_Search, Smart_Search.Result)
.search("content", "Lau*")
.select_fields(Smart_Search.Projection)
)
for result in results:
print(f"{result.collection}: {result.display_name}")
# Companies: Laughing Bacchus Wine Cellars
# Products: Laughing Lumberjack Lager
# Employees: Laura Callahan
Remarks
Remember that all map functions must output objects
with an identical shape (the field names have to match).