Summary: Working with Space Documents
Overview
Unlike POJOs, which force users to design a fixed data schema (in the form of a class definition) and adhere to it, a document is much more dynamic - users can add and remove properties at runtime as necessary. A Document always belongs to a certain type, represented by the class SpaceTypeDescriptor. Before a certain Document instance is written to the space, its type should be introduced to it. The type has a name and controls metadata such as identifier property, routing property and which properties are initially indexed (naturally, you can also index new properties at runtime after adding them to your documents).
Note that the Document type does not describe the properties themselves (except for the names of the ID and Routing properties). These are completely dynamic and each instance can have a different set of properties (although in most cases Document instances of the same type are likely to have identical or similar set of properties). Schema Evolution with Space DocumentsSince a SpaceDocument is completely dynamic by nature, it is very easy to change or evolve your data model without ever taking down the Space. Simply change your application code to add additional properties or remove existing ones, and you're good to go. Even better, old and new versions can co-exist since the space does not enforce any restriction with regards to the property set of documents that belong to a certain type. This is a much more lightweight model in comparison to the classic POJO model, where a recompilation and in many cases a full space restart is required to change the data schema. If POJO model cannot be replaced with Document model, yet some level of schema evolution is desired within the POJO model, Dynamic Properties can be used. Type DefinitionBefore we start writing and reading SpaceDocument from the space, we need an initial schema definition of the document type. For example, suppose we're implementing an electronic commerce system, and decided we need a type called Product with the following properties:
We also decide that CatalogNumber will be a primary key, partitioning will be done by the Category property, and the properties Name, Price should be indexed since they participate in most of the queries executed. Remember, the type definition is for metadata only, so we're not concerned about Description and other such fields in the type definition, because Description isn't used for indexing or any other metadata. The following is an example of how to introduce a new document type:
Spring Namespace Configuration
<os-core:space id="space" url="/./space" > <os-core:space-type type-name="Product" > <os-core:id property="CatalogNumber"/> <os-core:routing property="Category"/> <os-core:basic-index path="Name"/> <os-core:extended-index path="Price"/> </os-core:space-type> </os-core:space> <os-core:giga-space id="gigaSpace" space="space"/> Plain Spring XML <bean id="space" class="org.openspaces.core.space.UrlSpaceFactoryBean"> <property name="url" value="/./space" /> <property name="spaceTypes" > <list> <ref bean="productType"/> </list> </property> </bean> <bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean"> <property name="space" ref="space"/> </bean> <bean name="productType" class="org.openspaces.core.config.GigaSpaceDocumentTypeDescriptorFactoryBean"> <property name="typeName" value="Product"/> <property name="idProperty"> <bean class="org.openspaces.core.config.SpaceIdProperty"> <property name="propertyName" value="CatalogNumber"></property> </bean> </property> <property name="routingProperty"> <bean class="org.openspaces.core.config.SpaceRoutingProperty"> <property name="propertyName" value="Category"></property> </bean> </property> <property name="indexes"> <list> <bean class="org.openspaces.core.config.BasicIndex"> <property name="path" value="Name"></property> </bean> <bean class="org.openspaces.core.config.ExtendedIndex"> <property name="path" value="Price"></property> </bean> </list> </property> </bean> Code public void registerProductType(GigaSpace gigaspace) { // Create type descriptor: SpaceTypeDescriptor typeDescriptor = new SpaceTypeDescriptorBuilder("Product") .idProperty("CatalogNumber") .routingProperty("Category") .addPropertyIndex("Name", SpaceIndexType.BASIC) .addPropertyIndex("Price", SpaceIndexType.EXTENDED) .create(); // Register type: gigaspace.getTypeManager().registerTypeDescriptor(typeDescriptor); } Note that this code does not reflect the complete model - most of the properties does not need to be introduced to the schema. Only properties with special roles (ID, Routing) are part of the schema definition. These meta model settings cannot be changed without restarting the space or dropping the type, clearing all its instances and reintroducing it again. Creating and Writing DocumentsTo create a document create a Map<String,Object> with the requested properties, create a SpaceDocument object using the type name and properties, and write it to the space using the regular GigaSpace write method: public void writeProduct1(GigaSpace gigaspace) { // 1. Create the properties: Map<String, Object> properties = new HashMap<String, Object>(); properties.put("CatalogNumber", "hw-1234"); properties.put("Category", "Hardware"); properties.put("Name", "Anvil"); properties.put("Price", 9.99f); properties.put("Tags", new String[] {"heavy", "anvil"}); Map<String, Object> features = new HashMap<String, Object>(); features.put("Manufacturer", "Acme"); features.put("RequiresAssembly", false); features.put("Weight", 100); properties.put("Features", features); Map<String, Object> review1 = new HashMap<String, Object>(); review1.put("Name", "Wile E. Coyote"); review1.put("Rate", 1); review1.put("Comments", "Don't drop this on your toe, it will hurt."); Map<String, Object> review2 = new HashMap<String, Object>(); review2.put("Name", "Road Runner"); review2.put("Rate", 5); review2.put("Comments", "Beep Beep!"); properties.put("Reviews", new Map[] {review1, review2}); // 2. Create the document using the type name and properties: SpaceDocument document = new SpaceDocument("Product", properties); // 3. Write the document to the space: gigaspace.write(document); } Another way is to use the DocumentProperties class provided, which extends HashMap to provide fluent coding: public void writeProduct2(GigaSpace gigaspace) { // 1. Create the properties: DocumentProperties properties = new DocumentProperties() .setProperty("CatalogNumber", "av-9876") .setProperty("Category", "Aviation") .setProperty("Name", "Jet Propelled Pogo Stick") .setProperty("Price", 19.99f) .setProperty("Tags", new String[] {"New", "Cool", "Pogo", "Jet"}) .setProperty("Features", new DocumentProperties() .setProperty("Manufacturer", "Acme") .setProperty("RequiresAssembly", true) .setProperty("NumberOfParts", 42)) .setProperty("Reviews", new DocumentProperties[] { new DocumentProperties() .setProperty("Name", "Wile E. Coyote") .setProperty("Rate", 1), new DocumentProperties() .setProperty("Name", "Road Runner") .setProperty("Rate", 5)}); // 2. Create the document using the type name and properties: SpaceDocument document = new SpaceDocument("Product", properties); // 3. Write the document to the space: gigaspace.write(document); }
Reading and Removing DocumentsThere are three types of document queries: Template QueryThis type of query uses a SpaceDocument with type and any other set of properties values as a template for the query public SpaceDocument readProductByTemplate(GigaSpace gigaSpace) { // Create template: SpaceDocument template = new SpaceDocument("Product"); template.setProperty("Name", "Anvil"); // Read: SpaceDocument result = gigaSpace.read(template); return result; } SQL QueryYou can use the SqlQuery to search for matching SpaceDocument entries. public SpaceDocument readProductBySQL(GigaSpace gigaSpace) { // Create query: SQLQuery<SpaceDocument> query = new SQLQuery<SpaceDocument>("Product", "Price > ?"); query.setParameter(1, 15f); // Read: SpaceDocument result = gigaSpace.read(query); return result; }
Queries on nested properties are supported. For example, to read products manufactured by Acme: public SpaceDocument[] readProductBySQLNested(GigaSpace gigaSpace) { // Create query: SQLQuery<SpaceDocument> query = new SQLQuery<SpaceDocument>("Product", "Features.Manufacturer = ?"); query.setParameter(1, "Acme"); // Read: SpaceDocument[] result = gigaSpace.readMultiple(query, 10); return result; } ID Based QueryFor example: Read a document of type Product whose ID is hw-1234: public SpaceDocument readProductById(GigaSpace gigaSpace) { return gigaSpace.readById(new IdQuery<SpaceDocument>("Product", "hw-1234")); } Queries by multiple Ids are supported. For example: public SpaceDocument[] readProductByMultipleIds(GigaSpace gigaSpace) { Object[] ids = new Object[] {"hw-1234", "av-9876"}; ReadByIdsResult<SpaceDocument> result = gigaSpace.readByIds(new IdsQuery<SpaceDocument>("Product", ids)); return result.getResultsArray(); }
Nested PropertiesThe Document properties values can be either scalars (integers, strings, enumuerations, etc), collections (arrays, lists), or nested properties (Map or an extension of map, such as DocumentProperties). Values must adhere to the same restrictions as in the POJO model (e.g. be serializable). Nested properties can be queried by using the dot ('.') notation to describe paths, as shown above.
IndexingProperties and nested paths can be indexed to boost queries performance. In the type registration sample above the Name and Price properties are indexed. Since the schema is flexible and new properties might be added after the type has been registered, it is possible to add indexes dynamically as well. For more information about indexing, see the Indexing page. EventsEvent containers (both polling container and notify container) support Space Document entries. Here is a simple example of a polling event container configuration using a Document:
Annotation
<!-- Enable scan for OpenSpaces and Spring components --> <context:component-scan base-package="com.mycompany"/> <!-- Enable support for @Polling annotation --> <os-events:annotation-support /> <os-core:space id="space" url="/./space"> <os-core:space-type type-name="Product" > <os-core:id property="CatalogNumber"/> <os-core:routing property="Category"/> <os-core:basic-index path="Name"/> <os-core:extended-index path="Price"/> </os-core:space-type> </os-core:space> <os-core:giga-space id="gigaSpace" space="space"/> @EventDriven @Polling public class SimpleListener { @EventTemplate SpaceDocument unprocessedData() { SpaceDocument template = new SpaceDocument("Product"); template.setProperty("Name","Anvil"); return template; } @SpaceDataEvent public SpaceDocument eventListener(SpaceDocument event) { //process Data here } } Namespace <os-core:space id="space" url="/./space" > <os-core:space-type type-name="Product" > <os-core:id property="CatalogNumber"/> <os-core:routing property="Category"/> <os-core:basic-index path="Name"/> <os-core:extended-index path="Price"/> </os-core:space-type> </os-core:space> <os-core:giga-space id="gigaSpace" space="space"/> <bean id="simpleListener" class="SimpleListener" /> <os-events:polling-container id="eventContainer" giga-space="gigaSpace"> <os-core:template> <bean class="com.gigaspaces.document.SpaceDocument"> <constructor-arg value="Product"/> <constructor-arg type="java.util.Map"> <map> <entry key="Name" value="Anvil" /> </map> </constructor-arg> </bean> </os-core:template> <os-events:listener> <os-events:annotation-adapter> <os-events:delegate ref="simpleListener"/> </os-events:annotation-adapter> </os-events:listener> </os-events:polling-container> Plain XML <bean id="space" class="org.openspaces.core.space.UrlSpaceFactoryBean"> <property name="url" value="/./space" /> </bean> <bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean"> <property name="space" ref="space" /> </bean> <bean id="simpleListener" class="SimpleListener" /> <bean id="eventContainer" class="org.openspaces.events.polling.SimplePollingEventListenerContainer"> <property name="gigaSpace" ref="gigaSpace" /> <property name="template"> <bean class="com.gigaspaces.document.SpaceDocument"> <constructor-arg value="Product"/> <constructor-arg type="java.util.Map"> <map> <entry key="Name" value="Anvil" /> </map> </constructor-arg> </bean> </property> <property name="eventListener"> <bean class="org.openspaces.events.adapter.AnnotationEventListenerAdapter"> <property name="delegate" ref="simpleListener" /> </bean> </property> </bean> Code GigaSpace gigaSpace = // either create the GigaSpace or get it by injection SpaceDocument template = new SpaceDocument("Product"); template.setProperty("Name","Anvil"); SimplePollingEventListenerContainer pollingEventListenerContainer = new SimplePollingContainerConfigurer(gigaSpace) .template(template) .eventListenerAnnotation(new Object() { @SpaceDataEvent public void eventHappened() { eventCalled.set(true); } }).pollingContainer(); // when needed dispose of the notification container pollingEventListenerContainer.destroy(); FIFOFIFO Support is off by default with Document entries (same as with POJO). To enable FIFO support, modify the type introduction code and set the desired FIFO support mode. For example:
Spring Namespace Configuration
<os-core:space id="space" url="/./space" > <os-core:space-type type-name="Product" fifo-support="OPERATION" > <!-- other properties definition --> </os-core:space-type> </os-core:space> <os-core:giga-space id="gigaSpace" space="space"/> Plain Spring XML <bean id="space" class="org.openspaces.core.space.UrlSpaceFactoryBean"> <property name="url" value="/./space" /> <property name="spaceTypes" > <list> <ref bean="productType"/> </list> </property> </bean> <bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean"> <property name="space" ref="space"/> </bean> <bean name="productType" class="org.openspaces.core.config.GigaSpaceDocumentTypeDescriptorFactoryBean"> <property name="typeName" value="Product"/> <!-- other properties definition --> <property name="fifoSupport" value="OPERATION"/> </bean> Code // Create type descriptor: SpaceTypeDescriptor typeDescriptor = new SpaceTypeDescriptorBuilder("Product") // Other type descriptor settings. .fifoSupport(FifoSupport.OPERATION) .create(); // Register type: gigaspace.getTypeManager().registerTypeDescriptor(typeDescriptor);
Transactions and Optimistic LockingTransactions and isolation modifiers semantics is identical to the POJO semantics. For more information about transactions, see the Transaction Management page. Optimistic locking is disabled by default with Document entries (same as with POJO). To enable it, modify the type introduction code and set the optimistic locking support. For example:
Spring Namespace Configuration
<os-core:space id="space" url="/./space" /> <os-core:space-type type-name="Product" optimistic-lock="true" > <!-- other properties definition --> </os-core:space-type> </os-core:space> <os-core:giga-space id="gigaSpace" space="space"/> Plain Spring XML <bean id="space" class="org.openspaces.core.space.UrlSpaceFactoryBean"> <property name="url" value="/./space" /> <property name="spaceTypes" > <list> <ref bean="productType"/> </list> </property> </bean> <bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean"> <property name="space" ref="space"/> </bean> <bean name="productType" class="org.openspaces.core.config.GigaSpaceDocumentTypeDescriptorFactoryBean"> <property name="typeName" value="Product"/> <!-- other properties definition --> <property name="optimisticLock" value="true"/> </bean> Code // Create type descriptor: SpaceTypeDescriptor typeDescriptor = new SpaceTypeDescriptorBuilder("Product") // Other type descriptor settings. .supportsOptimisticLocking(true) .create(); // Register type: gigaspace.getTypeManager().registerTypeDescriptor(typeDescriptor);
Local Cache / Local ViewLocal View and Local Cache are supported for Documents. By default, the SpaceDocument instance is stored in the cache which speeds up query performance since the data does not need to be transformed from internal structure to SpaceDocument. If you intend to use local cache or local view in a mixed POJO-Document environment, please refer to Document-POJO Interoperability. PersistencyExternal Data Source is supported for space documents.
Configuration
<bean id="documentDataSource" class="com.test.DocumentEDS"/> <os-core:space id="space" url="/./space" schema="persistent" external-data-source="documentDataSource"> <os-core:space-type type-name="Trade" > <os-core:id property="uid" auto-generate="true"/> <os-core:routing property="symbolLabel"/> </os-core:space-type> <os-core:properties> <props> <prop key="space-config.external-data-source.data-class">com.gigaspaces.document.SpaceDocument</prop> </props> </os-core:properties> </os-core:space> The EDS Implementation
The EDS Implementation
package com.test; public class DocumentEDS implements ManagedDataSource<SpaceDocument>, BulkDataPersister { public void init(Properties prop) throws DataSourceException { // initialize persistency layer } public DataIterator<SpaceDocument> initialLoad() throws DataSourceException { // load all the data from persistency // build and return an iterator of documents } public void executeBulk(List<BulkItem> bulk) throws DataSourceException { for (BulkItem bulkItem : bulk) { SpaceDocument document = (SpaceDocument) bulkItem.getItem(); switch (bulkItem.getOperation()) { case BulkItem.WRITE: // writeDocument(document); break; case BulkItem.UPDATE: // updateDocument(document, bulkItem.getIdPropertyName()); break; case BulkItem.REMOVE: //removeDocument(document, bulkItem.getIdPropertyName()); break; default: break; } } } public void shutdown() throws DataSourceException { //cleanup resources and close the persistency } } Different document database can be used to implement the document persistency - MongoDB, CouchDB and others.
Space FiltersSpace Filter are supported for space documents. If you intend to use space filters in a mixed POJO-Document environment, please refer to Document-POJO Interoperability. Space Replication FiltersSpace Replication Filter are supported for space documents. If you intend to use space filters in a mixed POJO-Document environment, please refer to Document-POJO Interoperability. Advanced Options |
![]() |
GigaSpaces.com - Legal Notice - 3rd Party Licenses - Site Map - API Docs - Forum - Downloads - Blog - White Papers - Contact Tech Writing - Gen. by Atlassian Confluence |