Skip to content

Commit 59a253a

Browse files
authored
Merge pull request #5 from linkml/stashed-stuff
implements linkml-dataops
2 parents 971ff82 + c8efdfc commit 59a253a

File tree

13 files changed

+571
-484
lines changed

13 files changed

+571
-484
lines changed

Makefile

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
RUN = pipenv run
1+
RUN = poetry run
22
LSOLR = $(RUN) lsolr
33

44
all: build test
@@ -8,8 +8,11 @@ clean:
88

99
build: linkml_solr/solrschema.py
1010

11+
# Running make test
1112
# TODO: sometimes load starts before docker environment complete
12-
test: test-docker-env
13+
test: test-docker-env test_main
14+
15+
test_main:
1316
$(RUN) python -m unittest
1417

1518
test_data: tests/test_models/kitchen_sink.context.jsonld tests/test_models/kitchen_sink.py
@@ -27,6 +30,8 @@ tests/test_models/%.context.jsonld: $(TESTMODELS_SRC)
2730
$(RUN) gen-jsonld-context $< > $@
2831
tests/test_models/%.py: $(TESTMODELS_SRC)
2932
$(RUN) gen-python $< > $@
33+
tests/test_models/%_api.py: $(TESTMODELS_SRC)
34+
$(RUN) gen-python-api $< > $@
3035
tests/test_models/%.ttl: $(TESTMODELS_SRC)
3136
$(RUN) gen-rdf $< > $@
3237
tests/test_models/amigo.yaml: linkml_solr/utils/golr_schema_utils.py
@@ -39,7 +44,7 @@ test-docker-env: test-server wait-10 test-load
3944
start-solr-server:
4045
$(LSOLR) start-server
4146
test-server:
42-
$(LSOLR) start-server -C books -s tests/test_models/books.yaml
47+
$(LSOLR) start-server -C books --sleep 10 -s tests/test_models/books.yaml
4348

4449
add-test-core: add-core-test
4550
add-core-%:

Pipfile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[[source]]
2+
url = "https://pypi.org/simple"
3+
verify_ssl = true
4+
name = "pypi"
5+
6+
[packages]
7+
linkml = "*"
8+
linkml-runtime = ">=1.1.24"
9+
linkml-dataops = "*"
10+
pyparsing = "~=2.4"
11+
sparqlwrapper = "*"
12+
pysolr = "*"
13+
requests = "*"
14+
flatten-dict = "*"
15+
linkml-solr = {editable = true, path = "."}
16+
json-flattener = ">= 0.1.7"
17+
18+
[dev-packages]
19+
tox = "*"
20+
linkml-solr = {editable = true, path = "."}
21+
22+
[pipenv]
23+
allow_prereleases = true

linkml_solr/cli.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from typing import List
22
import click
3+
import time
34
import logging
45
import subprocess
5-
from linkml.generators.yamlgen import YAMLGenerator
6-
from linkml_runtime.linkml_model.meta import SchemaDefinition, SlotDefinitionName
6+
7+
from linkml_runtime.linkml_model import SchemaDefinition
78
from linkml_runtime.loaders import yaml_loader
89
from linkml_solr import SolrQueryEngine, SolrEndpoint, DEFAULT_CORE, DEFAULT_SOLR_URL
910
from linkml_solr.utils.solr_bulkload import bulkload_file
@@ -75,6 +76,10 @@ def bulkload(files, format, schema, url, core):
7576
default='8983',
7677
show_default=True,
7778
help='http port number on which solr rules.')
79+
@click.option('--sleep',
80+
default=10,
81+
show_default=True,
82+
help='Number of seconds to sleep after initiating server start before exiting.')
7883
@click.option('--create-schema/--no-create-schema',
7984
default=True,
8085
show_default=True,
@@ -87,7 +92,7 @@ def bulkload(files, format, schema, url, core):
8792
default=DEFAULT_SOLR_URL,
8893
show_default=True,
8994
help='solr url.')
90-
def start_server(schema, kill, container, url, core, port, create_schema):
95+
def start_server(schema, kill, container, url, core, port, sleep: int, create_schema):
9196
"""
9297
Starts a solr server (via Docker)
9398
"""
@@ -105,7 +110,12 @@ def start_server(schema, kill, container, url, core, port, create_schema):
105110
'solr-precreate',
106111
core]
107112
subprocess.Popen(command)
113+
print(f'Sleeping for {sleep} seconds to allow server time to initialize...')
114+
time.sleep(sleep)
115+
print(f'Done sleeping!')
108116
if create_schema and schema:
117+
# TODO: use SchemaView
118+
from linkml.generators.yamlgen import YAMLGenerator
109119
schema_obj = YAMLGenerator(schema).schema
110120
qe = SolrQueryEngine(schema=schema_obj,
111121
endpoint=SolrEndpoint(url=f'{url}/{core}'))
@@ -157,6 +167,8 @@ def create_schema(schema, url, core):
157167
"""
158168
Creates a solr schema from a LinkML schema
159169
"""
170+
# TODO: use schemaview
171+
from linkml.generators.yamlgen import YAMLGenerator
160172
schema_obj = YAMLGenerator(schema).schema
161173
qe = SolrQueryEngine(schema=schema_obj,
162174
endpoint=SolrEndpoint(url=f'{url}/{core}'))

