File: build_solr_schema.py

package info (click to toggle)
django-haystack 3.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,504 kB
  • sloc: python: 23,475; xml: 1,708; sh: 74; makefile: 71
file content (187 lines) | stat: -rw-r--r-- 7,236 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
import os

import requests
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.management.base import BaseCommand, CommandError
from django.template import loader

from haystack import connections, constants
from haystack.backends.solr_backend import SolrSearchBackend


class Command(BaseCommand):
    help = (  # noqa A003
        "Generates a Solr schema that reflects the indexes using templates "
        " under a django template dir 'search_configuration/*.xml'.  If none are "
        " found, then provides defaults suitable to Solr 6.4"
    )
    schema_template_loc = "search_configuration/schema.xml"
    solrcfg_template_loc = "search_configuration/solrconfig.xml"

    def add_arguments(self, parser):
        parser.add_argument(
            "-f",
            "--filename",
            help="Generate schema.xml directly into a file instead of stdout."
            " Does not render solrconfig.xml",
        )
        parser.add_argument(
            "-u",
            "--using",
            default=constants.DEFAULT_ALIAS,
            help="Select a specific Solr connection to work with.",
        )
        parser.add_argument(
            "-c",
            "--configure-directory",
            help="Attempt to configure a core located in the given directory"
            " by removing the managed-schema.xml(renaming) if it "
            " exists, configuring the core by rendering the schema.xml and "
            " solrconfig.xml templates provided in the django project's "
            " TEMPLATE_DIR/search_configuration directories",
        )
        parser.add_argument(
            "-r",
            "--reload-core",
            help="If provided, attempts to automatically reload the solr core"
            ' via the urls in the "URL" and "ADMIN_URL" settings of the SOLR'
            " HAYSTACK_CONNECTIONS entry. Both MUST be set.",
        )

    def handle(self, **options):
        """Generates a Solr schema that reflects the indexes."""
        using = options.get("using")
        if not isinstance(connections[using].get_backend(), SolrSearchBackend):
            raise ImproperlyConfigured("'%s' isn't configured as a SolrEngine" % using)

        schema_xml = self.build_template(
            using=using, template_filename=Command.schema_template_loc
        )
        solrcfg_xml = self.build_template(
            using=using, template_filename=Command.solrcfg_template_loc
        )

        filename = options.get("filename")
        configure_directory = options.get("configure_directory")
        reload_core = options.get("reload_core")

        if filename:
            self.stdout.write(
                "Trying to write schema file located at {}".format(filename)
            )
            self.write_file(filename, schema_xml)

            if reload_core:
                connections[using].get_backend().reload()

        if configure_directory:
            self.stdout.write(
                "Trying to configure core located at {}".format(configure_directory)
            )

            managed_schema_path = os.path.join(configure_directory, "managed-schema")

            if os.path.isfile(managed_schema_path):
                try:
                    os.rename(managed_schema_path, "%s.old" % managed_schema_path)
                except OSError as exc:
                    raise CommandError(
                        "Could not rename old managed schema file {}: {}".format(
                            managed_schema_path, exc
                        )
                    )

            schema_xml_path = os.path.join(configure_directory, "schema.xml")

            try:
                self.write_file(schema_xml_path, schema_xml)
            except EnvironmentError as exc:
                raise CommandError(
                    "Could not configure {}: {}".format(schema_xml_path, exc)
                )

            solrconfig_path = os.path.join(configure_directory, "solrconfig.xml")

            try:
                self.write_file(solrconfig_path, solrcfg_xml)
            except EnvironmentError as exc:
                raise CommandError(
                    "Could not write {}: {}".format(solrconfig_path, exc)
                )

        if reload_core:
            core = settings.HAYSTACK_CONNECTIONS[using]["URL"].rsplit("/", 1)[-1]

            if "ADMIN_URL" not in settings.HAYSTACK_CONNECTIONS[using]:
                raise ImproperlyConfigured(
                    "'ADMIN_URL' must be specified in the HAYSTACK_CONNECTIONS"
                    " for the %s backend" % using
                )
            if "URL" not in settings.HAYSTACK_CONNECTIONS[using]:
                raise ImproperlyConfigured(
                    "'URL' must be specified in the HAYSTACK_CONNECTIONS"
                    " for the %s backend" % using
                )

            try:
                self.stdout.write("Trying to reload core named {}".format(core))
                resp = requests.get(
                    settings.HAYSTACK_CONNECTIONS[using]["ADMIN_URL"],
                    params={"action": "RELOAD", "core": core},
                )

                if not resp.ok:
                    raise CommandError(
                        "Failed to reload core – Solr error: {}".format(resp)
                    )
            except CommandError:
                raise
            except Exception as exc:
                raise CommandError("Failed to reload core {}: {}".format(core, exc))

        if not filename and not configure_directory and not reload_core:
            self.print_stdout(schema_xml)

    def build_context(self, using):
        backend = connections[using].get_backend()

        if not isinstance(backend, SolrSearchBackend):
            raise ImproperlyConfigured(
                "'%s' isn't configured as a SolrEngine" % backend.connection_alias
            )

        content_field_name, fields = backend.build_schema(
            connections[using].get_unified_index().all_searchfields()
        )
        return {
            "content_field_name": content_field_name,
            "fields": fields,
            "default_operator": constants.DEFAULT_OPERATOR,
            "ID": constants.ID,
            "DJANGO_CT": constants.DJANGO_CT,
            "DJANGO_ID": constants.DJANGO_ID,
        }

    def build_template(self, using, template_filename=schema_template_loc):
        t = loader.get_template(template_filename)
        c = self.build_context(using=using)
        return t.render(c)

    def print_stdout(self, schema_xml):
        self.stderr.write("\n")
        self.stderr.write("\n")
        self.stderr.write("\n")
        self.stderr.write(
            "Save the following output to 'schema.xml' and place it in your Solr configuration directory.\n"
        )
        self.stderr.write(
            "--------------------------------------------------------------------------------------------\n"
        )
        self.stderr.write("\n")
        self.stdout.write(schema_xml)

    def write_file(self, filename, schema_xml):
        with open(filename, "w") as schema_file:
            schema_file.write(schema_xml)
            os.fsync(schema_file.fileno())