Category: Farm Business

Using Graph Database to Track Plant Hybridization

Graph databases are designed to both store data and record relationships between data elements. I wondered if this would be useful in tracking cross-breeding projects – essentially building “family trees” of the entity being cross-bred. The data model would have nodes with the hybrid with notes on it. Relationships for PARENT_M and PARENT_F (male and female parent of the hybrid) would be used to associate nodes.

Graph databases have a concept of pathing – what nodes do we need to traverse to get from A to B – but to create a lineage for the plants, you need to know the starting point. Which is great if you want to play six degrees of separation and find a path between two known people, but not great if I just want to know what the lineage is of Tomato #198. To make pathing possible, I needed to add a common root node to all of heirloom seedstocks – PLANT0

This allows me to take any plant and find the paths from PLANT0 to it

MATCH
p=(Tomato0:TOMATO {name: 'PLANT0'})-[*]->(Tomato8:TOMATO{name: 'Tomato0000008'})
RETURN p

And visualize the genetic heritage of the hybrid.

 

Neo4J — Setting Up and Basic Record Management

Setting up a Neo4J Database

Ostensibly, you can create a new database using “create database somethingorother”. However, that is if you are using the enterprise edition. Running the community edition, you can only run one database. Attempting to create a new database will produce an error indicating Neo.ClientError.Statement.UnsupportedAdministrationCommand

To use a database with a custom name, I need to edit neo4j.conf and set initial.dbms.default_database

Then create a Docker container – I am mapping /data to an external directory to persist my data and /var/lib/neo4j/conf to an external directory to persist configuration

docker run -dit --name neo4j --publish=7474:7474 --publish=7687:7687 --env=NEO4J_AUTH=none --volume=/docker/neo4j/data:/data --volume=/docker/neo4j/conf:/var/lib/neo4j/conf neo4j

Listing the databases using “show databases” will show my custom database name

Switch to our database with the “:use” instruction

Create single nodes

CREATE (:PLANTS {name: ‘Black Krim’, year: 1856, color: ‘deep red’, flavor: ‘sweet’, notes: ‘Heirloom seedstock’})

Note: After I started using my data, I realized that “PLANTS” is a silly label to use since they will all be plants. I recreated all of my data with nodes labeled “TOMATO” so I can also track peppers, daffodils, and any other plants we start hybridizing.

Load of all records:

CREATE(:TOMATO {flavor: "air",notes: "hypothetical",color: "invisible",year: "1",name: "PLANT0"});
CREATE(:TOMATO {flavor: "acidic",notes: "Heirloom seedstock",color: "purple",year: 1890,name: "Cherokee Purple"});
CREATE(:TOMATO {flavor: "sweet",notes: "Heirloom seedstock",color: "bright red",name: "Whittemore"});
CREATE(:TOMATO {flavor: "sweet",notes: "Heirloom seedstock",color: "deep red",year: 1856,name: "Black Krim"});
CREATE(:TOMATO {name: 'Kellogg', color: 'bright red', flavor: 'sweet', year: '1900', notes: 'beautiful and tasty'});
CREATE(:TOMATO {name: 'Brandywine', color: 'bright red', flavor: 'sweet', year: '1900', notes: 'very tasty'});
CREATE(:TOMATO {name: 'Japanese Trifele Black', color: 'dark purple red', flavor: 'sweet', year: '1900', notes: 'nice acidic flavor'});
CREATE(:TOMATO {name: 'Sweet Apertif', color: 'bright red', flavor: 'sweet', year: '1900', notes: 'cherry'});
CREATE(:TOMATO {name: 'Eva Purple', color: 'dark purple red', flavor: 'sweet', year: '1900', notes: 'did not grow well'});
CREATE(:TOMATO {name: 'Mortgage Lifter', color: 'bright red', flavor: 'sweet', year: '1900', notes: 'huge but lacking flavor and lots of bad spots'});
CREATE(:TOMATO {flavor: "sweet",notes: "",color: "deep red",year: "2021",name: "Tomato0000001"});
CREATE(:TOMATO {flavor: "bland",notes: "small tomatoes with little flavor",color: "red",year: "2021",name: "Tomato0000002"});
CREATE(:TOMATO {flavor: "watery",notes: "not much acid",color: "pinkish",year: "2022",name: "Tomato0000003"});
CREATE(:TOMATO {flavor: "sweet",notes: "sweet, slightly acidic",color: "red",year: "2022",name: "Tomato0000004"}) ;
CREATE (:TOMATO {name: 'Tomato0000005', color: 'bright red', flavor: 'sweet', year: '2022', notes: 'amazing'});
CREATE (:TOMATO {name: 'Tomato0000006', color: 'bright red', flavor: 'sweet', year: '2023', notes: 'beautiful and tasty but no bigger than parent'});
CREATE (:TOMATO {name: 'Tomato0000007', color: 'bright red', flavor: 'sweet', year: '2023', notes: 'beautiful and tasty but no bigger than parent'});
CREATE (:TOMATO {name: 'Tomato0000008', color: 'bright red', flavor: 'sweet', year: '2023', notes: 'beautiful and tasty, slightly larger than parent'});

Show records with MATCH

The search starts with the verb “MATCH”. In parenthesis, we add the matching rule. This begins with an object name variable – you can have anonymous nodes (no variable names assigned) by omitting this string and just typing the colon. This is followed by the label that we want to match – basically the type of node we are looking for. Then, in curly braces, a filter – in this case, I am looking for nodes where the “name” field has the value “Black Krim”. Finally, there’s a return statement that indicates that we want to output the matched results.

You can include relationships in the query – parenthesis around nodes and square brackets around relationships.

