xref: /dokuwiki/inc/Search/Index/MemoryIndex.php (revision 9f63f003f1342f30f2226786546a29e25d1b79ee)
19bd7d62fSAndreas Gohr<?php
29bd7d62fSAndreas Gohr
39bd7d62fSAndreas Gohrnamespace dokuwiki\Search\Index;
49bd7d62fSAndreas Gohr
59bd7d62fSAndreas Gohruse dokuwiki\Search\Exception\IndexWriteException;
69bd7d62fSAndreas Gohr
79bd7d62fSAndreas Gohr/**
89bd7d62fSAndreas Gohr * Access to a single index file
99bd7d62fSAndreas Gohr *
109bd7d62fSAndreas Gohr * Access using this class always happens by loading the full index into memory.
119bd7d62fSAndreas Gohr * All modifications need to be explicitly made permanent using the save() method.
129bd7d62fSAndreas Gohr * Should be used for small indexes that receive many changes at once.
139bd7d62fSAndreas Gohr */
149bd7d62fSAndreas Gohrclass MemoryIndex extends AbstractIndex
159bd7d62fSAndreas Gohr{
169bd7d62fSAndreas Gohr    /** @var string the raw data lines of the index, no newlines */
179bd7d62fSAndreas Gohr    protected $data;
189bd7d62fSAndreas Gohr
19b3cb0bc3SAndreas Gohr    /** @var bool has the index been modified? */
20b3cb0bc3SAndreas Gohr    protected $dirty = false;
21b3cb0bc3SAndreas Gohr
229bd7d62fSAndreas Gohr    /**
239bd7d62fSAndreas Gohr     * Loads the full contents of the index into memory
249bd7d62fSAndreas Gohr     *
259bd7d62fSAndreas Gohr     * @inheritdoc
269bd7d62fSAndreas Gohr     */
279bd7d62fSAndreas Gohr    public function __construct($idx, $suffix = '')
289bd7d62fSAndreas Gohr    {
299bd7d62fSAndreas Gohr        parent::__construct($idx, $suffix);
309bd7d62fSAndreas Gohr
319bd7d62fSAndreas Gohr        $this->data = [];
32b3cb0bc3SAndreas Gohr        if (!file_exists($this->filename)) {
33b3cb0bc3SAndreas Gohr            return;
34b3cb0bc3SAndreas Gohr        }
359bd7d62fSAndreas Gohr        $this->data = file($this->filename, FILE_IGNORE_NEW_LINES);
369bd7d62fSAndreas Gohr
379bd7d62fSAndreas Gohr    }
389bd7d62fSAndreas Gohr
399bd7d62fSAndreas Gohr    /** @inheritdoc */
409bd7d62fSAndreas Gohr    public function changeRow($rid, $value)
419bd7d62fSAndreas Gohr    {
429bd7d62fSAndreas Gohr        if ($rid > count($this->data)) {
439bd7d62fSAndreas Gohr            $this->data = array_pad($this->data, $rid, '');
449bd7d62fSAndreas Gohr        }
459bd7d62fSAndreas Gohr        $this->data[$rid] = $value;
46b3cb0bc3SAndreas Gohr        $this->dirty = true;
479bd7d62fSAndreas Gohr    }
489bd7d62fSAndreas Gohr
499bd7d62fSAndreas Gohr    /** @inheritdoc */
509bd7d62fSAndreas Gohr    public function retrieveRow($rid)
519bd7d62fSAndreas Gohr    {
52b3cb0bc3SAndreas Gohr        if (isset($this->data[$rid])) {
53b3cb0bc3SAndreas Gohr            return $this->data[$rid];
54b3cb0bc3SAndreas Gohr        }
55dec26820SAndreas Gohr        $this->changeRow($rid, ''); // add to index
569bd7d62fSAndreas Gohr        return '';
579bd7d62fSAndreas Gohr    }
589bd7d62fSAndreas Gohr
59d6396b6dSAndreas Gohr    /** @inheritdoc */
60*9f63f003SAndreas Gohr    public function retrieveRows($rids)
61*9f63f003SAndreas Gohr    {
62*9f63f003SAndreas Gohr        $result = [];
63*9f63f003SAndreas Gohr        foreach ($rids as $rid) {
64*9f63f003SAndreas Gohr            if (isset($this->data[$rid])) $result[$rid] = $this->data[$rid];
65*9f63f003SAndreas Gohr        }
66*9f63f003SAndreas Gohr
67*9f63f003SAndreas Gohr        return $result;
68*9f63f003SAndreas Gohr    }
69*9f63f003SAndreas Gohr
70*9f63f003SAndreas Gohr    /** @inheritdoc */
718ed35011SAndreas Gohr    public function getRowIDs($values)
72d6396b6dSAndreas Gohr    {
73d6396b6dSAndreas Gohr        $values = array_map('trim', $values);
74d6396b6dSAndreas Gohr        $values = array_fill_keys($values, 1); // easier access as associative array
75d6396b6dSAndreas Gohr
76d6396b6dSAndreas Gohr        $result = [];
77d6396b6dSAndreas Gohr        $count = count($this->data);
78d6396b6dSAndreas Gohr        for ($ln = 0; $ln < $count; $ln++) {
79d6396b6dSAndreas Gohr            $line = $this->data[$ln];
80d6396b6dSAndreas Gohr            if (isset($values[$line])) {
81d6396b6dSAndreas Gohr                $result[$line] = $ln;
82d6396b6dSAndreas Gohr                unset($values[$line]);
83d6396b6dSAndreas Gohr            }
84d6396b6dSAndreas Gohr        }
85d6396b6dSAndreas Gohr
86d6396b6dSAndreas Gohr        // if there are still values, they have not been found and will be appended
87d6396b6dSAndreas Gohr        foreach (array_keys($values) as $value) {
88d6396b6dSAndreas Gohr            $this->data[] = $value;
89d6396b6dSAndreas Gohr            $result[$value] = $ln++;
90b3cb0bc3SAndreas Gohr            $this->dirty = true;
91d6396b6dSAndreas Gohr        }
92d6396b6dSAndreas Gohr
93d6396b6dSAndreas Gohr        return $result;
94d6396b6dSAndreas Gohr    }
95d6396b6dSAndreas Gohr
9603a35633SAndreas Gohr    /** @inheritdoc */
9703a35633SAndreas Gohr    public function search($re)
9803a35633SAndreas Gohr    {
9903a35633SAndreas Gohr        return preg_grep($re, $this->data);
10003a35633SAndreas Gohr    }
10103a35633SAndreas Gohr
1029bd7d62fSAndreas Gohr    /**
1039bd7d62fSAndreas Gohr     * Save the changed index back to its file
1049bd7d62fSAndreas Gohr     *
105b3cb0bc3SAndreas Gohr     * The method will check the internal dirty state and will only write when the index has actually been changed
106b3cb0bc3SAndreas Gohr     *
1079bd7d62fSAndreas Gohr     * @throws IndexWriteException
1089bd7d62fSAndreas Gohr     */
1099bd7d62fSAndreas Gohr    public function save()
1109bd7d62fSAndreas Gohr    {
1119bd7d62fSAndreas Gohr        global $conf;
1129bd7d62fSAndreas Gohr
113b3cb0bc3SAndreas Gohr        if (!$this->isDirty()) {
114b3cb0bc3SAndreas Gohr            return;
115b3cb0bc3SAndreas Gohr        }
116b3cb0bc3SAndreas Gohr
1179bd7d62fSAndreas Gohr        $tempname = $this->filename . '.tmp';
1189bd7d62fSAndreas Gohr
1199bd7d62fSAndreas Gohr        $fh = @fopen($tempname, 'w');
1209bd7d62fSAndreas Gohr        if (!$fh) {
1219bd7d62fSAndreas Gohr            throw new IndexWriteException("Failed to write $tempname");
1229bd7d62fSAndreas Gohr        }
1239bd7d62fSAndreas Gohr        fwrite($fh, implode("\n", $this->data));
124dec26820SAndreas Gohr        if (count($this->data)) {
1259bd7d62fSAndreas Gohr            fwrite($fh, "\n");
1269bd7d62fSAndreas Gohr        }
1279bd7d62fSAndreas Gohr        fclose($fh);
1289bd7d62fSAndreas Gohr
1299bd7d62fSAndreas Gohr        if ($conf['fperm']) {
1309bd7d62fSAndreas Gohr            chmod($tempname, $conf['fperm']);
1319bd7d62fSAndreas Gohr        }
1329bd7d62fSAndreas Gohr
1339bd7d62fSAndreas Gohr        if (!io_rename($tempname, $this->filename)) {
1349bd7d62fSAndreas Gohr            throw new IndexWriteException("Failed to write {$this->filename}");
1359bd7d62fSAndreas Gohr        }
136b3cb0bc3SAndreas Gohr
137b3cb0bc3SAndreas Gohr        $this->dirty = false;
1389bd7d62fSAndreas Gohr    }
1399bd7d62fSAndreas Gohr
140b3cb0bc3SAndreas Gohr    /**
141b3cb0bc3SAndreas Gohr     * Check if the index has been modified and needs to be saved
142b3cb0bc3SAndreas Gohr     * @return bool
143b3cb0bc3SAndreas Gohr     */
144b3cb0bc3SAndreas Gohr    public function isDirty()
145b3cb0bc3SAndreas Gohr    {
146b3cb0bc3SAndreas Gohr        return $this->dirty;
147b3cb0bc3SAndreas Gohr    }
1489bd7d62fSAndreas Gohr}
149