linkml_solr/mapper.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import logging
2+
from typing import Union
23

34
from linkml_runtime.utils.formatutils import underscore
45
from linkml_runtime.linkml_model.meta import SchemaDefinition, ClassDefinition, YAMLRoot, ElementName, SlotDefinition
5-
from rdflib import BNode, URIRef, Literal
6-
from rdflib.term import Node
76

87
from linkml_solr.solrmodel import *
98

@@ -34,8 +33,11 @@ def _get_slot(self, sn: str) -> Optional[SlotDefinition]:
3433
def _get_python_field_for_slot(self, slot: SlotDefinition) -> str:
3534
return underscore(slot.name) # TODO: map to pythongen
3635

37-
def pyval_to_solr_atom(self, v: Any, range: ElementName = None, query: SolrQuery = None) -> str:
38-
return str(v)
36+
def pyval_to_solr_atom(self, v: Any, range: ElementName = None, query: SolrQuery = None) -> Union[str, List[str]]:
37+
if isinstance(v, list):
38+
return [str(v1) for v1 in v]
39+
else:
40+
return str(v)
3941

4042
def _get_linkml_class(self, in_obj: Dict) -> str:
4143
if ASSERTED_TYPE_FIELD in in_obj:

linkml_solr/query.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
from dataclasses import dataclass
66
import json
77
import requests
8+
from linkml_dataops.query.query_model import AbstractQuery, FetchById
9+
from linkml_dataops.query.queryengine import QueryEngine
810

911
from linkml_runtime.dumpers import json_dumper
1012
from linkml_runtime.utils.formatutils import underscore
1113
from linkml_runtime.linkml_model.meta import SchemaDefinition, ClassDefinition, YAMLRoot, ElementName, SlotDefinition, SlotDefinitionName
1214

1315
from linkml_solr.solrmodel import SolrEndpoint, SolrQuery, SolrQueryResult, RawSolrResult, FIELD
14-
from linkml_solr.solrschema import Transaction
1516
from linkml_solr.solrschemagen import SolrSchemaGenerator
16-
1717
from linkml_solr.mapper import LinkMLMapper
1818

1919
# https://stackoverflow.com/questions/1176136/convert-string-to-python-class-object
@@ -25,17 +25,17 @@ def class_for_name(module_name, class_name):
2525
return c
2626

2727

28-
# TODO: inherit from linkml_runtime_api.QueryEngine
2928
@dataclass
30-
class SolrQueryEngine(object):
29+
class SolrQueryEngine(QueryEngine):
3130
"""
3231
ORM wrapper for SOLR endpoint
3332
"""
3433

35-
endpoint: SolrEndpoint
36-
schema: SchemaDefinition
34+
endpoint: SolrEndpoint = None
35+
schema: SchemaDefinition = None
3736
mapper: LinkMLMapper = None
3837
discriminator_field: SlotDefinitionName = None
38+
python_classes: List[Type[YAMLRoot]] = None
3939