(placeholdername:nodes)-[:RELATIONSHIP_CONNECTION_TYPE]->(anotherplaceholdername:otherNodes)

This is what makes graph databases interesting for tracking hybridization – we can easily produce the lineage of the plants we develop.

Deleting a record

Deleting a record is used in conjunction with match — use the DELETE verb on the collections of objects returned into your variable name. Here, the variable is ‘x’:

MATCH (x:PLANTS{name: 'Black Krim'})
DELETE x

Deleting a record and relationships by ID

When deleting a record, you can include relationship matches:

MATCH (p:PLANTS) where ID(p)=1
OPTIONAL MATCH (p)-[r]-()
DELETE r,p

Create a relationship

To create a relationship, we first need to match two objects – here I am finding a plant named PLANT0 and all of the “heirloom seedstock” plants to which I assigned year 1900 – and create parent/child relationships. Since there is both a male and female parent, that is included in the relationship name:

MATCH (a:TOMATO), (b:TOMATO)
WHERE a.name = 'PLANT0' AND b.year = '1900'
CREATE (a)-[r:PARENT_M]->(b)

MATCH (a:TOMATO), (b:TOMATO)
WHERE a.name = 'PLANT0' AND b.year = '1900'
CREATE (a)-[r:PARENT_F]->(b)

Create records with parent/child relationships

You can create records and relationships in a single command, too:

CREATE p = (:PLANTS {name: 'Black Krim', year: 1856, color: 'deep red', flavor: 'sweet', notes: 'Heirloom seedstock'})-[:PARENT_M]->(:PLANTS {name: 'Tomato0000001', color: 'deep red', flavor: 'sweet', year: '2023', notes: ''})<-[:PARENT_F]-(:PLANTS {name: 'Cherokee Purple', color: 'purple', flavor: 'acidic', year: 1890, notes: 'Heirloom seedstock'})

RETURN p

Viewing Records with Relationships

When you match records, you will also get their relationships:

Bulk importing data

LOAD CSV WITH HEADERS FROM ‘https://www.rushworth.us/lisa/ljr_plant_history.csv’

 

Greenhouse Reinforcement

The biggest problem I’ve seen with the cheap metal frame / plastic covering high tunnel greenhouses is that water will pool up on the top, and the weight of the water will collapse the whole thing.

I’ve seen a few different approaches to preventing the plastic from forming a dish and holding gallons of water. We opted for a simpler route that has, thus far, proven effective. We bought three EMT tubes — metal tubes — to create a firmer ridge-line in the greenhouse. I had 1/2″ CPVC tubing from the low tunnel greenhouse. From being held in an arch over several seasons, it was permanently bent. We cut those tubes in half, and placed them above the ridge line tubes. The ends were then pulled under the next set of horizontal bars of the greenhouse frame. This holds them in place firmly. They prevent water from pooling — the last section of the greenhouse still does pool, so I will eventually add something else in that section. It is, however, a small enough weight that the greenhouse frame can support it.

Notes for Ohio Land Leases

OSU provides a summary of different requirements based on the term of the lease:
  • Up to 1 year –  Verbal can be enforceable
  • 1-2 years – Must be in writing and signed by both parties
  • 2-3 years- Must be in writing, signed by both parties, notarized,  and recorded in the county where the land is located
  • 3 years or more- Must be in writing, signed by both parties before two witnesses, notarized, and recorded in the county where the land is located

Legal references:

O.R.C. § 5301.08 creates an exemption for land leases under 3 years from notarization and recording requirements in O.R.C. § 5301.01

Ohio CAUV Notes

Land used exclusively for commercial agriculture can be valued, for property tax purposes, based on the gross proceeds from the agricultural use. To qualify, you need to have used the land for agricultural purposes for three years producing an average gross income of at least $2,500 (or, if you have 10+ acres, there is no income requirement).

Once you qualify, file DTE Form 109 with the county auditor. You need to re-assert the commercial agricultural use each year to continue CAUV status. If the land ceases to be used for agriculture, three years of “makeup taxes” are owed — however much you would have been taxed minus the amount actually paid.

Ohio Nursery Licensing

Anyone growing plants for sale in Ohio needs to have their plants inspected for pests — the idea is similar to not moving firewood to prevent the spread of insects … if you are going to be sending plants elsewhere, it is a good idea to ensure you are not also exporting ecosystem destroying bugs!

Relevant definitions are found in ORC 927.51— including what constitutes ‘nursery stock’ — and ORC 927.55 lists exceptions where a license/inspection is not required. It appears that you do not need a license to sell plants that cannot overwinter in Ohio (I see the logic there — if a bug or disease impacts PlantX and PlantX is only going to last a few months … we probably don’t need to worry about rampant spread of that bug or disease) or plants in bloom (that’s an odd exception — but explains how the folks I see selling chrysanthemums in the Autumn do so without a license). While there is a dealer license, that is for resellers and nurseries do not appear to need a dealer license. A nursery, instead, can get a license for additional sales locations.

The nursery license is about $100 a year (plus $11 per acre of production space), and you can apply for a permit online at https://www.apps.agri.ohio.gov/NILS

There is an annual inspection of the growing facility and plants — presuming the inspection doesn’t identify any serious pest or disease infestations, a certificate is issued. The certificate must be displayed in the nursery.

If selling plants for resale (wholesale or resale), then a copy of the certificate must be included on each box/package sent out. If you plan to ship plants outside of Ohio, other states may require a phytosanitary certificate from the Ohio Department of Agriculture. If you plan to ship outside of the United States, there’s an additional federal phytosanitary certificate process through the US Department of Agriculture.