4040
def __post_init__(self):
4141
# TODO: use schemaview
@@ -147,6 +147,33 @@ def fetch_object(self, row: Dict,
147147
logging.debug(new_obj)
148148
return cls(**new_obj)
149149

150+
def fetch_by_id(self, q: AbstractQuery) -> YAMLRoot:
151+
if isinstance(q, FetchById):
152+
tgt_classes = [c for c in self.python_classes if c.class_name == q.target_class]
153+
id_field = None
154+
for c in self.schema.classes.values():
155+
if c.name == q.target_class:
156+
for s in c.slots:
157+
if self.schema.slots[s].identifier:
158+
id_field = s
159+
if not id_field:
160+
raise ValueError(f'No ID found for {q.target_class}')
161+
params = {'target_class': tgt_classes[0],
162+
id_field: q.id}
163+
result = self.search(**params)
164+
return result.items
165+
166+
def simple_query(self, target_class: str, **kwargs) -> List[YAMLRoot]:
167+
tgt_classes = [c for c in self.python_classes if c.class_name == target_class]
168+
if len(tgt_classes) != 1:
169+
raise ValueError(f'Class: {target_class}')
170+
py_class = tgt_classes[0]
171+
params = {k: v for k, v in kwargs.items() if v is not None}
172+
result = self.search(target_class=py_class, **params)
173+
return result.items
174+
175+
176+
150177
def execute(self, query: SolrQuery) -> RawSolrResult:
151178
"""
152179
Execute a solr query on endpoint

linkml_solr/solrmodel.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,14 @@ def http_params(self) -> dict:
3232
params[k] = v
3333
if self.filter_query is None:
3434
self.filter_query = {}
35-
params['fq'] = [f'{k}:{_quote(v)}' for (k,v) in self.filter_query.items()]
35+
fq = []
36+
for k, v in self.filter_query.items():
37+
if isinstance(v, list):
38+
for v1 in v:
39+
fq.append(f'{k}:{_quote(v1)}')
40+
else:
41+
fq.append(f'{k}:{_quote(v)}')
42+
params['fq'] = fq
3643
if self.facet_fields is not None:
3744
params['facet'] = 'on'
3845
params['facet.field'] = self.facet_fields

linkml_solr/solrschemagen.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
from linkml_runtime.linkml_model.meta import SchemaDefinition, ClassDefinition, SlotDefinition, ClassDefinitionName
77
from linkml_runtime.dumpers import json_dumper
88
from linkml_runtime.utils.schemaview import SchemaView
9-
from linkml_runtime.utils.yamlutils import YAMLRoot, as_json_object
10-
from linkml_runtime.utils.formatutils import camelcase, be, underscore
119

1210
from linkml.utils.generator import Generator, shared_arguments
1311

linkml_solr/utils/solr_bulkload.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
from typing import List
22
import logging
33
import subprocess
4-
from linkml.generators.yamlgen import YAMLGenerator
54
from linkml_runtime.linkml_model.meta import SchemaDefinition, SlotDefinitionName
6-
from linkml_runtime.loaders import yaml_loader
7-
from linkml_solr import SolrQueryEngine, SolrEndpoint
85

96
def _get_multivalued_slots(schema: SchemaDefinition) -> List[SlotDefinitionName]:
107
return [s.name for s in schema.slots.values() if s.multivalued]

poetry.lock

Lines changed: 5 additions & 43 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ lsolr = "linkml_solr.cli:main"
1010
[tool.poetry.dependencies]
1111
python = "^3.7.4"
1212
linkml = "^1.1.15"
13-
linkml-runtime = "^1.1.12"
13+
linkml-runtime = "^1.2.7"
1414
pyparsing = "2.4"
1515
SPARQLWrapper = "^1.8.5"
1616
pysolr = "^3.9.0"
1717
flatten-dict = "^0.4.2"
1818
json-flattener = "^0.1.9"
19+
linkml-dataops = "^0.1.0"
1920

2021
[tool.poetry.dev-dependencies]
2122
tox = "^3.24.5"

0 commit comments

Comments
 (0)